Skip to content

vibeflowing-inc/agentable

Repository files navigation

Agentable

Experimental npm version GitHub stars MIT License Join Discord

Put your app in any AI. Register actions where they already live, no refactoring needed.

Want to skip setup? Start from the ready-to-run examples/express-react app and test the full round-trip in minutes.

Experimental Status

Agentable is currently experimental. APIs and behavior may change between minor versions while we iterate quickly.

AI agents that can "use your app" usually force you to build and maintain a separate tool layer. Agentable flips that model: register actions directly inside your React components, right next to the state they control. The agent only needs two tools, discoverActions and callAction.

Features

  • React-native action registration with direct state and closure access.
  • Minimal agent interface: discoverActions + callAction.
  • Built-in server bridge for Express and Next.js App Router.
  • Optional user confirmation flow for destructive actions.
  • Vercel AI SDK adapter plus framework-agnostic dispatch() usage.

Install

# npm
npm install @vibeflowai/agentable-core @vibeflowai/agentable-react @vibeflowai/agentable-server zod

# pnpm
pnpm add @vibeflowai/agentable-core @vibeflowai/agentable-react @vibeflowai/agentable-server zod

How It Works

  AI Agent
     │
     │  discoverActions()        →  GET  /agentable/manifest
     │  callAction(name, params) →  GET  /agentable/pending   (frontend polls)
     │                           ←  POST /agentable/result    (frontend posts back)
     ▼
  @vibeflowai/agentable-server  ←────────────────────────────────────────────────────────────┐
  (your backend)                                                                   │
                               @vibeflowai/agentable-react                                   │
                               (AgentableProvider)  →  useRegisterAction          │
                               polls /pending            (inside your components) │
                               posts /result  ──────────────────────────────────→─┘
  1. AgentableProvider mounts in your React app and POSTs an action manifest to the server.
  2. Your backend agent calls agentable.dispatch('action.name', params) to queue an action call.
  3. The React provider polls GET /agentable/pending, executes the matching handler in your component context, then POSTs the result.
  4. dispatch() resolves with that handler result.

Quick Start

1) Create the server bridge

Express

import express from 'express'
import { createAgentable, expressHandler } from '@vibeflowai/agentable-server'

const app = express()
app.use(express.json())

export const agentable = createAgentable()
app.use('/agentable', expressHandler(agentable))

app.listen(3001)

Next.js App Router (app/agentable/[...path]/route.ts)

import { createAgentable, nextHandler } from '@vibeflowai/agentable-server'

export const agentable = createAgentable()

const handle = nextHandler(agentable)
export const GET = handle
export const POST = handle
export const OPTIONS = handle

2) Wrap your React app

import { AgentableProvider } from '@vibeflowai/agentable-react'

export default function App() {
  return (
    <AgentableProvider endpoint="http://localhost:3001/agentable">
      <YourApp />
    </AgentableProvider>
  )
}

3) Register actions inside your components

import { useRegisterAction } from '@vibeflowai/agentable-react'
import { z } from 'zod'

function Counter() {
  const [count, setCount] = useState(0)

  useRegisterAction(
    {
      name: 'counter.increment',
      description: 'Increment the counter by a given amount.',
      schema: z.object({ amount: z.number().int().min(1).default(1) }),
      handler: async ({ amount }) => {
        const newCount = count + amount
        setCount(newCount)
        return { newCount }
      },
    },
    [count]
  )

  return <div>Count: {count}</div>
}

The agent can now discover and call counter.increment.

4) Connect your LLM

Vercel AI SDK

npm install @vibeflowai/agentable-adapter-vercel-ai
import { toVercelAITools } from '@vibeflowai/agentable-adapter-vercel-ai'
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'

const tools = toVercelAITools(agentable)

const result = await streamText({
  model: openai('gpt-4o'),
  tools,
  messages,
})

Any other framework

// Inside your tool implementation
const result = await agentable.dispatch('counter.increment', { amount: 5 })
// resolves when the frontend executes the action

Confirmation Dialogs

Mark destructive or irreversible actions with requiresConfirmation: true. dispatch() blocks until the user approves or rejects.

