# Ageniti Official documentation snapshot for LLM consumption. Primary docs site: /en/docs Templates gallery: /en/templates ## Product Summary Title: Build Apps for Agents Subtitle: Turn app capabilities into structured actions for CLI, HTTP, MCP, OpenAI / AI SDK tools, React hooks, and typed clients. ## Choose Your Path - Start with the core flow: Learn the baseline setup, package layout, and the first action-to-surface workflow. - Integrate into an existing app: Follow practical recipes for MCP servers, React apps, route handlers, and automation scripts. - Start from a pattern: Use documented starter patterns when you want a proven integration path instead of guessing the surface wiring. ## Templates - CLI App Starter (CLI, Runtime): Start with one action and expose it through generated CLI commands with flags, JSON input, schema output, and stable envelopes. Link: /en/docs/recipes#cli-first-internal-tool - MCP Server Bundle (MCP, Tools): Expose selected app capabilities through MCP tools and generate the stdio launcher plus descriptor used by MCP hosts. Link: /en/docs/recipes#mcp-tools-for-agent-platforms - Next.js AI SDK Bridge (Next.js, AI SDK): Map selected actions to Vercel AI SDK-style tools from a Node-safe app entry while keeping app logic in the shared runtime. Link: /en/docs/recipes#openai-and-ai-sdk-tool-adapters - React Invocation (React, UI): Call the same Ageniti action from existing React UI handlers without replacing your router, components, or state management. Link: /en/docs/recipes#existing-react-app-button - HTTP API Surface (HTTP, Node.js): Put the lightweight HTTP handler behind your own server, route handler, or API gateway while returning the same runtime envelope. Link: /en/docs/surfaces#http - Tool Adapter Pattern (Adapters, Tools): Use the shared runtime as the stable boundary when adapting actions into another tool ecosystem or internal platform. Link: /en/docs/surfaces#choosing-a-surface - OpenAI Function Tools (OpenAI, Tools): Generate OpenAI Chat Completions and Responses function tool schemas from the same explicit action contract. Link: /en/docs/surfaces#openai-and-ai-sdk-tools - JSON Automation Runner (JSON, Automation): Invoke actions from scripts, tests, and CI using JSON requests without standing up HTTP or MCP infrastructure. Link: /en/docs/recipes#json-runner-for-automation ## Documentation ### Ageniti Path: /en/docs # Ageniti Ageniti is the action primitive layer for apps that need to expose capabilities to agents, automation systems, and external tools. Define an action once and the runtime makes it callable from CLI, HTTP, MCP, OpenAI-compatible tool calling, Vercel AI SDK-style tools, a JSON runner, a typed client, the React `useAction` hook, and a local dev console — all from the same contract. ## What You Get - A typed action contract built with `defineAction()` (or `defineActions` / `actionsFromHandlers` for batch wiring). - A shared runtime with validation, permissions, middleware, timeout, retry, idempotency keys, concurrency limits, log redaction, and live streaming events. - Thin surface adapters for CLI, HTTP, MCP, React, JSON automation, local development, and LLM tool calling. - A typed client (`createClient`) and codegen (`generateClientTypes`) for IDE autocomplete across processes. - Direct interop with Zod and Standard Schema v1 — no schema rewrite required. - Official packaging helpers for CLI launchers, MCP stdio launchers (Content-Length and newline framing), manifests, and bundle metadata. - Project tools for `init`, `doctor`, config-driven builds, and zero-config app entry discovery. - A consistent success and failure envelope across surfaces. ## Core Model ```text Existing app capability | v Typed action contract | v Shared runtime | +--> CLI +--> JSON runner +--> MCP +--> OpenAI tools +--> AI SDK tools +--> Dev console +--> React invocation ``` Ageniti keeps most decisions in the runtime and keeps each external surface thin. That means one action definition can be reused across tooling without rewriting the business logic for every integration. ## Install ```text npm install @ageniti/core ``` Ageniti is ESM-only and requires Node.js 20 or newer. ## Minimal Example ```js import { createAgenitiApp, defineAction, s } from "@ageniti/core"; const searchTasks = defineAction({ name: "search_tasks", description: "Search workspace tasks by status.", input: s.object({ status: s.enum(["open", "blocked", "done"]).describe("Task status"), }), output: s.object({ items: s.array( s.object({ id: s.string(), status: s.string(), }) ), }), run(input, ctx) { ctx.logger.info("Searching tasks.", { status: input.status }); return ctx.services.tasks.search(input); }, }); export const app = createAgenitiApp({ name: "workspace-tools", actions: [searchTasks], services: { tasks }, }); ``` From there you can expose the same action through `app.createCli()`, `app.createMcpHandler()`, `app.createAISDKTools()`, `app.createReactAdapter()`, or `app.createDevServer()`. ## React And Expo Workflow For React, Next.js, and Expo projects, the intended model is: - keep business logic in shared services - wrap selected capabilities as Ageniti actions - add one headless Node-safe app entry such as `src/ageniti/app.ts` - use `npx @ageniti/core init react`, `npx @ageniti/core init expo`, `npx @ageniti/core init next`, and `npx @ageniti/core doctor` for project setup - use your generated app CLI, such as `task-app build` and `task-app publish`, to generate and ship official artifacts That keeps your UI unchanged while giving you a stable packaging path for the surfaces agents and automation systems can use. ## Documentation Map - [Getting Started](/en/docs/getting-started): build the first action and expose it through the main surfaces. - [Agentic Primitives](/en/docs/agentic-primitives): the five core primitives — action contract, schema interop, bulk handlers, streaming events, typed client. - [Core Concepts](/en/docs/concepts): app, action, contract, runtime, surface, and headless entry. - [Surfaces](/en/docs/surfaces): when to use CLI, MCP, HTTP, JSON, React, tools, and the dev console. - [Safety Model](/en/docs/safety): visibility, permissions, confirmation, side effects, and metadata. - [Packaging](/en/docs/packaging): generated bundles, MCP descriptors, package, publish, and release review. - [API Reference](/en/docs/api): detailed reference for the public SDK API. - [Entry Points](/en/docs/entry-points): package exports and when to import each subpath. - [Runtime Semantics](/en/docs/runtime): envelopes, errors, retry, timeout, confirmation, logs, and artifacts. - [Recipes](/en/docs/recipes): copyable integration patterns for common setups. - [SDK Readiness](/en/docs/sdk-readiness): product bar for shipping an SDK that exposes app capabilities to agents. - [Roadmap](/en/docs/roadmap): prioritized next steps for turning the SDK into a more trusted product. - [Skill Guide](/en/docs/skill): compact reference for the skills an Ageniti app exposes. - [Scope](/en/docs/scope): what Ageniti deliberately does and does not do. - [FAQ](/en/docs/faq): direct answers about positioning and usage. - [Release Checklist](/en/docs/release-checklist): packaging and publish checks. ## Design Boundaries You explicitly declare the capabilities you want to expose, then choose which surfaces should be allowed to invoke them. The runtime stays small — it does not inspect your React tree, replace routing, own state management, or orchestrate planning loops. ### Getting Started Path: /en/docs/getting-started # Getting Started Ageniti is the action primitive layer for agentic apps. You define an action once and the runtime makes it callable from CLI, HTTP, MCP, OpenAI / AI SDK tools, the typed client, and React — all from the same contract. If you already have a React or TypeScript app, you don't need to restructure it — just declare which capabilities should be addressable as actions and mount the surfaces you want. For the conceptual tour of the five core primitives (action contract, schema interop, bulk handlers, streaming events, typed client), see [**Primitives**](/en/docs/agentic-primitives). ## Install ```text npm install @ageniti/core ``` Requirements: - ESM-only package - Node.js 20 or newer ## 1. Pick One Existing Capability Start with one application capability you already trust: - create a task - search workspace records - export a report - send a message - summarise a document Do not start by rewriting the app. Start by wrapping one stable capability. ## 2. Define An Action ```js import { defineAction, s } from "@ageniti/core"; export const createTask = defineAction({ name: "create_task", description: "Create a workspace task.", visibility: "public", sideEffects: "write", idempotency: "conditional", permissions: ["task:create"], input: s.object({ title: s.string().min(1).describe("Task title"), assignee: s.string().optional().describe("Optional assignee id"), priority: s.enum(["low", "normal", "high"]).default("normal"), }), output: s.object({ taskId: s.string(), status: s.string(), priority: s.string(), }), async run(input, ctx) { ctx.logger.info("Creating task.", { title: input.title }); return ctx.services.tasks.create(input); }, }); ``` Useful defaults from `defineAction()`: - `title` defaults to a humanised version of `name`. - `input` defaults to `s.object({})`. - `visibility` defaults to `"public"`. - `sideEffects` defaults to `"read"`. - `idempotency` defaults to `"unspecified"`. - `supportedSurfaces` defaults to `cli`, `json`, `http`, `mcp`, `react`, `dev`, and `ai-sdk`. - Destructive actions require confirmation unless you override `requiresConfirmation`. ## 3. Create An App ```js import { createAgenitiApp } from "@ageniti/core"; import { createTask } from "./actions/create-task.js"; import { tasks } from "./services/tasks.js"; export const app = createAgenitiApp({ name: "task-app", description: "Workspace task operations exposed to external tools, automation, and agent callers.", attribution: { text: "Powered by Ageniti", vendor: "Ageniti", product: "Ageniti Core", url: "https://ageniti.dev", docsUrl: "https://ageniti.dev/docs", }, docs: { summary: "Use this app to create tasks and inspect task state.", }, actions: [createTask], services: { tasks, }, permissionChecker({ action, context }) { return action.permissions.every((permission) => context.auth?.permissions?.includes(permission) ) || "Missing required permission."; }, }); ``` `createAgenitiApp()` gives you a shared runtime plus convenience methods for every supported surface. If you want generated CLI help, MCP manifests, tool metadata, and bundle artifacts to include visible origin information, set the optional `attribution` field on the app. ## 4. Write Natural-Language Guidance Ageniti can export one unified guide from natural-language fields on the app and actions. It does not call a model or infer hidden behaviour from your UI. It reads the contract you explicitly wrote. ```js export const createTask = defineAction({ name: "create_task", description: "Create a workspace task.", docs: { whenToUse: "Use this when an external caller needs to create a trackable task.", whenNotToUse: "Do not use this for multi-step approvals.", usageNotes: [ "The caller should provide a short, human-readable title.", "High priority tasks may trigger downstream notifications.", ], inputExample: { title: "Follow up with design review", priority: "high", }, outputExample: { taskId: "task_123", status: "open", priority: "high", }, }, run(input, ctx) { return ctx.services.tasks.create(input); }, }); export const app = createAgenitiApp({ name: "task-app", description: "Workspace task operations exposed to external tools, automation, and agent callers.", docs: { summary: "Use this app to create tasks and inspect task state.", audience: "Internal tools, automation scripts, agent hosts, and operators.", whenToUse: [ "Use it when an external caller needs structured task creation or lookup.", "Use it when the caller needs CLI, MCP, HTTP, or LLM tool surfaces.", ], setup: [ "Make sure the caller has the required permissions.", "Test action input through the dev console or CLI first.", ], operationalNotes: [ "Destructive actions are not exposed to MCP and LLM tools by default.", "Put internal-only details in metadata, and exposed guidance in publicMetadata or docs.", ], }, actions: [createTask], }); ``` Those fields are merged into one `GUIDE.md`, which can include: - app overview - intended audience - when to use it - setup and usage steps - operational notes - available actions - each action's description, permissions, side effects, surfaces, example input, and example output Export it with: ```text task-app docs task-app docs --out-dir ./dist/ageniti task-app build docs --out-dir ./dist/ageniti task-app build bundle --out-dir ./dist/ageniti ``` `task-app docs` prints the guide to stdout. With `--out-dir`, it writes `GUIDE.md`. `build bundle` automatically includes `GUIDE.md` in the final bundle. The guide is useful for: - agent host documentation - MCP server installation notes - internal operator docs - package-level usage instructions - human review of the action contract ## 5. Expose CLI ```js #!/usr/bin/env node import { app } from "./app.js"; await app.createCli().main(); ``` Example commands: ```text task-app create-task --title "Follow up with design review" --priority high task-app create-task --json '{"title":"Follow up with design review","priority":"high"}' task-app create-task --schema task-app actions task-app manifest task-app build task-app build docs task-app docs task-app package task-app publish task-app lint task-app mcp task-app mcp --stdio task-app dev --port 4321 ``` Notes: - Action names can be called as snake_case or kebab-case. - `--schema` prints the input JSON Schema. - `--confirm` is required for actions that require confirmation. - CLI exit codes are mapped from runtime error codes. See [Runtime Semantics](/en/docs/runtime). ## 6. Expose JSON Automation ```js const runner = app.createJsonRunner(); const result = await runner.invoke({ action: "create_task", input: { title: "Follow up with design review", priority: "high", }, }); ``` Use the JSON runner when a script, test harness, worker, or HTTP wrapper already has structured input and output. ## 7. Expose MCP ```js const handleMcp = app.createMcpHandler(); await handleMcp({ jsonrpc: "2.0", id: 1, method: "tools/list", }); await handleMcp({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "create_task", arguments: { title: "Follow up with design review", priority: "high", }, }, }); ``` MCP exposure is filtered by default: - actions must support the `mcp` surface - `private` actions are excluded unless you opt in - `destructive` actions are excluded unless you opt in ## 8. Expose OpenAI And AI SDK Tools ```js const openaiTools = app.createOpenAITools(); const responsesTools = app.createOpenAIResponsesTools(); const aiSdkTools = app.createAISDKTools(); const task = await aiSdkTools.create_task.execute({ title: "Follow up with design review", priority: "high", }); ``` Default LLM tool filtering: - private actions are excluded - destructive actions are excluded - the action must support the `ai-sdk` surface If you want the full runtime envelope instead of raw `data`, pass `returnEnvelope: true` when you build AI SDK tools. ## 9. Use From Existing React UI ```js const { useAction } = app.createReactAdapter(); const runCreateTask = useAction(createTask); const result = await runCreateTask({ title: "Follow up with design review", priority: "high", }); ``` React invocation uses the same runtime but forces the surface to `react`. Confirmation is not blocked at runtime for the `react` surface, so your UI should own the confirmation flow. ## 10. Start The Local Dev Console ```text task-app dev --port 4321 ``` The dev console provides: - action list - generated input schema - JSON input editor - structured runtime result preview ## 11. Build Official Artifacts When you want to ship CLI or MCP launchers from the same app definition: ```js await app.build({ targets: ["bundle"], appModule: "./src/ageniti/app.js", appExport: "app", outDir: "./dist/ageniti", }); ``` If you prefer the CLI flow: ```text task-app build bundle --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti task-app build docs --out-dir ./dist/ageniti task-app docs --out-dir ./dist/ageniti ``` If `appModule` is omitted, Ageniti tries to discover a default Node-safe entry such as `./src/ageniti/app.js`. Bundle output includes: - `ageniti.manifest.json` - `ageniti.actions.json` - `cli.mjs` - `mcp-stdio.mjs` - `ageniti.mcp.json` - `GUIDE.md` - `package.json` - `README.md` - `ageniti.bundle.json` If you also want a distributable tarball, run: ```text task-app package --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` That command builds the bundle and runs `npm pack` inside it. For publish rehearsal or release: ```text task-app publish --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti task-app publish --live --access public --tag latest --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` `publish` defaults to `npm publish --dry-run`. Pass `--live` only for a real publish. The generated bundle also includes its own `README.md`. That file explains the concrete deployment shapes: - CLI: publish or distribute the generated npm package, then users run the generated bin. - MCP: point a host at `node ./mcp-stdio.mjs` during local development, or at the published `-mcp` command after install. - HTTP: deploy your own backend and mount `app.createHttpHandler()` so your existing auth and infrastructure stay in control. See [Packaging](/en/docs/packaging) for the full install, publish, and MCP client examples. ## 12. Bootstrap Or Diagnose A Project If you are starting from an existing React or Expo application, Ageniti also includes project tools: ```text npx @ageniti/core init react npx @ageniti/core init expo npx @ageniti/core init next npx @ageniti/core doctor ``` Use `init` to scaffold a headless entry. Use `doctor` to inspect whether the current project already has a valid build entry and whether it looks safe for launcher generation. If you want reproducible defaults for every teammate, add `ageniti.config.json`: ```json { "build": { "appModule": "./src/ageniti/app.ts", "appExport": "app", "outDir": "./dist/ageniti", "includePackageJson": true, "typescriptRuntime": "tsx" }, "mcp": { "transport": "stdio" }, "package": { "name": "task-app-ageniti", "version": "0.1.0", "description": "CLI and MCP bundle for task-app", "private": false, "binName": "task-app" } } ``` After that, your generated app CLI can run `build` without repeating the same flags, for example `task-app build`. ## 13. TypeScript-Only Entrypoints If your project uses `src/ageniti/app.ts` without a JavaScript wrapper, Ageniti can still generate CLI and MCP launchers when the `tsx` runtime is available. Typical options: - install `tsx` and set `build.typescriptRuntime` to `tsx` - point `--app-module` at compiled JavaScript - create a tiny `src/ageniti/app.js` wrapper that re-exports the app When Ageniti detects a React page, layout, or component file instead of a headless entry, `doctor` and `build` fail with a targeted error so you do not accidentally package UI modules as MCP or CLI launchers. ## 14. Know What To Read Next - [API Reference](/en/docs/api) for the full public API. - [Entry Points](/en/docs/entry-points) for package export paths. - [Runtime Semantics](/en/docs/runtime) for envelopes, retry, timeout, and errors. - [Recipes](/en/docs/recipes) for common integration patterns. - [Release Checklist](/en/docs/release-checklist) for packaging and publish checks. ### Agentic Primitives Path: /en/docs/agentic-primitives Description: The foundational building blocks Ageniti provides — action contract, schema interop, bulk handlers, streaming events, and the typed client. # Agentic Primitives This page walks through the five core primitives Ageniti gives you, in the order you usually encounter them. ## 1. The Action Contract Every callable capability is an **action**: a typed input/output pair plus a declarative side-effects policy. The contract is the source of truth for every surface. ```ts import { defineAction, s } from "@ageniti/core"; export const createTask = defineAction({ name: "create_task", description: "Create a task in the user's inbox.", sideEffects: "write", idempotency: "conditional", input: s.object({ title: s.string().min(1), priority: s.enum(["low", "high"]).default("low"), }), output: s.object({ id: s.string(), title: s.string() }), async run({ title, priority }, ctx) { ctx.logger.info("Creating task", { title }); const task = await ctx.services.tasks.create({ title, priority }); return { id: task.id, title: task.title }; }, }); ``` Required fields are `name`, `description`, and `run`. Reserved names that clash with built-in CLI commands (`actions`, `mcp`, `dev`, …) are rejected at definition time. ## 2. Bring Your Own Schema `defineAction` accepts any schema implementing **Standard Schema v1** or any Zod-like schema (`.safeParse`, `.parse`): ```ts import { z } from "zod"; import { defineAction } from "@ageniti/core"; export const search = defineAction({ name: "search_tasks", description: "Search for tasks matching a query.", input: z.object({ query: z.string(), limit: z.number().int().optional(), }), output: z.object({ results: z.array(z.object({ id: z.string(), title: z.string() })), }), async run({ query, limit }) { return { results: await tasks.search(query, limit ?? 20) }; }, }); ``` Foreign schemas are detected by duck typing. JSON Schema for MCP tool definitions and OpenAI tool specs is generated through best-effort introspection (`ZodObject`, `ZodArray`, `ZodUnion`, `ZodOptional`, `ZodDefault`, etc.). If your schema is exotic, override the JSON Schema: ```ts import { wrapSchema } from "@ageniti/core/schema-adapter"; const wrapped = wrapSchema(myCustomSchema, { jsonSchema: { type: "object", properties: { … } }, }); ``` ## 3. Bulk-Wrap Existing Functions For Next.js Server Actions, tRPC procedures, plain handler records: ```ts import { actionsFromHandlers, s } from "@ageniti/core"; import * as handlers from "@/app/actions/tasks"; // your existing functions export const actions = actionsFromHandlers(handlers, { createTask: { description: "Create a task.", input: s.object({ title: s.string() }), sideEffects: "write", }, searchTasks: { description: "Search tasks.", input: s.object({ query: s.string() }), }, }); ``` Or for full control, `defineActions` accepts a record of configs (or plain functions for read-only no-input cases): ```ts import { defineActions, s } from "@ageniti/core"; export const actions = defineActions({ createTask: { description: "Create a task.", input: s.object({ title: s.string() }), run: async ({ title }) => tasks.create({ title }), }, ping: () => ({ ok: true, time: Date.now() }), }); ``` CamelCase keys are normalized to snake_case action names automatically. ## 4. Streaming Events Every action runs through a runtime that emits **live events** as it executes. Any consumer (UI, agent, log shipper) can subscribe via `runtime.stream()`: ```ts const events = runtime.stream("create_task", { title: "Ship v1" }); for await (const event of events) { if (event.type === "log") console.log(event.level, event.message); if (event.type === "progress") updateProgressBar(event.percent); if (event.type === "artifact") attachToUi(event.artifact); if (event.type === "result") finalize(event.envelope); } ``` Events come from `ctx.logger.*`, `ctx.progress.report()`, and `ctx.artifacts.add()` inside your `run()` function. The CLI's `--ndjson` mode and the React `useAction` hook are both built on this primitive. ## 5. Typed Client + Codegen A typed client wraps any runtime or remote `@ageniti` HTTP server. Calls return validated `data` on success and throw `AgenitiClientError` on failure. Remote HTTP mode can send `metadata`, `confirm`, and `idempotencyKey`, but trusted `user` / `auth` must be resolved server-side: ```ts import { createClient } from "@ageniti/core/client"; // In-process const client = createClient({ runtime }); const task = await client.create_task({ title: "Hello" }); // Or talk to a remote @ageniti HTTP server const remote = createClient({ url: "https://api.example.com" }); const tasks = await remote.search_tasks({ query: "today" }); ``` Generate `.d.ts` for IDE autocomplete on the consumer side: ```ts import { generateClientTypes } from "@ageniti/core/client-gen"; import { writeFile } from "node:fs/promises"; await writeFile(".ageniti/client.d.ts", generateClientTypes(actions, { interfaceName: "TasksClient", })); ``` ## React Hook (built on the streaming primitive) For React UI, the `useAction` hook subscribes to `runtime.stream` so logs, artifacts, and progress update live during the invocation: ```tsx import { useAction } from "@ageniti/core/react-hooks"; function CreateTaskButton() { const { invoke, status, data, error, logs, progress, cancel } = useAction(createTask, { runtime }); return ( <> {status === "loading" && } {status === "success" &&

Created task {data.id}

} {status === "error" &&

Error: {error.message}

} ); } ``` State machine: `idle → loading → (success | error | cancelled)`. Component unmount auto-aborts. New `invoke` calls cancel the previous one. ## Test Utilities ```ts import { createTestRuntime, expectOk, expectError, collectStream, } from "@ageniti/core/test-utils"; test("create_task happy path", async () => { const t = createTestRuntime([createTask], { services: { tasks: stubTasks } }); const env = await t.invoke("create_task", { title: "Hello" }); const data = expectOk(env); expect(data.title).toBe("Hello"); }); ``` Framework-agnostic — works with `node:test`, vitest, jest. The `createTestRuntime` helper preconfigures `surface: "json"` and bypasses the confirmation gate so destructive actions can be tested without ceremony. ## How It Fits Together The five primitives above are the foundation. Every other surface — CLI generation, MCP server, OpenAI tool specs, AI SDK tools, HTTP server — is a thin adapter over the same action contract and the same streaming runtime. Whatever you build on top (agents, planners, workflows, custom UIs) reads from the same contract and subscribes to the same events. ### Core Concepts Path: /en/docs/concepts # Core Concepts Ageniti is the action primitive layer for agentic applications. It gives your app a clear boundary where selected capabilities become callable, structured, and reviewable across every surface. ## App An Ageniti app is the container for the capabilities you choose to expose. ```js export const app = createAgenitiApp({ name: "workspace-tools", description: "Workspace capabilities exposed through controlled surfaces.", actions: [searchTasks, createTask], services: { tasks }, }); ``` The app object is where you create CLI, MCP, HTTP, React, JSON, docs, and packaging surfaces from the same action list. ## Action An action is one explicit app capability. It should represent something your product already knows how to do: - search records - create a task - export a report - inspect account state - trigger a controlled internal workflow An action is not a React component, route, prompt, or agent plan. ## Contract The action contract is the source of truth. It includes: - `name` - `description` - `input` - `output` - `sideEffects` - `permissions` - `supportedSurfaces` - `visibility` - `docs` Every surface should be generated from this contract instead of reimplementing business logic. ## Runtime The runtime executes actions consistently. It handles: - action lookup - surface checks - input validation - confirmation gates - permission checks - middleware - timeout and retry - logs, progress, and artifacts - output validation - structured success and failure envelopes ## Surface A surface is a way for a caller to use an action. Supported surfaces include CLI, HTTP, MCP, JSON runner, React invocation, local dev console, OpenAI-compatible tools, and AI SDK-style tools. Surfaces should stay thin. They translate their own input format into a runtime invocation and return the runtime result. ## Headless Entry React, Next.js, and Expo apps should keep UI files separate from the Ageniti entry used for CLI, MCP, HTTP, package, and publish artifacts. Recommended layout: ```text src/ageniti/app.js src/ageniti/actions/create-task.js src/ageniti/services/tasks.js ``` The headless entry should import services and actions, not React components or browser-only APIs. ## Skill Guide Ageniti can export one deterministic `GUIDE.md` from app and action docs fields. This guide is for agents, coding assistants, and automation systems that need to understand how to use the app safely. It is not model-generated. It is derived from the contract you wrote. ### Surfaces Path: /en/docs/surfaces # Surfaces Surfaces are the callable interfaces generated from an Ageniti app. Each surface should reuse the shared runtime instead of duplicating validation, authorization, logging, or output handling. ## CLI Use CLI when humans, scripts, CI jobs, or local operators need a command entry point. ```js await app.createCli().main(); ``` Generated action commands support flags, `--json`, `--schema`, stable JSON output, and confirmation for protected actions. ## MCP Use MCP when an MCP host should discover and call selected app capabilities. ```js const handleMcp = app.createMcpHandler(); ``` A bundled app can also generate a stdio launcher: ```text node ./dist/ageniti/mcp-stdio.mjs ``` Private and destructive actions are filtered from MCP discovery by default. ## HTTP Use HTTP when you want to put Ageniti behind your own server, API gateway, route handler, or internal platform. Default routes: - `GET /ageniti/actions` - `POST /ageniti/actions/:name/invoke` The HTTP handler returns the same structured runtime envelope as other surfaces. ## JSON Runner Use the JSON runner for tests, local scripts, automation, and deterministic invocation without a network server. ```js const result = await app.createJsonRunner().invoke({ action: "create_task", input: { title: "Review release" }, }); ``` ## React Invocation Use React invocation when an existing UI wants to call the same action as CLI or MCP. ```js const { useAction } = app.createReactAdapter(); const runCreateTask = useAction(createTask); ``` Ageniti does not replace your components, router, state management, or form library. ## OpenAI And AI SDK Tools Use tool adapters when an LLM stack needs function/tool schemas generated from action contracts. ```js const openaiTools = app.createOpenAITools(); const responsesTools = app.createOpenAIResponsesTools(); const aiSdkTools = app.createAISDKTools(); ``` LLM-oriented adapters filter private, local, and destructive actions by default. ## Typed Client Use the typed client when other code (server, frontend, scripts, tests) needs to call actions through the same contract — in-process or over HTTP. ```js import { createClient } from "@ageniti/core/client"; // In-process const client = createClient({ runtime }); const task = await client.create_task({ title: "Hello" }); // Or talk to a remote @ageniti HTTP server const remote = createClient({ url: "https://api.example.com" }); ``` For IDE autocomplete on the consumer side, generate `.d.ts` from your action manifest with `generateClientTypes`. ## Streaming Events All surfaces are built on the same streaming runtime. Subscribe directly with `runtime.stream(...)` to observe an invocation live: ```js for await (const event of runtime.stream("create_task", input)) { if (event.type === "log") /* ... */; if (event.type === "progress") /* ... */; if (event.type === "result") return event.envelope; } ``` The CLI's `--ndjson` mode and the React `useAction` hook are both built on this primitive. ## Dev Console Use the dev console during integration to inspect action schemas and invoke actions locally. ```text task-app dev --port 4321 ``` The dev console is for local testing, not a hosted production UI. ## Choosing A Surface - Start with CLI when you want the fastest local proof. - Add MCP when an agent host needs tool discovery. - Add HTTP when your own backend should own transport and auth. - Add React invocation when existing UI should reuse the same action. - Add generated tool schemas when an LLM stack needs structured function definitions. ### Safety Model Path: /en/docs/safety # Safety Model Ageniti is built around explicit exposure. Your app chooses which capabilities become actions and which surfaces are allowed to call them. ## Visibility Use `visibility` to describe who should be able to discover or use an action. ```js defineAction({ name: "create_task", visibility: "public", // ... }); ``` Common meanings: - `private`: internal only - `local`: local development or operator usage - `agent`: suitable for agent/tool usage - `public`: safe for public manifests and docs Private actions are hidden from public tool surfaces by default. ## Supported Surfaces Use `supportedSurfaces` to explicitly limit where an action can run. ```js defineAction({ name: "export_report", supportedSurfaces: ["cli", "json", "dev"], // ... }); ``` If a runtime invocation arrives from an unsupported surface, it returns `UNSUPPORTED_SURFACE`. ## Side Effects Use `sideEffects` to communicate operational risk. - `read`: no external mutation - `write`: changes external state - `destructive`: deletes, overwrites, cancels, closes, or performs irreversible work Destructive actions require confirmation by default and are filtered from MCP and LLM adapters unless explicitly included. ## Permissions Permissions are declared on the action and enforced through the app-level `permissionChecker`. ```js createAgenitiApp({ actions, permissionChecker({ action, context }) { return action.permissions.every((permission) => context.auth?.permissions?.includes(permission) ) || "Missing required permission."; }, }); ``` Ageniti does not replace your auth system. It gives your app a consistent hook for applying it across surfaces. ## Public Metadata Use `publicMetadata` for data that can safely appear in manifests, MCP tools, and LLM tool schemas. Use `metadata` for internal app-only fields. ## Confirmation Set `requiresConfirmation: true` when an action should not run without an explicit confirmation flag. Destructive actions inherit this behavior by default. ## Practical Checklist - Keep actions narrow and named clearly. - Prefer structured output over prose-only strings. - Mark write and destructive operations honestly. - Limit high-risk actions with `supportedSurfaces`. - Use `permissions` plus `permissionChecker` for real authorization. - Put safe usage guidance in `description` and `docs`. - Review generated manifests before publishing. ### Runtime Semantics Path: /en/docs/runtime # Runtime Semantics Ageniti keeps the important behaviour in the runtime so that CLI, HTTP, MCP, React, JSON automation, dev tools, and LLM adapters all share one execution model. ## Execution Flow When you invoke an action, the runtime performs these steps: 1. Resolve the action by name or object reference. 2. Check whether the action supports the requested surface. 3. Validate input against the action input schema. 4. Enforce confirmation for protected actions. 5. Run the permission checker. 6. Execute middleware. 7. Run the action with timeout and retry handling. 8. Ensure the output can be serialised as JSON. 9. Validate output against the output schema, if present. 10. Return a structured success or failure envelope. ## Surface Rules Supported surfaces are declared on each action through `supportedSurfaces`. Default supported surfaces: ```text cli, json, http, mcp, react, dev, ai-sdk ``` If a surface is not supported, the runtime returns `UNSUPPORTED_SURFACE`. ## Confirmation Rules Actions with `requiresConfirmation: true` require an explicit confirmation flag. By default, destructive actions inherit confirmation automatically. Runtime confirmation is enforced for most surfaces, but not for: - `react` - `dev` That means UI surfaces should handle confirmation in the UI layer before invoking the action. ## Permission Checks `permissionChecker({ action, input, context })` must return either: - `true` to allow execution - `false` to deny execution with a generic authorisation message - a string to deny execution with a custom message If the check fails, the runtime returns `AUTHORIZATION_ERROR`. ## Timeout And Retry Timeout is controlled by: - action-level `timeoutMs` - per-invocation `timeoutMs` Retry is controlled by: - action-level `retry` - per-invocation `retry` Retry only happens when the thrown error is retryable. That usually means the action throws an `AgenitiError` with `retryable: true`, or the timeout logic marks the failure as retryable. `retry: true` normalises to: ```json { "retries": 2, "delayMs": 100 } ``` Retries use linear backoff based on attempt count. ## Logging, Progress, And Artifacts Every invocation accumulates structured logs and artifacts. From `context.logger`: - `debug(message, fields?)` - `info(message, fields?)` - `warn(message, fields?)` - `error(message, fields?)` From `context.progress.report()`: - records a log entry with `fields.type = "progress"` - may include `percent` - may include additional structured fields From `context.artifacts.add()`: - normalises missing `id` - defaults `type` to `"file"` - stores `metadata` as an object ## Streaming Events Every invocation can be observed live by another caller via `runtime.stream(name, input, options)`, which returns an async iterable yielding `{ type: "log" | "artifact" | "progress" | "result", ... }` as the action runs. The stream always ends with one `result` event carrying the final envelope. Events come from `ctx.logger.*`, `ctx.progress.report()`, and `ctx.artifacts.add()` inside the action. The CLI `--ndjson` mode, the React `useAction` hook, and the typed client `$stream` method are all built on this primitive. ## Idempotency Keys Pass `idempotencyKey` in the invocation options to deduplicate retries. Successful envelopes for non-`read` actions are cached by action name, key, validated input, surface, and trusted caller fingerprint. Replays return a fresh copy of the cached envelope with `meta.idempotent === "replayed"`. Cache size is capped (default 1000 entries, configurable via `idempotencyMaxEntries`) with TTL (default 5 minutes, `idempotencyTtlMs`). ## Concurrency Limits Set `concurrency: { max: N }` on an action to bound active invocations. Calls beyond the limit return `CONCURRENCY_LIMIT` (retryable). Useful for protecting upstream rate limits. ## Cancellation Pass `signal: AbortSignal` to cancel an invocation externally. The CLI wires `SIGINT` to abort by default. Each retry attempt gets its own internal `AbortController`, so retries after a timeout still see a fresh signal. ## Hooks And Redaction Pass `hooks` to `createRuntime` for invocation telemetry, and `redact` to control how secrets are stripped from logs, artifact metadata, and error messages. ```js createRuntime({ actions, hooks: { onInvocationStart: ({ action, invocationId, input }) => metrics.timer.start(invocationId), onInvocationEnd: ({ action, invocationId, envelope }) => metrics.timer.end(invocationId, envelope.ok), }, redact: { keys: ["ssn", "credit_card"] }, }); ``` Default redaction keys cover `password`, `passwd`, `secret`, `token`, `apikey`, `api_key`, `authorization`, `cookie`, `session`, `x-api-key`, `access_token`, `refresh_token`, `private_key`, and `client_secret`. Error messages additionally have known token shapes redacted (`Bearer ...`, JWT, `sk-...`, `ghp_...`, `xoxb-...`, `key=value`). ## Success Envelope ```json { "ok": true, "data": {}, "artifacts": [], "logs": [], "meta": { "action": "create_task", "invocationId": "invocation-id", "surface": "cli", "durationMs": 12 } } ``` ## Failure Envelope ```json { "ok": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid action input.", "issues": [], "retryable": false }, "artifacts": [], "logs": [], "meta": { "action": "create_task", "invocationId": "invocation-id", "surface": "mcp", "durationMs": 3 } } ``` ## Common Runtime Error Codes Core runtime codes: - `ACTION_NOT_FOUND` - `UNSUPPORTED_SURFACE` - `VALIDATION_ERROR` - `CONFIRMATION_REQUIRED` - `AUTHORIZATION_ERROR` - `OUTPUT_SERIALIZATION_ERROR` - `OUTPUT_VALIDATION_ERROR` - `TIMEOUT` - `CANCELLED` - `INTERNAL_ERROR` Wrapper-specific codes you may also see: - `INVALID_JSON_RUNNER_PAYLOAD` - `DEV_SERVER_ERROR` - `NOT_FOUND` from the local dev server HTTP routes ## How Errors Are Normalised If an action throws: - `AgenitiError`, its `code`, `message`, `issues`, and `retryable` fields are preserved - `AbortError`, the runtime returns `CANCELLED` - any other error, the runtime returns `INTERNAL_ERROR` ## CLI Exit Codes The built-in CLI maps runtime errors to process exit codes. Important mappings: - `VALIDATION_ERROR` -> `2` - `AUTHENTICATION_ERROR` -> `3` - `AUTHORIZATION_ERROR` -> `3` - `ACTION_NOT_FOUND` -> `4` - `EXTERNAL_SERVICE_ERROR` -> `5` - `TIMEOUT` -> `124` - `CANCELLED` -> `130` - anything else -> `1` ## Output Guarantees Ageniti only returns values that can be serialised safely as JSON. If an output schema exists, the runtime validates the action result against it before returning success. This makes the result envelope suitable for CLI output, MCP structured content, tests, and machine-to-machine integrations. ### Skill Guide Path: /en/docs/skill Description: A compact reference for the skills an Ageniti app exposes to agents, coding assistants, and automation systems. # Skill Guide This page describes how an Ageniti app exposes its skills to agents, coding assistants, and automation systems. The docs site also exposes `/llms.txt`, which includes this page in a plain-text bundle for machine readers. ## What Ageniti Is Ageniti is an SDK for building apps that agents can use. It lets a React or TypeScript app expose selected capabilities as structured actions that can be called through CLI, HTTP, MCP, OpenAI-compatible tools, Vercel AI SDK-style tools, JSON automation, a local dev console, and React invocation. Ageniti does not create an agent. It makes an app callable by agents. ## Core Mental Model ```text Existing app capability | v Ageniti action contract | v Shared runtime | +--> CLI +--> HTTP +--> MCP +--> OpenAI tools +--> AI SDK tools +--> JSON runner +--> Dev console +--> React invocation ``` The action contract is the source of truth. Every external surface should go through the shared runtime so validation, permissions, confirmation, timeout, retry, middleware, logging, artifacts, and output validation behave consistently. ## Canonical Import ```js import { createAgenitiApp, defineAction, s } from "@ageniti/core"; ``` Use subpath imports only when the host needs a narrower boundary: ```js import { createMcpHandler } from "@ageniti/core/mcp"; import { createHttpHandler } from "@ageniti/core/http"; import { createAISDKTools, createOpenAITools } from "@ageniti/core/ai-sdk"; ``` ## Minimal Action Pattern ```js import { createAgenitiApp, defineAction, s } from "@ageniti/core"; export const createTask = defineAction({ name: "create_task", version: "1.0.0", description: "Create a workspace task.", visibility: "public", sideEffects: "write", idempotency: "conditional", permissions: ["task:create"], input: s.object({ title: s.string().min(1).describe("Task title"), priority: s.enum(["low", "normal", "high"]).default("normal"), }), output: s.object({ taskId: s.string(), status: s.string(), }), async run(input, ctx) { return ctx.services.tasks.create(input); }, }); export const app = createAgenitiApp({ name: "task-app", description: "Workspace task operations exposed to agents.", actions: [createTask], services: { tasks, }, }); ``` ## Recommended React And Expo Shape For React, Next.js, Expo, or any app with UI code, keep Ageniti in a headless Node-safe entry: ```text src/ageniti/app.js src/ageniti/actions/create-task.js src/ageniti/services/tasks.js ``` Do not import React components, browser-only APIs, route handlers, or mobile runtime code from the Ageniti entry used for CLI, MCP, HTTP, package, or publish artifacts. Put reusable business logic in services and call those services from actions. ## Surfaces - `app.createCli()` creates a CLI with action commands, schemas, manifests, docs, build, package, publish, init, doctor, lint, MCP, and dev commands. - `app.createHttpHandler()` creates a lightweight HTTP JSON handler that invokes actions through the shared runtime. - `app.createMcpHandler()` creates an MCP JSON-RPC handler for `tools/list` and `tools/call`. - `app.createOpenAITools()` creates OpenAI Chat Completions-style tool definitions. - `app.createOpenAIResponsesTools()` creates OpenAI Responses-style function tool definitions. - `app.createAISDKTools()` creates Vercel AI SDK-style executable tools. - `app.createJsonRunner()` creates a structured runner for scripts, tests, and automation. - `app.createDevServer()` starts a local action inspection and testing console. - `app.createReactAdapter()` creates a React-friendly action invocation adapter for existing UI code. ## Safety Rules For Agents - Call actions through the runtime or through app-created adapters. Do not call `action.run()` directly. - Respect `visibility`, `supportedSurfaces`, `sideEffects`, `requiresConfirmation`, `permissions`, `version`, `deprecated`, and `deprecation`. - Private actions are not public API. Do not expose or invoke them from external agent surfaces. - Destructive actions require confirmation by default and are filtered from LLM-oriented surfaces unless explicitly allowed. - Put secrets, internal identifiers, and non-public implementation notes in `metadata`, not `publicMetadata`. - Put host-facing instructions in `description`, `docs`, and `publicMetadata`. - Treat `GUIDE.md`, manifests, schemas, and `publicMetadata` as the public contract. ## Documentation Export Ageniti can export one deterministic guide from app-level and action-level natural-language fields. It does not call a model and does not infer hidden behavior from UI code. Useful fields: - app `description` - app `docs.summary` - app `docs.audience` - app `docs.whenToUse` - app `docs.setup` - app `docs.operationalNotes` - action `description` - action `docs.whenToUse` - action `docs.whenNotToUse` - action `docs.usageNotes` - action `docs.inputExample` - action `docs.outputExample` - action `publicMetadata` Export commands: ```text task-app docs task-app docs --out-dir ./dist/ageniti task-app build docs --out-dir ./dist/ageniti task-app build bundle --out-dir ./dist/ageniti ``` The output file is `GUIDE.md`. A bundle includes it automatically. ## Build And Package ```text npx @ageniti/core init react npx @ageniti/core init expo npx @ageniti/core init next npx @ageniti/core doctor task-app build task-app build bundle --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti task-app package --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti task-app publish --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` `bundle` produces: - `ageniti.manifest.json` - `ageniti.actions.json` - `ageniti.mcp.json` - `cli.mjs` - `mcp-stdio.mjs` - `GUIDE.md` - `package.json` - `README.md` - `ageniti.bundle.json` The generated npm package is an app distribution package containing runnable CLI and MCP launchers plus manifests and documentation. The generated bundle `README.md` is part of the app contract. It explains: - how to run the CLI and MCP launchers locally - how to create a tarball with `npm pack` - how to publish the app package to npm - how MCP clients should call either `node ./mcp-stdio.mjs` or the installed `-mcp` - why HTTP deployment belongs in the user's own backend via `app.createHttpHandler()` ## Do Not - Do not describe Ageniti as an agent framework. - Do not make Ageniti own planning, memory, workflow orchestration, routing, or UI state. - Do not scan arbitrary React components and expose them as tools automatically. - Do not bypass runtime validation and permissions. - Do not put secrets in generated docs, public metadata, manifests, or examples. - Do not expose hidden actions just because a caller guessed the action name. ## Useful Files - `README.md`: human overview. - `docs/getting-started.md`: first working integration. - `docs/api.md`: public API reference. - `docs/scope.md`: explicit product boundary. - `docs/release-checklist.md`: publish checklist. - `docs/skill.md`: skill-facing SDK guide included in the npm package. ### Packaging Path: /en/docs/packaging # Packaging Ageniti can turn one app definition into distributable artifacts for CLI, MCP, manifests, and skill documentation. ## What Gets Built The bundle target writes: ```text ageniti.manifest.json ageniti.actions.json ageniti.mcp.json cli.mjs mcp-stdio.mjs GUIDE.md package.json README.md ageniti.bundle.json ``` These files describe the app, expose runnable launchers, and package the app for installation or review. The generated `README.md` is not filler. It is included so the bundle itself explains how to run, install, publish, and connect the app surfaces. ## Required App Module Launcher targets need a Node-safe module that exports your Ageniti app. ```text src/ageniti/app.js ``` Avoid importing React components, browser-only APIs, route files, or mobile runtime code from this entry. ## Build ```text task-app build bundle --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` If your project has an `ageniti.config.json`, build commands can read defaults from it. ## Package ```text task-app package --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` This builds the bundle and runs `npm pack` inside the generated output directory. The output is a tarball such as: ```text task-app-ageniti-0.1.0.tgz ``` You can test the same install shape your users will receive: ```text npm install -g ./dist/ageniti/task-app-ageniti-*.tgz task-app task-app-mcp ``` ## Publish ```text task-app publish --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` `publish` defaults to a dry run. Use `--live` only when you intentionally want a real npm publish. ```text task-app publish --live --access public --tag latest --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti ``` If npm requires two-factor auth, complete the extra verification step requested by npm during publish. After the package is published, downstream users install the generated app package, not the Ageniti SDK itself: ```text npm install -g task-app-ageniti task-app task-app-mcp ``` ## MCP Descriptor `ageniti.mcp.json` includes command metadata and snippets for common MCP host configuration. The generated stdio command points at: ```text node ./mcp-stdio.mjs ``` For local development, an MCP client can point at the generated bundle folder: ```json { "mcpServers": { "task-app": { "command": "node", "args": ["./mcp-stdio.mjs"], "cwd": "/absolute/path/to/dist/ageniti" } } } ``` After global install, many MCP clients can call the generated MCP bin: ```json { "mcpServers": { "task-app": { "command": "task-app-mcp", "args": [] } } } ``` Use the exact snippets in `ageniti.mcp.json` as the source of truth for a generated bundle. ## CLI Deployment The generated CLI can run locally from the bundle: ```text node ./dist/ageniti/cli.mjs actions node ./dist/ageniti/cli.mjs manifest ``` After npm install, users run the generated bin: ```text task-app actions task-app manifest task-app create-task --json '{"title":"Follow up"}' ``` The bin name comes from `package.binName` in `ageniti.config.json`. ## HTTP Deployment HTTP is normally deployed inside your own backend rather than from the static bundle. Mount the same Ageniti app in your server process: ```js const handle = app.createHttpHandler(); ``` Requests use: ```text GET /ageniti/actions POST /ageniti/actions//invoke ``` This keeps auth, sessions, network policy, and hosting inside your infrastructure. Use `npx @ageniti/core init host-http` for a starter. ## Skill Documentation `GUIDE.md` is generated from app and action metadata. It gives agents, coding assistants, and automation systems a compact usage guide for the capabilities exposed by the app. ## Release Review Before publishing a new bundle: - inspect `ageniti.manifest.json` - inspect `ageniti.actions.json` - inspect `ageniti.mcp.json` - run the generated `cli.mjs` - run the generated `mcp-stdio.mjs` with an MCP host - compare old and new manifests with `task-app diff` ### Entry Points Path: /en/docs/entry-points # Entry Points `@ageniti/core` ships a root export plus narrower subpath exports. ## Recommended Default Most applications should start with the root package: ```js import { createAgenitiApp, defineAction, s } from "@ageniti/core"; ``` That keeps usage simple and matches the examples in the documentation. ## Available Exports ### Root Export ```text @ageniti/core ``` Use the root export when you want the main authoring API in one place. It carries the action contract, runtime, transports, adapters, schema, client, and test utilities — everything you need at runtime in your app or host. > **Since 0.2.0**: the packaging, docs, and project tool-chain helpers (`buildArtifacts`, `packageArtifacts`, `publishArtifacts`, `createGuideDoc`, `exportDocs`, `initProject`, `doctorProject`, `findDefaultAppModule`, `loadProjectConfig`, `detectTypeScriptRuntime`, `supportsTypeScriptEntrypoints`) are no longer re-exported from the root entry. Import them from their dedicated subpath (`@ageniti/core/build`, `@ageniti/core/docs`, `@ageniti/core/project`) so the runtime hot path stays lean. ### Subpath Exports ```text @ageniti/core/ai-sdk @ageniti/core/adapters @ageniti/core/app @ageniti/core/build @ageniti/core/cli @ageniti/core/client @ageniti/core/client-gen @ageniti/core/core @ageniti/core/dev @ageniti/core/docs @ageniti/core/handlers @ageniti/core/http @ageniti/core/json-runner @ageniti/core/lint @ageniti/core/manifest @ageniti/core/mcp @ageniti/core/project @ageniti/core/react @ageniti/core/react-hooks @ageniti/core/schema @ageniti/core/schema-adapter @ageniti/core/test-utils ``` ## What Each Subpath Contains - `@ageniti/core/ai-sdk`: OpenAI-compatible tools, Responses tools, AI SDK tools, and combined manifests. - `@ageniti/core/adapters`: built-in surface adapters plus adapter helpers. - `@ageniti/core/app`: `createAgenitiApp()`. - `@ageniti/core/build`: `buildArtifacts()`, `packageArtifacts()`, `publishArtifacts()` — build, pack, and publish a distributable CLI tarball. - `@ageniti/core/docs`: `createGuideDoc()` and `exportDocs()` — emit a unified `GUIDE.md` for app actions. - `@ageniti/core/project`: `initProject()`, `doctorProject()`, `findDefaultAppModule()`, `loadProjectConfig()`, `detectTypeScriptRuntime()`, `supportsTypeScriptEntrypoints()` — project scaffolding and discovery helpers. - `@ageniti/core/cli`: `createCli()`. - `@ageniti/core/client`: `createClient()` and `AgenitiClientError` for the typed in-process / HTTP client. - `@ageniti/core/client-gen`: `generateClientTypes()` and `jsonSchemaToTs()` for emitting `.d.ts` typed-client files at build time. - `@ageniti/core/core`: runtime and action primitives such as `defineAction()`, `createRuntime()`, and the `ERROR_CODES` constants. - `@ageniti/core/dev`: `createDevServer()`. - `@ageniti/core/handlers`: `defineActions()`, `actionFromHandler()`, and `actionsFromHandlers()` for batch-wrapping existing functions. - `@ageniti/core/http`: `createHttpHandler()`, `createHttpServer()`, `parseRequestBody()`, `sendJson()`, and `sendText()`. - `@ageniti/core/json-runner`: `createJsonRunner()`. - `@ageniti/core/lint`: `lintActions()`. - `@ageniti/core/manifest`: manifest helpers such as `describeAction()`, `createSurfaceManifest()`, and `diffActionManifests()`. - `@ageniti/core/mcp`: MCP manifest, handler, and stdio server helpers (Content-Length and newline framing). - `@ageniti/core/react`: `createReactActionAdapter()`, `makeInvoker()`, and `streamAction()` (no React import — safe in non-React projects). - `@ageniti/core/react-hooks`: the `useAction()` state-machine hook (React peer dep). - `@ageniti/core/schema`: `s`, `isSchema()`, `assertSchema()`, `toJSONSchema()`, and `SchemaValidationError`. - `@ageniti/core/schema-adapter`: `wrapSchema()`, `isZodLike()`, `isStandardSchemaV1()`, and `zodToJsonSchema()` for foreign schema interop. - `@ageniti/core/test-utils`: `createTestRuntime()`, `expectOk()`, `expectError()`, `expectLog()`, `collectStream()`, `stubAction()`. ## Example Import Patterns Authoring app actions: ```js import { defineAction, s } from "@ageniti/core"; ``` Building only an MCP server wrapper: ```js import { createMcpHandler } from "@ageniti/core/mcp"; ``` Running a validation or tooling step: ```js import { lintActions } from "@ageniti/core/lint"; ``` Generating AI tool definitions without pulling the app helper: ```js import { createAISDKTools, createOpenAITools } from "@ageniti/core/ai-sdk"; ``` ## When To Prefer Subpaths Prefer a subpath when: - you are building a narrowly scoped integration layer - you want imports to reflect architectural boundaries - you are exposing only one surface from a package or service - you are writing tooling around manifests, linting, or schema conversion Prefer the root export when: - you are writing the main application integration - you want the shortest onboarding path - you do not need explicit module-level separation - you need build helpers or project setup helpers that do not have dedicated subpath exports ## Type Declarations Type declarations are now reduced to the smallest practical shape: the package keeps one root declaration file for the main entry point and one central subpath declaration map under `src/types/` for every public subpath export. ### API Reference Path: /en/docs/api # API Reference This page documents the public SDK surface exported by `@ageniti/core`. ## Top-Level Import For most applications, start with the root export: ```js import { createAgenitiApp, createRuntime, defineAction, s, } from "@ageniti/core"; ``` Use subpath exports only when you want a narrower import boundary. See [Entry Points](/en/docs/entry-points). > **Since 0.2.0**: build/package/publish, docs, project, lint, and manifest helpers documented below are imported from dedicated subpaths, not the root entry. Each section below shows the exact subpath. The `app.build()`, `app.package()`, `app.publish()`, `app.createGuideDoc()`, `app.exportDocs()` methods on the result of `createAgenitiApp()` are unchanged. ## `defineAction(config)` Defines a typed action contract. Required fields: - `name`: lowercase snake_case action name - `description`: human-readable action description - `run(input, context)`: action implementation Optional fields: - `version` - `title` - `input` - `output` - `visibility` - `sideEffects` - `idempotency` - `permissions` - `supportedSurfaces` - `timeoutMs` - `retry` - `concurrency` - `requiresConfirmation` - `metadata` - `publicMetadata` - `docs` - `deprecated` - `deprecation` Metadata model: - `metadata`: internal action metadata for app code and build-time tooling; it is not copied into public manifests or tool metadata - `publicMetadata`: safe-to-expose metadata copied into public manifests, MCP tool metadata, and LLM tool adapters Docs model: - `docs`: natural-language guidance used to generate a unified `GUIDE.md` - app-level `docs` supports `summary`, `audience`, `whenToUse`, `quickStart`, `setup`, `operationalNotes`, `sections`, and `examples` - action-level `docs` supports `whenToUse`, `whenNotToUse`, `usageNotes`, `inputExample`, and `outputExample` - `GUIDE.md` is deterministic output; it does not call a model or infer behaviour from UI code Versioning model: - `version`: action contract version, default `1.0.0` - `deprecated`: marks an action as deprecated without removing it - `deprecation`: optional message, replacement, and timeline metadata Important defaults: - `title`: derived from `name` - `input`: `s.object({})` - `visibility`: `"public"` - `sideEffects`: `"read"` - `idempotency`: `"unspecified"` - `permissions`: `[]` - `supportedSurfaces`: `cli`, `json`, `http`, `mcp`, `react`, `dev`, `ai-sdk` - `retry`: normalised to `{ retries, delayMs }` - `requiresConfirmation`: `true` for destructive actions unless overridden Example: ```js const deleteTask = defineAction({ name: "delete_task", description: "Delete a task by id.", sideEffects: "destructive", requiresConfirmation: true, input: s.object({ taskId: s.string().min(1), }), async run(input, ctx) { return ctx.services.tasks.remove(input.taskId); }, }); ``` ## `createAgenitiApp(options)` Creates an app object that owns actions, runtime, and surface helpers. ```js const app = createAgenitiApp({ name: "task-app", description: "Workspace task operations for external tools, automation, and agent callers.", attribution: { text: "Powered by Ageniti", vendor: "Ageniti", product: "Ageniti Core", url: "https://ageniti.dev", docsUrl: "https://ageniti.dev/docs", }, docs: { summary: "Use this app to create tasks and inspect status.", }, actions, services, permissionChecker, middleware, adapters, build, }); ``` Options: - `name`: required app name - `attribution`: optional source attribution metadata for CLI help, MCP manifests, tool metadata, generated guides, and bundle artifacts - `actions`: array of Ageniti actions - `services`: shared services injected into `context.services` - `permissionChecker`: authorisation hook - `middleware`: runtime middleware chain - `adapters`: custom surface adapter list for manifest generation - `build`: default build settings reused by `app.build()` and CLI build commands `attribution` is descriptive metadata, not telemetry. Use it when you want generated surfaces to show product origin or branding information in a structured way. Returned members: - `name` - `actions` - `adapters` - `runtime` - `manifest(options?)` - `actionManifest()` - `lint()` - `build()` - `package()` - `publish()` - `createGuideDoc()` - `exportDocs()` - `createCli()` - `createJsonRunner()` - `createHttpHandler()` - `createHttpServer()` - `createMcpHandler()` - `createMcpManifest()` - `createOpenAITools()` - `createOpenAIResponsesTools()` - `createAISDKTools()` - `createFunctionCallingManifest()` - `createReactAdapter()` - `createDevServer()` Use `createAgenitiApp()` when you want one app definition and convenient access to every supported surface. ## `createRuntime(options)` Creates the headless runtime used by all app capability surfaces. Options: - `actions` - `services` - `permissionChecker` - `middleware` - `hooks` - `redact` - `idempotencyCache` - `idempotencyTtlMs` - `idempotencyMaxEntries` Returned API: - `registry`: `Map` - `listActions({ surface? })` - `invoke(actionOrName, input?, invokeOptions?)` - `stream(actionOrName, input?, invokeOptions?)` `invokeOptions` supports: - `invocationId` - `surface` - `user` - `auth` - `env` - `services` - `metadata` - `signal` - `timeoutMs` - `retry` - `confirm` - `idempotencyKey` The runtime is responsible for: - action lookup - supported surface checks - input validation - confirmation checks - permission checks - middleware execution - timeout and retry - output serialisation and output validation - success and failure envelopes Read [Runtime Semantics](/en/docs/runtime) for the full execution model. ## Manifest And Registry Helpers ### `createActionRegistry(actions)` Builds a `Map` keyed by action name and throws on duplicates. ### `createActionManifest(actions)` Returns a plain array describing the public action contract, including schemas, visibility, confirmation, and `publicMetadata`. This helper is useful for inspection, tooling, tests, or custom wrappers. ### `describeAction(action)` ```js import { describeAction } from "@ageniti/core/manifest"; ``` Normalises a single action into a manifest-friendly object. ### `createSurfaceManifest({ appName, actions, adapters })` ```js import { createSurfaceManifest } from "@ageniti/core/manifest"; ``` Returns a manifest object with: - app name - generation timestamp - action descriptions - surface descriptions and capabilities ## CLI API ### `createCli(options)` Creates a CLI object. ```js const cli = createCli({ name: "task-app", attribution: { text: "Powered by Ageniti", }, actions, runtime, runtimeOptions, env, adapters, buildOptions, }); ``` Returned members: - `name` - `actions` - `runtime` - `run(argv?, io?)` - `main(argv?, io?)` Built-in commands: ```text [options] --json '{"field":"value"}' --schema actions manifest diff --previous old.json --next new.json build [manifest|cli|mcp|docs|bundle] [options] docs [options] package [options] publish [options] init [options] doctor [options] lint mcp mcp --stdio dev --port 4321 ``` Input behaviour: - snake_case actions can also be called as kebab-case commands - boolean flags accept `--flag`, `--flag true`, or `--no-flag` - array, object, and `any` inputs are parsed from JSON strings - runtime confirmation maps to `--confirm` Build command options: - `--out-dir ` - `--app-module ` - `--app-export ` - `--package-json` - `--cwd ` - `--filename ` for `docs` Launcher targets need a Node-safe app module so the generated files know what to import. If `--app-module` is omitted, Ageniti tries to discover a default entry such as `./src/ageniti/app.js`. ## JSON Runner API ### `createJsonRunner(options)` Creates a small structured invocation wrapper. ```js const runner = createJsonRunner({ actions, runtime, runtimeOptions }); ``` Returned API: - `runtime` - `invoke(payload)` Payload fields: - `action` - `input` - `confirm` - `user` - `auth` - `metadata` If the payload is not an object, the runner returns `INVALID_JSON_RUNNER_PAYLOAD`. ## MCP API ### `createMcpManifest(actions, options)` Returns: ```js { tools: [ { name, title, description, inputSchema, metadata, }, ], } ``` Filtering rules: - action must support the `mcp` surface - private actions are excluded unless `includePrivate: true` - local actions are excluded unless `includeLocal: true` - destructive actions are excluded unless `includeDestructive: true` ### `createMcpHandler(options)` Creates a JSON-RPC handler for `tools/list` and `tools/call`. ```js const handle = createMcpHandler({ actions, runtime, runtimeOptions, includePrivate, includeDestructive, }); ``` `tools/call` returns both a text block and `structuredContent` containing the runtime result. When a destructive tool is intentionally exposed with `includeDestructive: true`, callers still need `params.confirm: true` for execution. ### `createMcpStdioServer(options)` Creates a stdio transport around `createMcpHandler()` with automatic newline and `Content-Length` framing support. ```js await createMcpStdioServer({ actions, runtime }).start(); ``` Use this when another process expects JSON-RPC over stdio. Ageniti auto-detects newline-delimited and `Content-Length` framed requests. ## LLM Tool Adapters ### `createOpenAITools(actions, options)` Returns Chat Completions-style function tools. ### `createOpenAIResponsesTools(actions, options)` Returns Responses-style function tools. ### `createAISDKTools(actions, options)` Returns a Vercel AI SDK-style tools object. ```js const tools = createAISDKTools(actions, { runtime, returnEnvelope: true, }); ``` Each tool contains: - `description` - `parameters`: Ageniti schema object - `inputSchema`: JSON Schema representation - `execute(input, options?)` ### `createFunctionCallingManifest(actions, options)` Returns a combined summary: - `openaiChatTools` - `openaiResponsesTools` - `aiSdkTools` as action names Shared adapter options: - `runtime` - `strict` - `includePrivate` - `includeLocal` - `includeDestructive` - `surface` - `returnEnvelope` - `filter` Filtering rules: - action must support the selected surface, default `ai-sdk` - private actions are excluded unless `includePrivate: true` - local actions are excluded unless `includeLocal: true` - destructive actions are excluded unless `includeDestructive: true` - `filter(action)` can apply additional filtering ## React API ### `createReactActionAdapter(options)` Creates a React-friendly wrapper over the shared runtime. ```js const { runtime, useAction } = createReactActionAdapter({ actions, runtime }); ``` `useAction(action)` returns an async function that invokes the action with `surface: "react"`. ## Dev Server API ### `createDevServer(options)` Creates the local dev console server. ```js const devServer = createDevServer({ name: "task-app", actions, runtime, }); const listener = await devServer.listen(4321, "127.0.0.1"); ``` Routes: - `GET /`: HTML dev console - `GET /api/actions`: action manifest for the dev surface - `POST /api/actions/:name/invoke`: invoke one action ## HTTP API ### `app.createHttpHandler(options)` Creates a framework-friendly HTTP JSON handler. Request body `auth` / `user` fields are ignored by default; inject trusted identity on the request object or via `resolveContext`. ```js const handle = app.createHttpHandler(); const response = await handle({ method: "POST", path: "/ageniti/actions/create_task/invoke", body: { input: { title: "Follow up with design review", priority: "high", }, }, }); ``` Routes: - `GET /ageniti/actions` - `POST /ageniti/actions/:name/invoke` ### `app.createHttpServer(options)` Creates a small Node HTTP server around the same handler. ```js const server = app.createHttpServer(); const listener = await server.listen(4322); ``` HTTP actions must support the `http` surface. Private, local, and destructive actions are filtered out by default. Server-only options: - `maxBodyBytes` - `requireJsonContentType` ## Build API ```js import { buildArtifacts, packageArtifacts, publishArtifacts } from "@ageniti/core/build"; import { createGuideDoc, exportDocs } from "@ageniti/core/docs"; ``` The `app.build()`, `app.package()`, `app.publish()`, `app.createGuideDoc()`, and `app.exportDocs()` methods on the result of `createAgenitiApp()` continue to work unchanged. ### `app.build(options)` Builds official Ageniti distribution artifacts from an app object. ```js await app.build({ targets: ["bundle"], appModule: "./src/ageniti/app.js", appExport: "app", outDir: "./dist/ageniti", }); ``` Common options: - `targets`: `manifest`, `cli`, `mcp`, `docs`, or `bundle` - `outDir`: output directory, default `dist/ageniti` - `appModule`: headless Node-safe module that exports your app - `appExport`: export name, default `app` - `includePackageJson`: force a generated `package.json` - `cwd`: working directory used for path resolution - `filename`: custom file name for `docs`, default `GUIDE.md` `bundle` expands to: - `ageniti.manifest.json` - `ageniti.actions.json` - `cli.mjs` - `mcp-stdio.mjs` - `ageniti.mcp.json` - `GUIDE.md` - `package.json` - `README.md` - `ageniti.bundle.json` The generated `README.md` is part of the app distribution. It explains how to run the CLI locally, publish the generated package, install the resulting bin, connect MCP clients, and deploy HTTP through your own backend. ### `app.createGuideDoc(options)` Returns one Markdown guide string. ```js const markdown = app.createGuideDoc(); ``` It reads the app `description` / `docs`, plus each action's `description`, permissions, side effects, surfaces, `publicMetadata`, and `docs`. ### `app.exportDocs(options)` Writes a unified guide document to disk. ```js await app.exportDocs({ outDir: "./dist/ageniti", }); ``` Default output: - `GUIDE.md` The same capability is available through the CLI: ```text task-app docs task-app docs --out-dir ./dist/ageniti task-app build docs --out-dir ./dist/ageniti ``` ### `app.package(options)` Builds a bundle and then runs `npm pack` in the generated output directory. ```js const result = await app.package({ appModule: "./src/ageniti/app.js", outDir: "./dist/ageniti", }); ``` The result includes: - `outDir` - `packageDir` - `packageFile` - `build` ### `app.publish(options)` Builds a bundle, runs `npm pack`, and then runs `npm publish`. ```js const result = await app.publish({ appModule: "./src/ageniti/app.js", outDir: "./dist/ageniti", dryRun: true, access: "public", }); ``` Important defaults: - `dryRun` defaults to `true` - pass `dryRun: false` only for a real publish Deployment shapes: - CLI package: publish or distribute the generated npm package and expose its `bin`. - MCP server: run `mcp-stdio.mjs` locally or the generated `-mcp` command after install. - HTTP gateway: mount `app.createHttpHandler()` in your own backend; Ageniti does not require a hosted runtime. The returned result includes: - `ok` - `outDir` - `packageDir` - `packageFile` - `published` - `stdout` - `stderr` - `build` ### `buildArtifacts(options)` Lower-level build helper used by `app.build()`. ### `packageArtifacts(options)` Lower-level packaging helper used by `app.package()`. ```js const result = await packageArtifacts({ appName: "task-app", actions, adapters, appModule: "./src/ageniti/app.js", appExport: "app", }); ``` The result includes: - `ok` - `outDir` - `packageDir` - `packageFile` - `build` ### `publishArtifacts(options)` Lower-level publish helper used by `app.publish()`. ```js const result = await publishArtifacts({ appName: "task-app", actions, adapters, appModule: "./src/ageniti/app.js", appExport: "app", dryRun: true, }); ``` The result includes: - `ok` - `outDir` - `packageDir` - `packageFile` - `published` - `stdout` - `stderr` - `build` ## Manifest Diff ```js import { diffActionManifests } from "@ageniti/core/manifest"; ``` ### `diffActionManifests(previous, next)` Compares two action manifests or surface manifests and reports breaking changes, warnings, and informational changes. ```js const diff = diffActionManifests(previousManifest, nextManifest); ``` Breaking changes include removed actions and input/output schema changes. ```js const result = await buildArtifacts({ appName: "task-app", actions, adapters, targets: ["bundle"], appModule: "./src/ageniti/app.js", appExport: "app", }); ``` Use this helper when you want to build launchers or manifests without first constructing an app object. ## Project Tools ```js import { initProject, doctorProject, loadProjectConfig, findDefaultAppModule, detectTypeScriptRuntime, supportsTypeScriptEntrypoints, } from "@ageniti/core/project"; ``` ### `initProject(options)` Scaffolds a headless Ageniti entry for app projects, including `react`, `expo`, `next`, and host starter templates such as `host-openai`, `host-ai-sdk`, `host-mcp`, and `host-http`. ```js const result = await initProject({ cwd: process.cwd(), template: "react", }); ``` The result includes: - `ok` - `template` - `cwd` - `files` - `appModule` - `nextSteps` ### `doctorProject(options)` Inspects a project for framework type, default Ageniti entry discovery, and common launcher or build issues. ```js const result = await doctorProject({ cwd: process.cwd() }); ``` The result includes: - `ok` - `kind` - `cwd` - `defaultAppModule` - `checks` - `recommendations` ### `findDefaultAppModule(options)` Finds the default Node-safe Ageniti app entry used by zero-config build flows. ```js const result = await findDefaultAppModule({ cwd: process.cwd() }); ``` Possible outcomes include: - `found: true` with `modulePath` and reason `node-safe-default` - `found: true` with `modulePath` and reason `configured` - `found: false` with reason `typescript-only-entry` - `found: false` with reason `missing` ### `loadProjectConfig(options)` Loads `ageniti.config.json`, `ageniti.config.js`, `ageniti.config.mjs`, or `ageniti.config.cjs` from a project root. ```js const config = await loadProjectConfig({ cwd: process.cwd() }); ``` Returned fields may include: - `build.appModule` - `build.appExport` - `build.outDir` - `build.includePackageJson` - `build.typescriptRuntime` - `mcp.transport` - `mcp.env` - `package.name` - `package.version` - `package.description` - `package.private` - `package.binName` - `configPath` ### `detectTypeScriptRuntime(options)` Detects whether the current project is configured to generate TypeScript launchers. ```js const runtime = detectTypeScriptRuntime({ packageJson, config }); ``` Current built-in support: - returns `tsx` when `tsx` is configured or installed - returns `undefined` when no supported TypeScript runtime is available ### `supportsTypeScriptEntrypoints(options)` Returns whether the current project can generate launcher files from a TypeScript-only headless app entry. ```js const supported = supportsTypeScriptEntrypoints({ packageJson, config }); ``` ## Surface Adapter API ### `defineSurfaceAdapter(adapter)` Creates a custom adapter description. Required field: - `name` Defaulted fields: - `description: ""` - `capabilities: {}` - `canExpose: () => true` - `describe: (action) => action` ### Built-In Adapters - `cliAdapter` - `jsonAdapter` - `httpAdapter` - `mcpAdapter` - `reactAdapter` - `devAdapter` - `aiSdkAdapter` ### `defaultSurfaceAdapters()` Returns the built-in adapter list used by `createAgenitiApp()` and `createCli()`. ### `findAdapter(adapters, name)` Finds one adapter by name. ## Lint API ```js import { lintActions } from "@ageniti/core/lint"; ``` ### `lintActions(actions)` Runs static checks against action definitions. Current findings include: - duplicate action names - invalid action naming - weak descriptions - destructive MCP exposure - unspecified idempotency on write or destructive actions - non-object input shapes - write or destructive actions without permissions ## Schema API ### `s` Lightweight schema builder. Available builders: - `s.string()` - `s.number()` - `s.boolean()` - `s.enum(values)` - `s.array(schema)` - `s.object(shape)` - `s.literal(value)` - `s.union(schemas)` - `s.record(schema)` - `s.any()` Common modifiers: - `.describe(text)` - `.default(value)` - `.optional()` - `.nullable()` - `.meta(data)` Type-specific helpers: - string: `.min()`, `.max()`, `.pattern()`, `.url()`, `.datetime()` - number: `.min()`, `.max()`, `.int()` - object: `.passthrough()` ### `toJSONSchema(schema)` Converts an Ageniti schema into JSON Schema. ### `SchemaValidationError` Thrown by schema parsing paths when validation fails. ## Error And Result Types ### `AgenitiError` Custom runtime error with: - `code` - `issues` - `retryable` ### Runtime Result Shape Success: ```json { "ok": true, "data": {}, "artifacts": [], "logs": [], "meta": { "action": "create_task", "invocationId": "invocation-id", "surface": "cli", "durationMs": 12 } } ``` Failure: ```json { "ok": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid action input.", "issues": [], "retryable": false }, "artifacts": [], "logs": [], "meta": { "action": "create_task", "invocationId": "invocation-id", "surface": "mcp", "durationMs": 3 } } ``` ## Bulk Registration ### `defineActions(map, options?)` Register a record of actions in one call. CamelCase keys are normalized to snake_case action names. ```js import { defineActions, s } from "@ageniti/core"; export const actions = defineActions({ createTask: { description: "Create a task.", input: s.object({ title: s.string() }), run: async ({ title }) => ({ id: crypto.randomUUID(), title }), }, ping: () => ({ ok: true }), }); ``` Options: - `defaults`: shared config applied to every entry - `rename(key)`: custom name strategy ### `actionFromHandler(handler, config)` Wrap a single existing function as an action. Handlers can be 1-arg `(input)` or 2-arg `(input, ctx)` — Ageniti detects and forwards correctly. ### `actionsFromHandlers(handlers, metadata?)` Pair a record of plain functions with metadata. ```js import * as handlers from "./app/actions/tasks"; import { actionsFromHandlers, s } from "@ageniti/core"; export const actions = actionsFromHandlers(handlers, { createTask: { description: "Create a task.", input: s.object({ title: s.string() }) }, }); ``` ## Schema Interop ### `wrapSchema(foreign, options?)` Wrap any Zod-style schema (`.safeParse` / `.parse`) or Standard Schema v1 as an Ageniti `Schema`. JSON Schema is generated through best-effort introspection; pass `{ jsonSchema }` to override. `assertSchema` (used internally by `defineAction`) auto-wraps foreign schemas, so you usually never call `wrapSchema` directly. ### `s.object(shape).strict()` Reject unknown properties at validation time (companion to `.passthrough()`). ## Streaming Events ### `runtime.stream(actionOrName, input, options?)` Returns an `AsyncIterableIterator` that yields events as the action runs: ```ts type RuntimeStreamEvent = | { type: "log"; level: string; message: string; time: string; fields: Record } | { type: "artifact"; artifact: Artifact } | { type: "progress"; message?: string; percent?: number; fields?: Record; time: string } | { type: "result"; envelope: RuntimeResult }; ``` Events come from `ctx.logger.*`, `ctx.progress.report()`, and `ctx.artifacts.add()` inside `run()`. The stream always ends with one `result` event. ## Typed Client ### `createClient(options)` ```js import { createClient } from "@ageniti/core/client"; // In-process const client = createClient({ runtime }); const task = await client.create_task({ title: "Hello" }); // Remote const remote = createClient({ url: "https://api.example.com" }); ``` Special methods: - `client.$invoke(name, input, options?)` — typed invocation by string - `client.$stream(name, input, options?)` — async iterable of events - `client.$transport` — underlying transport object Notes: - Remote HTTP clients can send `metadata`, `confirm`, and `idempotencyKey` in the request body. - Remote HTTP clients cannot send trusted `user` or `auth` in the request body. Use headers / custom `fetch` and resolve identity with `resolveContext` on the server. - Transport failures and invalid remote responses also throw `AgenitiClientError`, with client-side codes such as `TRANSPORT_ERROR` or `INVALID_RESPONSE`. ### `AgenitiClientError` Thrown on failure with `code`, `issues`, `retryable`, and optional `envelope`. Transport-level failures also use this error type. ### `generateClientTypes(actions, options?)` Generate a TypeScript `.d.ts` from an action manifest: ```js import { generateClientTypes } from "@ageniti/core/client-gen"; import { writeFile } from "node:fs/promises"; await writeFile(".ageniti/client.d.ts", generateClientTypes(actions, { interfaceName: "TasksClient", })); ``` ## React Hook ### `useAction(action, { runtime })` State machine hook. Subscribes to `runtime.stream` so logs / progress / artifacts update live. Imported from `@ageniti/core/react-hooks`. State shape: `{ status, data, error, logs, artifacts, progress, invoke, cancel, reset }`. Component unmount auto-aborts; new `invoke()` calls cancel the previous one. ## Test Utilities ```js import { createTestRuntime, expectOk, expectError, expectLog, collectStream, stubAction, } from "@ageniti/core/test-utils"; const t = createTestRuntime([myAction], { services: { db: stubDb }, allow: true, // or "denied reason" or async permission checker }); const env = await t.invoke("my_action", { x: 1 }); expectOk(env); ``` ## Runtime Hooks & Redaction ```js const runtime = createRuntime({ actions, hooks: { onInvocationStart: ({ action, invocationId, input }) => metrics.timer.start(invocationId), onInvocationEnd: ({ action, invocationId, envelope }) => metrics.timer.end(invocationId, envelope.ok), }, redact: { keys: ["ssn", "credit_card"], placeholder: "[REDACTED]" }, idempotencyMaxEntries: 5000, idempotencyTtlMs: 10 * 60 * 1000, }); ``` Default redaction keys: `password`, `passwd`, `secret`, `token`, `apikey`, `api_key`, `authorization`, `cookie`, `session`, `x-api-key`, `access_token`, `refresh_token`, `private_key`, `client_secret`. Error messages and issue text additionally have known token shapes redacted (`Bearer …`, JWT, `sk-…`, `ghp_…`, `xoxb-…`, `key=value`). ## Error Codes | Code | HTTP | CLI exit | When | |---|---|---|---| | `VALIDATION_ERROR` | 400 | 2 | input failed schema | | `OUTPUT_VALIDATION_ERROR` | 502 | 2 | run returned invalid output | | `OUTPUT_SERIALIZATION_ERROR` | 502 | 2 | run returned non-JSON-safe value | | `AUTHENTICATION_ERROR` | 401 | 3 | missing credentials | | `AUTHORIZATION_ERROR` | 403 | 3 | permission denied | | `CONFIRMATION_REQUIRED` | 409 | 3 | destructive action without confirm | | `ACTION_NOT_FOUND` | 404 | 4 | unknown action | | `UNSUPPORTED_SURFACE` | 405 | 4 | action not on this surface | | `RATE_LIMITED` | 429 | 5 | upstream rate limit | | `CONCURRENCY_LIMIT` | 429 | 5 | per-action limit hit (retryable) | | `EXTERNAL_SERVICE_ERROR` | 502 | 5 | upstream failure | | `TIMEOUT` | 504 | 124 | exceeded timeoutMs | | `CANCELLED` | 499 | 130 | user/runtime aborted | | `CONFLICT` | 409 | 1 | optimistic concurrency / state conflict | | `INTERNAL_ERROR` | 500 | 1 | uncaught run failure | ### Recipes Path: /en/docs/recipes # Recipes This page collects practical integration patterns built on top of the public Ageniti API. ## CLI-First Internal Tool Use this pattern when the main interface is a terminal command for operators or scripts. ```js import { createAgenitiApp, defineAction, s } from "@ageniti/core"; const searchTasks = defineAction({ name: "search_tasks", description: "Search tasks by status.", input: s.object({ status: s.enum(["open", "blocked", "done"]), }), run(input, ctx) { return ctx.services.tasks.search(input); }, }); const app = createAgenitiApp({ name: "ops-tools", actions: [searchTasks], services: { tasks }, }); await app.createCli().main(); ``` Why this works well: - one command entry point - JSON schema for arguments - structured results for shell wrappers and tests ## Existing React App Button Use this pattern when the app already has UI handlers and you want to reuse the same business capability for external tool, automation, or agent-caller flows. ```js const { useAction } = app.createReactAdapter(); const runSearchTasks = useAction(searchTasks); async function onClick() { const result = await runSearchTasks({ status: "open" }); if (result.ok) { setTasks(result.data.items); } } ``` Recommended practice: - keep confirmation UX in the UI - keep permission context in `invokeOptions` - reuse the same action in non-UI surfaces later ## JSON Runner For Automation Use this pattern for scripts, queues, cron jobs, or test harnesses. ```js const runner = app.createJsonRunner(); const result = await runner.invoke({ action: "search_tasks", input: { status: "open" }, metadata: { source: "nightly-job" }, }); ``` This is often the simplest way to wrap Ageniti behind your own transport. Repository example: - `ageniti/examples/task-app.js` ## MCP Tools For Agent Platforms Use this when a host system already speaks MCP-style JSON-RPC. ```js const handle = app.createMcpHandler(); const list = await handle({ jsonrpc: "2.0", id: 1, method: "tools/list", }); const call = await handle({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "search_tasks", arguments: { status: "open" }, }, }); ``` If the host expects stdio instead of an in-process handler, use: ```js await app.createCli().run(["mcp", "--stdio"]); ``` Repository example: - `ageniti/examples/mcp-host.js` ## OpenAI And AI SDK Tool Adapters Use this when your host application already knows how to call tools through OpenAI-compatible schemas or AI SDK tool objects. ```js const openaiTools = app.createOpenAITools(); const responsesTools = app.createOpenAIResponsesTools(); const aiSdkTools = app.createAISDKTools({ returnEnvelope: true }); ``` Recommendations: - keep destructive actions excluded by default - add explicit filters for high-risk tools - use `returnEnvelope: true` when the caller needs logs or artifacts ## OpenAI Responses Host Use this pattern when your host app is already calling the OpenAI Responses API and only needs a stable tool set from the app SDK. ```js import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const tools = app.createOpenAIResponsesTools(); const response = await client.responses.create({ model: "gpt-4.1", input: "Find the blocked tasks and summarize the owners.", tools, }); ``` Why this matters: - the host owns model choice and prompting - Ageniti owns app capability contracts - tool schemas stay aligned with the runtime Repository example: - `ageniti/examples/openai-responses-host.js` ## Vercel AI SDK Route Handler Use this when a Next.js or server-side host already uses the AI SDK and you want executable tools backed by the shared runtime. ```js import { generateText } from "ai"; const tools = app.createAISDKTools({ returnEnvelope: true }); const result = await generateText({ model, prompt: "Create a high-priority follow-up task for the open incident.", tools, }); ``` Recommended practice: - keep app auth outside the model call and inject it through runtime options - return envelopes when the host needs logs or artifacts - keep risky tools filtered unless the host is trusted Repository example: - `ageniti/examples/ai-sdk-route.js` ## HTTP Gateway Wrapper Use this when your own backend should own transport, auth, and deployment, while Ageniti stays focused on action execution. ```js const handle = app.createHttpHandler(); const response = await handle({ method: "POST", url: "/ageniti/actions/create_task/invoke", auth: { permissions: ["task:create"], }, body: { input: { title: "Write the release review summary", priority: "high", }, }, }); ``` Recommended practice: - keep caller identity and authorization in the gateway layer - inject trusted `auth` / `user` on the request object or through `resolveContext` - pass only the app-specific permission context into Ageniti - keep the HTTP payload close to the runtime envelope for debugging Repository example: - `ageniti/examples/http-gateway.js` ## Restricting Surface Exposure Use action metadata to keep high-risk operations off external and agent-caller surfaces. ```js const deleteTask = defineAction({ name: "delete_task", description: "Delete a task by id.", sideEffects: "destructive", supportedSurfaces: ["cli", "react", "dev"], input: s.object({ taskId: s.string(), }), run(input, ctx) { return ctx.services.tasks.remove(input.taskId); }, }); ``` This keeps the action available for trusted operator paths while excluding it from MCP and LLM tool exposure. ## Manifest And Lint In CI Use this pattern to keep the contract observable during release checks. ```js const manifest = app.manifest(); const lint = app.lint(); if (!lint.ok) { process.exitCode = 1; } ``` You can snapshot the manifest in tests, or publish it as an internal contract artifact for tooling teams. ### Testing Your Actions Path: /en/docs/testing Description: Use the @ageniti/core/test-utils toolkit to test the actions you build, with any test runner. # Testing Your Actions Ageniti ships a small, dependency-free testing toolkit at `@ageniti/core/test-utils`. It works with any runner that has plain assertions — `node:test`, Vitest, or Jest — because the helpers throw ordinary `Error`s rather than depending on a specific framework. The core idea: you test an action **once**, against the shared runtime, and that behavior holds across every surface (CLI, HTTP, MCP, tool calls, React), because every surface runs through the same runtime. ## Quick start ```js import test from "node:test"; import { createTestRuntime, expectOk, expectError } from "@ageniti/core/test-utils"; import { createTask } from "./actions/create-task.js"; test("creates a task", async () => { const t = createTestRuntime([createTask], { services: { tasks: fakeTaskService() }, }); const data = expectOk(await t.invoke("create_task", { title: "Ship it" })); assert.equal(data.title, "Ship it"); }); test("rejects an empty title", async () => { const t = createTestRuntime([createTask]); expectError(await t.invoke("create_task", { title: "" }), "VALIDATION_ERROR"); }); ``` ## `createTestRuntime(actions, options?)` Spins up a runtime preconfigured for tests: - all `actions` are auto-registered - default surface is `json` — no confirmation gate, no UI assumptions - confirmation is bypassed by default (so destructive actions run without a `confirm` flag in tests) Options: | Option | Purpose | | --- | --- | | `services` | Inject dependency stubs available as `ctx.services` inside `run`. | | `allow` | Simulate permission outcomes. `{ allow: false }` denies everything; pass a function or string to control the `permissionChecker`. | | `middleware` | Provide middleware to exercise cross-cutting logic. | | `hooks` | Provide lifecycle hooks. | | `redact` | Custom redaction keys. | | `idempotencyCache` | Provide a cache to test idempotent replays. | The returned object exposes: - `runtime` — the underlying runtime, if you need direct access - `invoke(name, input?, options?)` — invoke an action, returns the result envelope - `stream(name, input?, options?)` — invoke and get the live event stream ## Assertion helpers ```js import { expectOk, expectError, expectLog, collectStream } from "@ageniti/core/test-utils"; ``` - **`expectOk(envelope)`** — asserts success and returns `envelope.data`. - **`expectError(envelope, code?)`** — asserts failure; if `code` is given, also asserts the error code (e.g. `"VALIDATION_ERROR"`, `"PERMISSION_DENIED"`). - **`expectLog(envelope, matcher)`** — asserts a log entry exists. `matcher` can be a substring, a `RegExp`, or a predicate function. - **`collectStream(stream)`** — drains an async event stream into an array so you can assert the full sequence of `log` / `progress` / `artifact` / `result` events. ## Testing streaming behavior ```js import { createTestRuntime, collectStream } from "@ageniti/core/test-utils"; test("emits progress then a result", async () => { const t = createTestRuntime([longRunningAction]); const events = await collectStream(t.stream("reindex", { full: true })); const types = events.map((e) => e.type); assert.ok(types.includes("progress")); assert.equal(types.at(-1), "result"); assert.equal(events.at(-1).envelope.ok, true); }); ``` ## Testing permissions ```js test("denies without the right permission", async () => { const t = createTestRuntime([deleteTask], { allow: false }); expectError(await t.invoke("delete_task", { taskId: "t_1" }), "PERMISSION_DENIED"); }); ``` ## Stubbing dependencies Use `stubAction(name, options)` to build a controllable fake action — handy when testing middleware or composition without wiring real implementations: ```js import { stubAction, createTestRuntime, expectOk } from "@ageniti/core/test-utils"; const stub = stubAction("charge_card", { returns: { receiptId: "r_1" } }); const t = createTestRuntime([stub]); expectOk(await t.invoke("charge_card", {})); ``` ## Why this is enough Because every surface (CLI, HTTP, MCP, OpenAI / AI SDK tool calls, React) is a thin adapter over the same runtime, a passing action test means the capability is correct **everywhere it is exposed**. You do not need to write separate tests per surface. ### Exporting Tools & Docs Path: /en/docs/exporting-tools Description: Generate MCP manifests, LLM tool-call definitions, machine-readable manifests, and human docs from one set of actions. # Exporting Tools & Docs You define an action once. From that single contract Ageniti can emit every artifact a consumer needs: MCP tool manifests, LLM tool-call definitions, machine-readable manifests for CI, and human-readable guide docs. Nothing is hand-maintained — the exports are derived from the live action definitions, so they never drift from the implementation. All examples assume an app or a list of actions: ```js import { createAgenitiApp } from "@ageniti/core"; const app = createAgenitiApp({ name: "task-app", actions, services }); ``` ## LLM tool-call definitions One action list, four target formats — import from `@ageniti/core/ai-sdk`: ```js import { createOpenAITools, createOpenAIResponsesTools, createAISDKTools, createFunctionCallingManifest, } from "@ageniti/core/ai-sdk"; const openai = createOpenAITools(actions); // OpenAI function calling const responses = createOpenAIResponsesTools(actions); // OpenAI Responses API const aiSdk = createAISDKTools(actions, { runtime }); // Vercel AI SDK tools const generic = createFunctionCallingManifest(actions);// generic tool-call manifest ``` By default, destructive actions and non-public actions are filtered out of these external tool exports — see [Safety](/en/docs/safety) for the visibility and side-effect rules. ## MCP manifest & server ```js import { createMcpManifest, createMcpHandler } from "@ageniti/core/mcp"; const manifest = createMcpManifest(actions); // { tools: [...] } const handler = createMcpHandler({ actions, runtime }); ``` From the CLI: ```text ageniti mcp # inspect the MCP tool manifest / run the server ``` ## Machine-readable manifests Use these for inspection, custom wrappers, or contract checks in CI — import from `@ageniti/core/manifest`: ```js import { createActionManifest, createSurfaceManifest, describeAction, } from "@ageniti/core/manifest"; const actionsManifest = createActionManifest(actions); const surfaceManifest = createSurfaceManifest({ appName: "task-app", actions }); ``` Or via the app and CLI: ```js const manifest = app.manifest(); // surface manifest const actionsOnly = app.actionManifest(); // action contracts only ``` ```text ageniti manifest # export the manifest (pipe into tooling / CI) ``` ## Human-readable docs Generate a unified `GUIDE.md` describing every action — name, description, side effects, visibility, permissions, supported surfaces, when-to-use notes, and input/output examples — from `@ageniti/core/docs`: ```js import { createGuideDoc, exportDocs } from "@ageniti/core/docs"; const markdown = createGuideDoc({ appName: "task-app", actions }); await exportDocs({ appName: "task-app", actions, outDir: "dist/ageniti" }); ``` Or via the app and CLI: ```js const markdown = app.createGuideDoc(); await app.exportDocs(); ``` ```text ageniti docs # print or export the unified guide ``` ## Catching contract regressions Two extras most SDKs do not give you: - **`ageniti diff`** / `diffActionManifests(prev, next)` — compares two manifests and reports breaking changes, warnings, and informational changes. Wire it into CI to fail a PR that breaks an action contract. - **`ageniti lint`** / `lintActions(actions)` — flags risky action contracts before you ship. ```text ageniti diff old.json new.json # detect breaking contract changes ageniti lint # health-check action contracts ``` ```js import { diffActionManifests } from "@ageniti/core/manifest"; import { lintActions } from "@ageniti/core/lint"; const diff = diffActionManifests(previous, next); if (!diff.ok) throw new Error("Breaking action contract change"); ``` ## Why this matters Because every export is derived from the same action definitions, your MCP tools, LLM tool calls, manifests, and docs are always consistent with what the runtime actually does. Add an action once and it shows up — correctly — in every surface and every exported artifact. ### SDK Readiness Path: /en/docs/sdk-readiness # SDK Readiness Use this page when you are evaluating whether Ageniti is mature enough as an SDK for building apps that agents can use. This is not a checklist for autonomous agent orchestration. It is a checklist for an app-facing SDK that must expose product capabilities safely and predictably to external hosts. ## What "ready enough" means For Ageniti, "ready enough" does not mean: - it owns planning loops - it ships long-term memory - it routes between models - it becomes an agent runtime It does mean: - developers can expose one real app capability without restructuring the whole app - hosts can discover and call that capability through standard surfaces - operators can review the contract before release - risky actions can be filtered, confirmed, or denied consistently - every surface returns the same execution semantics If those properties are strong, the SDK is doing its job. ## Release Bar For An Agent-App SDK These are the capabilities that should feel solid before you call the SDK broadly usable. ### 1. Stable action contract The SDK should make app capabilities explicit and reviewable. Minimum bar: - typed input schema - typed output schema - stable action names - side effect metadata - visibility controls - natural-language documentation for hosts and operators In Ageniti, this is the role of `defineAction()`, action metadata, and exported manifests. ### 2. Shared runtime semantics Every surface should execute through the same runtime rules. Minimum bar: - input validation - output validation - timeout handling - retry support - permission checks - confirmation for risky actions - structured logs, progress, and artifacts - stable success and failure envelopes Without this layer, each adapter becomes its own behavior fork. ### 3. Standard host surfaces An SDK for apps that agents use needs enough surfaces to meet hosts where they already are. Current high-value surfaces are: - CLI for operators, scripts, and CI - MCP for tool discovery - HTTP for internal gateways - OpenAI-compatible tools - OpenAI Responses tools - AI SDK executable tools - JSON runner for tests and automation The key question is not "how many surfaces exist?" but "do they all preserve the same contract?" ### 4. Safety defaults If an app SDK exposes product capabilities to external callers, safety defaults matter more than feature count. Minimum bar: - destructive actions require confirmation by default - private actions stay off public surfaces - high-risk actions can be restricted to trusted surfaces - auth hooks can reuse the host app's authorization model - public metadata stays separate from internal metadata This is part of product credibility, not only security. ### 5. Package and artifact model The SDK should generate artifacts that downstream teams can actually install, inspect, and review. Minimum bar: - CLI launcher - MCP stdio launcher - action manifest - surface manifest - deterministic guide or contract docs - bundle metadata for release review If shipping requires custom glue every time, the SDK is still too thin. ## What To Improve Next If the core runtime and surfaces already exist, the next gains usually come from adoption quality rather than more framework breadth. ### Host integration examples The SDK should show real host-facing examples, not only primitive API snippets. Highest-value examples: - OpenAI Responses API host - Vercel AI SDK route handler - MCP desktop host setup - internal HTTP gateway wrapper These examples help developers answer "how do I plug this into my stack this week?" ### Contract maintenance workflow The SDK should help teams evolve app capabilities without accidental breakage. High-value additions: - manifest diff in CI - compatibility notes on renamed or deprecated actions - examples of versioned action rollout - release review guidance for risky contract changes ### Operator-facing debugging Teams adopting an app SDK need to diagnose failures quickly. High-value additions: - clearer error taxonomy - invocation replay examples - artifact inspection guidance - examples of logging and progress conventions - doctor checks for common integration mistakes ### Better positioning docs Many SDKs get misunderstood because they are compared against agent frameworks. The docs should make these distinctions obvious: - app SDK versus agent framework - app capability versus agent skill - host integration versus orchestration - product boundary versus workflow engine When the positioning is sharp, the right users self-select faster. ## A Simple Maturity Test If a new team can do all of the following in one afternoon, the SDK is in good shape: 1. Wrap one existing product capability as an action. 2. Run it through CLI locally. 3. Expose it through MCP or tool calling. 4. Restrict one risky action from public surfaces. 5. Export a reviewable guide and manifest. 6. Understand failures without reading SDK internals. If any of those steps still feel fragile, that points to the next documentation or product gap to fix. ## Bottom Line Ageniti does not need to grow into an agent framework to become stronger. It becomes stronger by making app capability exposure: - clearer to adopt - safer by default - easier to review - easier to package - easier to integrate into real hosts That is the standard to optimize for. ### Roadmap Path: /en/docs/roadmap # Roadmap This roadmap assumes Ageniti is already positioned as an SDK for building apps that agents can use. The goal is not to add agent orchestration. The goal is to make adoption, release, and upgrade confidence much stronger. ## P0 Must Ship These are the highest-leverage gaps to close next. ### 1. End-to-end adoption path A new team should be able to go from install to a working host integration without reading SDK internals. Target outcome: - install the package - scaffold a host starter - run one action locally - connect the starter to a real host - export docs and manifests - build a distributable bundle Acceptance bar: - one documented path works in under 10 minutes - one path covers an existing app - one path covers a host-oriented starter ### 2. Full project creation flow `init` is good for adding Ageniti to an existing project. It is not yet the smoothest path for greenfield users. Target outcome: - add `ageniti create