import type { StandardSchemaV1 } from "@standard-schema/spec"; import { type ActionChatUIConfig } from "./action-ui.js"; import type { AgentChatAttachment, AgentChatEvent } from "./agent/types.js"; import type { ActionAuditConfig } from "./audit/types.js"; /** * How an action's `run` was invoked. Tagged at each dispatch site so the action * (and tracking) can branch on the surface that called it. * * - `"tool"` — the in-app agent loop, sub-agents/agent-teams, or A2A (which * drives the same agent loop). All agent tool calls are `"tool"`. * - `"http"` — a programmatic HTTP POST/GET to `/_agent-native/actions/` * without the frontend request marker. * - `"frontend"` — a browser call via `useActionQuery` / `useActionMutation` / * `callAction` (tagged with the `X-Request-Source` header). * - `"cli"` — `pnpm action ` (the CLI runner). * - `"mcp"` — an external agent over the MCP `tools/call` endpoint. * - `"a2a"` — a direct, explicitly exposed read-only A2A action dispatch. * Natural-language A2A delegation still runs through the agent loop and its * selected actions are attributed as `"tool"`. * - `"automation"` — an event-triggered automation dispatched from a stored * workspace trigger with trusted trigger lineage. */ export type ActionCaller = "tool" | "http" | "frontend" | "cli" | "mcp" | "a2a" | "automation"; /** * Trusted automation lineage added by the trigger dispatcher. Action inputs * must never be used to create or override this context. */ export interface ActionAutomationContext { triggerId: string; triggerName: string; policyId?: string; } /** * Context passed as the optional second argument to an action's `run`. * Carries the resolved request identity and the invocation source so actions * can read `ctx.userEmail` / `ctx.orgId` / `ctx.caller` directly instead of * calling `getRequestUserEmail()` / `getRequestOrgId()` by hand. * * Backward compatible: existing 1-arg `run(args)` functions keep working, and * callers that only need `send` (the agent loop) can still destructure it. */ export interface ActionRunContext { /** * Emit an SSE event to the client. Only meaningful inside the agent tool * loop (e.g. `agent_call_text` streaming); `undefined` on every other * surface. */ send?: (event: AgentChatEvent) => void; /** * Resolved request user email, or `undefined` when there is no authenticated * identity. NEVER defaulted to a dev identity — actions that need a fallback * must apply their own. */ userEmail?: string; /** Resolved org id, or `null` when the request has no org. */ orgId?: string | null; /** How this action was invoked. */ caller: ActionCaller; /** Present only for trigger-dispatched automation calls. */ automation?: ActionAutomationContext; /** Verified network lineage for direct delegated action calls. */ networkProtocol?: "a2a" | "mcp" | "provider-api"; networkId?: string; networkPeer?: string; /** * Attachments submitted with the current agent turn (pasted text blocks, * uploaded files, images), exactly as the server received them — with full, * untruncated `text` for text attachments. Populated only inside the agent * tool loop (`caller: "tool"`); `undefined` on every other surface. * * Lets an action consume a large pasted artifact BY REFERENCE (e.g. * `create-extension`'s `contentFromAttachment`) instead of forcing the model * to re-emit the whole file as a tool argument — which frequently gets cut * off mid-stream and triggers a continuation loop. */ attachments?: AgentChatAttachment[]; /** * Abort signal for the current agent run. Fires when the run is soft-timed * out, user-cancelled, or the server is shutting down. Well-behaved actions * can observe this signal to cancel in-flight work early instead of waiting * for the per-tool 60-second hard timeout. * * Populated only inside the agent tool loop (`caller: "tool"`); `undefined` * on every other surface. Never throws — checking `signal.aborted` or * attaching an `"abort"` listener is always safe. */ signal?: AbortSignal; /** * Name of the action being invoked (the registry key, e.g. * `delete-recording`). Set at each dispatch site so cross-cutting concerns — * notably the audit log — can attribute the call. `undefined` for direct * programmatic `run()` calls that bypass the dispatcher. */ actionName?: string; /** * Agent conversation thread + turn that triggered this call, populated only * inside the agent tool loop (`caller: "tool"`). Lets the audit log link a * mutation to the specific agent run/turn that caused it. `undefined` on * every human/programmatic surface. */ threadId?: string; /** Concrete execution id for this agent-loop attempt. */ runId?: string; turnId?: string; } export interface AgentActionStopOptions { /** Optional stable code surfaced in run metadata and tests. */ errorCode?: string; /** Optional short tool-result text. Defaults to the user-facing message. */ toolResult?: string; } /** * Throw from an action when the agent should stop the current turn instead of * feeding the failure back to the model for another retry. */ export declare class AgentActionStopError extends Error { readonly agentNativeStop = true; readonly errorCode?: string; readonly toolResult?: string; constructor(message: string, options?: AgentActionStopOptions); } export declare function isAgentActionStopError(err: unknown): err is AgentActionStopError; /** HTTP exposure config for an action. */ export interface ActionHttpConfig { /** HTTP method. Default: "POST". Use "GET" for read-only actions. */ method?: "GET" | "POST" | "PUT" | "DELETE"; /** Override route path under /_agent-native/actions/. Default: action filename. */ path?: string; } /** Explicit opt-in metadata for public agent protocols such as MCP or A2A. */ export interface PublicAgentActionConfig { expose: boolean; readOnly: boolean; requiresAuth?: boolean; isConsequential?: boolean; title?: string; description?: string; } /** A deep link an external agent (MCP / A2A) can surface to the user so they * can open the produced/listed resource in the running app UI. */ export interface ActionDeepLink { /** App-relative path (e.g. `/_agent-native/open?app=mail&view=inbox&...`) * or an absolute URL. The MCP layer prefixes the request origin when this * is relative, and may rewrite it to the `agentnative://` desktop scheme. */ url: string; /** Human-readable label, e.g. "Open draft in Mail". */ label: string; /** Optional view hint (matches the `navigate` command `view`). */ view?: string; } /** Builds a deep link from an action's args + result so external agents can * surface an "Open in →" link. MUST be pure and synchronous — no I/O, * no awaits. Best-effort: a throw or null is swallowed and never fails the * tool call. See the `external-agents` skill. */ export type ActionLinkBuilder = (ctx: { args: Record; result: any; }) => ActionDeepLink | null | undefined; export declare const MCP_APP_EXTENSION_ID: "io.modelcontextprotocol/ui"; export declare const MCP_APP_MIME_TYPE: "text/html;profile=mcp-app"; export declare const MCP_APP_RESOURCE_URI_META_KEY: "ui/resourceUri"; export interface ActionMcpAppCsp { connectDomains?: string[]; resourceDomains?: string[]; frameDomains?: string[]; baseUriDomains?: string[]; } export type ActionMcpAppCspBuilder = (ctx: { actionName: string; appId?: string; requestOrigin?: string; }) => ActionMcpAppCsp | Promise; export interface ActionMcpAppPermissions { camera?: Record; microphone?: Record; geolocation?: Record; clipboardWrite?: Record; } export interface ActionMcpAppResourceMeta { csp?: ActionMcpAppCsp | ActionMcpAppCspBuilder; permissions?: ActionMcpAppPermissions; /** * Host-specific sandbox domain hint. Do not set this to the app's normal * production URL; app origins belong in CSP/open-link metadata. ChatGPT uses * the separate `openai/widgetDomain` compatibility field. */ domain?: string; prefersBorder?: boolean; } export type ActionMcpAppHtmlBuilder = (ctx: { actionName: string; appId?: string; requestOrigin?: string; }) => string; export interface ActionMcpAppResourceConfig { /** `ui://` URI. Defaults to `ui:///`. */ uri?: string; /** MCP resource name. Defaults to the action name. */ name?: string; title?: string; description?: string; /** * HTML5 document content for the MCP App resource. Keep this self-contained * or declare any external origins in `csp`. */ html: string | ActionMcpAppHtmlBuilder; /** Defaults to the MCP Apps HTML MIME type. */ mimeType?: typeof MCP_APP_MIME_TYPE; /** Extra resource/content metadata. `ui` is merged with the fields below. */ _meta?: Record; csp?: ActionMcpAppCsp | ActionMcpAppCspBuilder; permissions?: ActionMcpAppPermissions; /** * Host-specific sandbox domain hint. Do not set this to the app's normal * production URL; app origins belong in CSP/open-link metadata. ChatGPT uses * the separate `openai/widgetDomain` compatibility field. */ domain?: string; prefersBorder?: boolean; } export interface ActionMcpAppConfig { /** * Optional MCP Apps UI resource for hosts that render inline app iframes. * Required when the action should open an interactive app view. Omit when * you only need `compactCatalog: true` to keep a non-UI action visible in * the compact catalog (e.g. read/update actions that should be callable from * Claude.ai / ChatGPT without a dedicated iframe resource). */ resource?: ActionMcpAppResourceConfig; /** * MCP Apps tool visibility. Defaults to model + app so the LLM can call the * action and the app iframe can call it back through the host bridge. */ visibility?: Array<"model" | "app">; /** * Rare escape hatch for MCP Apps chat hosts. By default OAuth callers with * `mcp:apps` see the generic app tools (`open_app`, `list_apps`, etc.) so * hosts do not ingest every action-specific UI resource. Set this only when * this specific action must stay visible in that compact catalog. */ compactCatalog?: boolean; } /** Schema definition for a single action parameter (legacy JSON schema style). */ export interface ParameterSchema { type: string; description?: string; enum?: string[]; } /** Infer runtime parameter types from a legacy parameter schema map. */ type InferParams | undefined> = T extends Record ? { [K in keyof T]?: string; } : Record; /** * What to do when an action's RETURN value fails `outputSchema` validation. * * - `"strict"` — throw a clear error so a buggy action surfaces loudly. * - `"warn"` (default) — `console.warn` the issues and return the ORIGINAL * result unchanged. Non-breaking: behavior never changes unless the dev * opts into `"strict"` or `"fallback"`. * - `"fallback"` — return `outputFallback` in place of the invalid result. * * Mirrors Mastra/Flue structured-output handling, kept on the action layer. */ export type ActionOutputErrorStrategy = "strict" | "warn" | "fallback"; interface DefineActionWithSchema { description: string; /** Standard Schema-compatible schema (Zod, Valibot, ArkType). Provides runtime * validation and full TypeScript type inference for `run()` args. The schema is * also converted to JSON Schema for the Claude API tool definition. */ schema: TSchema; /** Legacy parameters — ignored when `schema` is provided. */ parameters?: never; /** * Optional alternate Standard Schema (Zod, Valibot, ArkType) used ONLY to * build the tool definition advertised to the model — the JSON Schema that * lands in the Claude `tools` array (and MCP/A2A tool listings, which read * the same `tool.parameters`). Runtime validation always runs against * `schema` above via the normal `wrapWithValidation` path; setting this * never weakens validation and never changes `run()`'s argument type. * * Use this when the full input schema is much richer than what the model * needs to see up front — the canonical example is a deep discriminated * union of block/shape types where a per-call catalog lookup tool (e.g. * `get-plan-blocks`) already teaches the full field shapes. Advertise a * compact version (e.g. an enum of valid `type` values plus a note to call * the lookup tool) instead of embedding every variant's fields in every * request. Invalid calls still get the full, actionable validation error * (missing/invalid fields, received args) from `schema` — this only trims * what is proactively shown, not what is accepted or checked. */ agentInputSchema?: StandardSchemaV1; /** Optional Standard Schema-compatible schema (Zod, Valibot, ArkType) the * action's RETURN value is validated against AFTER `run()` resolves. Borrowed * from Mastra/Flue structured-output. When omitted, behavior is byte-for-byte * unchanged. The mismatch handling is governed by `outputErrorStrategy`. */ outputSchema?: TOutputSchema; /** What to do when the result fails `outputSchema`. Default: `"warn"`. */ outputErrorStrategy?: ActionOutputErrorStrategy; /** Value returned in place of an invalid result when `outputErrorStrategy` is * `"fallback"`. Ignored for the other strategies. */ outputFallback?: TReturn; run: (args: StandardSchemaV1.InferOutput, ctx?: ActionRunContext) => Promise | TReturn; http?: ActionHttpConfig | false; /** Whether the HTTP/frontend action route must have an authenticated owner. * Defaults to true. Set to false only for metadata/read actions that safely * handle `ctx.userEmail` / `getRequestUserEmail()` being undefined. */ requiresAuth?: boolean; /** Max HTTP request body in bytes. When set, the route 413s on the declared * `Content-Length` before parsing. Use for public, no-auth POST actions; * unset = no route-level cap. */ maxBodyBytes?: number; /** Whether this action is exposed to the agent — the in-app assistant and the * app's MCP/A2A tool surfaces — as a callable tool. **Default-allow opt-out**: * `undefined` / `true` expose it; only an explicit `false` hides it from every * agent tool list while keeping it callable from the frontend / HTTP * (`useActionMutation`, `callAction`, `/_agent-native/actions/`). Use * this for UI-only or purely programmatic actions you want behind the * framework's auth + action surface WITHOUT spending a slot in the model's * tool list. Distinct from `toolCallable`, which only governs the sandboxed * extension ("tools") iframe bridge. See `packages/core/docs/content/actions.mdx`. */ agentTool?: boolean; /** If true, the framework will NOT emit a screen-refresh change event after a * successful call. Auto-inferred as `true` when `http.method === "GET"`. * Only set this manually when you need to override the inference — e.g. a * POST action that only reads data but can't use GET for a protocol reason. */ readOnly?: boolean; /** Set false for read-only tools that should stay available in Act mode but * must not run during Plan mode because they perform substantive work * rather than lightweight inspection. Defaults to allowed when read-only. */ allowInPlanMode?: boolean; /** If true, the agent may execute this action concurrently with other * read-only or parallel-safe tool calls emitted in the same model turn. * Only set this for mutating actions that are internally concurrency-safe * and order-independent for same-turn execution. */ parallelSafe?: boolean; /** Set false to exempt a read-only tool from the agent loop's duplicate * read-only call guard (per-turn result cache + "Skipped duplicate..." * repeat detection). Default true (deduped). Use this for volatile/polling * reads where an identical call is expected to return a different result * each time — e.g. polling a code-execution status by id, or re-fetching * current on-screen state. Has no effect on non-read-only actions, which * are never deduped in the first place. */ dedupe?: boolean; /** Whether this action may be invoked from the tools (Alpine iframe) bridge * via `appAction(name, params)` — see `packages/core/docs/content/actions.mdx` * ("Tools Callability"). **Default-allow opt-out**: undefined / `true` both * allow tool-iframe calls; only an explicit `false` returns 403. Set to * `false` for high-blast-radius admin operations (account deletion, org * membership changes, anything that modifies auth state) — used by the * framework's `share-resource`, `unshare-resource`, and * `set-resource-visibility` for defense-in-depth. Regular UI/agent/CLI/MCP/A2A * calls are unaffected. Enforced by the action HTTP route layer — see * `packages/core/src/server/action-routes.ts`. Audit reference: H5 in * `security-audit/05-tools-sandbox.md`. */ toolCallable?: boolean; /** Explicit public-agent exposure metadata. Public web routes never imply * public MCP/A2A/OpenAPI tool exposure. Actions must opt in here and public * protocol mounts must still filter for safe, route-appropriate tools. */ publicAgent?: PublicAgentActionConfig; /** Optional deep-link builder. When set, MCP/A2A surfaces append an * "Open in →" link built from the call's args + result so the * external agent can drop the user into the running app at the right * view/record. Pure + sync + best-effort. See the `external-agents` skill. */ link?: ActionLinkBuilder; /** Optional MCP Apps UI resource for hosts that can render inline * interactive app iframes. Text/deep-link tool results remain the fallback * for CLI and non-UI hosts. */ mcpApp?: ActionMcpAppConfig; /** Optional native Agent-Native chat renderer for this action's structured * result. This is first-party React UI, not arbitrary HTML/JS. */ chatUI?: ActionChatUIConfig; /** * Per-tool timeout override in milliseconds for agent-loop tool calls. Use * sparingly for actions that legitimately wait on slow provider work. */ timeoutMs?: number; /** Per-tool result truncation override for agent-loop tool calls. */ maxResultChars?: number; /** * Opt-in human-in-the-loop approval gate. **Default off** — the framework * intentionally keeps HITL approvals rare; almost every action should run * without one. Set this only for high-consequence, outward-facing, * hard-to-undo operations (the canonical example is actually sending an * email). When `needsApproval` resolves truthy and the agent calls this * action, the loop does NOT execute `run()`: it emits an `approval_required` * event and stops the turn, waiting for a human to approve. The action runs * only once the human re-issues the turn approving this specific call. * * - `true` — always require approval. * - `(args, ctx) => boolean | Promise` — require approval only when * the predicate returns true (e.g. only for external recipients, only * above a dollar threshold). Keep it pure + fast; thrown errors are treated * as "approval required" (fail closed). */ needsApproval?: boolean | ((args: StandardSchemaV1.InferOutput, ctx?: ActionRunContext) => boolean | Promise); /** * Audit-log configuration. **Default-on for mutating actions** — you only * need this to tune capture: declare the mutated `target` (so the change * shows up in the owner's audit trail) and/or a `summary`, opt a read-only * action in via `onRead`, or opt a noisy action out via `enabled: false`. * See the `audit-log` skill. */ audit?: ActionAuditConfig; } interface DefineActionWithParams | undefined = Record | undefined, TReturn = any> { description: string; /** Flat map of parameter names to their schema. Automatically wrapped in * `{ type: "object", properties: ... }` for the Claude API. */ parameters?: TParams; /** Standard Schema — not used in this overload. */ schema?: never; /** Advertised-only schema override — not used in this overload (no runtime * schema to advertise a compact alternative for). See the schema overload * above. */ agentInputSchema?: never; /** Optional Standard Schema-compatible schema the action's RETURN value is * validated against AFTER `run()` resolves. See the schema overload above. * When omitted, behavior is byte-for-byte unchanged. */ outputSchema?: StandardSchemaV1; /** What to do when the result fails `outputSchema`. Default: `"warn"`. */ outputErrorStrategy?: ActionOutputErrorStrategy; /** Value returned in place of an invalid result when `outputErrorStrategy` is * `"fallback"`. Ignored for the other strategies. */ outputFallback?: TReturn; run: (args: InferParams, ctx?: ActionRunContext) => Promise | TReturn; http?: ActionHttpConfig | false; /** Whether the HTTP/frontend action route must have an authenticated owner. * Defaults to true. See the schema overload above. */ requiresAuth?: boolean; /** Max HTTP request body in bytes; 413s on `Content-Length` before parsing. * See the schema overload above. */ maxBodyBytes?: number; /** Whether this action is exposed to the agent as a callable tool. Only an * explicit `false` hides it from every agent tool list while keeping it * frontend/HTTP-callable. See the schema overload above and actions.md. */ agentTool?: boolean; /** If true, the framework will NOT emit a screen-refresh change event after a * successful call. Auto-inferred as `true` when `http.method === "GET"`. */ readOnly?: boolean; /** Set false for read-only tools that should stay available in Act mode but * must not run during Plan mode. See the schema overload above. */ allowInPlanMode?: boolean; /** If true, the agent may execute this action concurrently with other * read-only or parallel-safe tool calls emitted in the same model turn. */ parallelSafe?: boolean; /** Set false to exempt a read-only tool from the duplicate read-only call * guard. Default true. See the schema overload above. */ dedupe?: boolean; /** Whether this action may be invoked from the tools (Alpine iframe) bridge * via `appAction(name, params)`. See the schema overload above for details * and the `toolCallable` section in actions.md. */ toolCallable?: boolean; /** Explicit public-agent exposure metadata. See schema overload above. */ publicAgent?: PublicAgentActionConfig; /** Optional deep-link builder. See schema overload above. */ link?: ActionLinkBuilder; /** Optional MCP Apps UI resource. See schema overload above. */ mcpApp?: ActionMcpAppConfig; /** Optional native Agent-Native chat renderer. See schema overload above. */ chatUI?: ActionChatUIConfig; /** Per-tool timeout override in milliseconds. See schema overload above. */ timeoutMs?: number; /** Per-tool result truncation override. See schema overload above. */ maxResultChars?: number; /** Opt-in human-in-the-loop approval gate (default off). See the schema * overload above for full semantics. */ needsApproval?: boolean | ((args: InferParams, ctx?: ActionRunContext) => boolean | Promise); /** Audit-log configuration (default-on for mutations). See the schema * overload above and the `audit-log` skill. */ audit?: ActionAuditConfig; } /** * Opaque typed wrapper returned by `defineAction`. The type parameters carry * the schema-inferred input and the `run` return type so that: * * - The generated `.generated/action-types.d.ts` can extract them via * `typeof import("../actions/my-action").default.run` and augment * `ActionRegistry` with concrete param/result types. * - `useActionQuery` / `useActionMutation` / `callAction` in the client hooks * flow the correct types end-to-end without manual generic annotations. * * Runtime shape is unchanged — this is a declaration-only wrapper. */ export interface ActionDefinition { /** * Typed run function — declaration only; infer input/return from this. * `TInput` is the schema's input type (optional defaults allowed at call * sites); `TReturn` is the awaited result type of the run callback. */ readonly run: (args: TInput, ctx?: ActionRunContext) => Promise | TReturn; /** @internal Framework use only — do not call directly. */ readonly tool: import("./agent/types.js").ActionTool; readonly http?: ActionHttpConfig | false; readonly requiresAuth?: boolean; readonly maxBodyBytes?: number; readonly agentTool?: boolean; readonly readOnly?: boolean; readonly allowInPlanMode?: boolean; readonly parallelSafe?: boolean; readonly dedupe?: boolean; readonly toolCallable?: boolean; readonly publicAgent?: PublicAgentActionConfig; readonly link?: ActionLinkBuilder; readonly mcpApp?: ActionMcpAppConfig; readonly chatUI?: ActionChatUIConfig; /** Per-tool timeout override in milliseconds for agent-loop tool calls. */ readonly timeoutMs?: number; /** Per-tool result truncation override for agent-loop tool calls. */ readonly maxResultChars?: number; /** Standard Schema the action's RETURN value is validated against after * `run()` resolves. Present only when the caller passed `outputSchema`. */ readonly outputSchema?: StandardSchemaV1; /** Resolved output-mismatch strategy. Present only when `outputSchema` is * set; defaults to `"warn"`. */ readonly outputErrorStrategy?: ActionOutputErrorStrategy; /** Value substituted for an invalid result under the `"fallback"` strategy. */ readonly outputFallback?: TReturn; /** Opt-in human-in-the-loop approval gate (default off). When truthy, the * agent loop emits `approval_required` and pauses instead of executing this * action until a human approves the specific call. */ readonly needsApproval?: boolean | ((args: TInput, ctx?: ActionRunContext) => boolean | Promise); /** Resolved audit-log configuration. Present only when the caller passed * `audit`. The audit capture wrapper is baked into `run`; this field is for * introspection. */ readonly audit?: ActionAuditConfig; } /** * Define an agent action. Place in `actions/` directory — auto-discovered by the framework. * * Supports two modes: * * **Schema mode (recommended)** — pass a Standard Schema-compatible schema (Zod, Valibot, * ArkType) for runtime validation and full type inference: * * ```ts * import { defineAction } from "@agent-native/core"; * import { z } from "zod"; * * export default defineAction({ * description: "Create a form", * schema: z.object({ * title: z.string().describe("Form title"), * status: z.enum(["draft", "published", "closed"]).default("draft"), * }), * run: async (args) => { * // args is { title: string; status: "draft" | "published" | "closed" } * // Already validated — invalid inputs never reach here * }, * }); * ``` * * **Parameters mode (legacy)** — pass raw JSON schema-like parameter definitions: * * ```ts * export default defineAction({ * description: "List events", * parameters: { * from: { type: "string", description: "Start date" }, * }, * run: async (args) => { ... }, * }); * ``` */ export declare function defineAction(options: DefineActionWithSchema): ActionDefinition, TReturn>; export declare function defineAction | undefined, TReturn>(options: DefineActionWithParams): ActionDefinition, TReturn>; export {}; //# sourceMappingURL=action.d.ts.map