import { useRegisterAction, ConfirmationDialog } from '@vibeflowai/agentable-react'

function DataTable() {
  const [rows, setRows] = useState(initialRows)

  useRegisterAction(
    {
      name: 'table.deleteAll',
      description: 'Delete all rows from the table.',
      schema: z.object({}),
      requiresConfirmation: true,
      handler: async () => {
        setRows([])
        return { deleted: true }
      },
    },
    []
  )

  return (
    <>
      <table>{/* ... */}</table>

      <ConfirmationDialog
        render={({ actionName, params, onApprove, onReject }) => (
          <div className="dialog">
            <p>
              Allow AI to run <strong>{actionName}</strong>?
            </p>
            {Object.keys(params).length > 0 && (
              <pre>{JSON.stringify(params, null, 2)}</pre>
            )}
            <button onClick={onApprove}>Allow</button>
            <button onClick={onReject}>Deny</button>
          </div>
        )}
      />
    </>
  )
}

If the user denies, dispatch() resolves with { status: 'rejected' }.
If approved, it resolves with { status: 'success', result: ... }.

API Reference

@vibeflowai/agentable-server

createAgentable(options?)

createAgentable({
  timeoutMs?: number // default: 30_000
})

Returns an AgentableServer with:

Method Description
handler(req) Web API request handler for framework adapters
dispatch(name, params) Queue an action and await the frontend result
getManifest() Get current action manifest, or null before frontend connect

expressHandler(agentable)

app.use('/agentable', expressHandler(agentable))

nextHandler(agentable)

const handle = nextHandler(agentable)
export const GET = handle
export const POST = handle
export const OPTIONS = handle

@vibeflowai/agentable-react

<AgentableProvider>

<AgentableProvider
  endpoint="http://localhost:3001/agentable" // required
  pollInterval={500} // optional, default 500ms
  context={{ userId, sessionId }} // optional, passed to every handler
>

useRegisterAction(opts, deps?)

useRegisterAction({
  name: string
  description: string
  schema: ZodObject
  requiresConfirmation?: boolean
  handler: (params) => Promise<unknown>
}, deps?)

Actions auto-unregister on unmount or dependency changes.

useAgentable()

const {
  registry,
  pendingConfirmation, // { callId, name, params } | null
  resolvePending, // (callId, approved: boolean) => void
  executeAction, // (callId, name, params) => Promise<void>
} = useAgentable()

Must be called inside <AgentableProvider>.

<ConfirmationDialog render={...} />

render receives:

{
  actionName: string
  params: unknown
  onApprove: () => void
  onReject: () => void
}

@vibeflowai/agentable-core

Framework-agnostic primitives used internally by @vibeflowai/agentable-react and @vibeflowai/agentable-server.

ActionRegistry

const registry = new ActionRegistry()

registry.register(opts)
registry.get(name)
registry.has(name)
registry.size
registry.getManifest()
registry.onChange(listener)

callAction(registry, opts)

const result = await callAction(registry, {
  name: string
  params: unknown
  context?: ActionContext
  requestConfirmation?: (name, params) => Promise<boolean>
})
// { status: 'success', result: unknown }
// { status: 'rejected' }
// { status: 'error', message: string }

@vibeflowai/agentable-adapter-vercel-ai

toVercelAITools(agentable)

Returns:

  • discoverActions (no params, returns manifest)
  • callAction ({ name, params }, dispatches an action call)
const { discoverActions, callAction } = toVercelAITools(agentable)

const result = await streamText({
  model: openai('gpt-4o'),
  tools: { discoverActions, callAction },
  messages,
})

Packages

Package Description
@vibeflowai/agentable-core Action registry and dispatcher
@vibeflowai/agentable-react React provider, hooks, confirmation dialog
@vibeflowai/agentable-server HTTP bridge for Express and Next.js
@vibeflowai/agentable-adapter-vercel-ai Vercel AI SDK adapter

Examples

License

MIT. See LICENSE.

About

Put your app in any AI. Register React actions where they already live, no tool-layer refactor needed.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors