🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@vistal/core

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vistal/core

ORM + Access Control Layer for AI Agents

Source
npmnpm
Version
0.1.2
Version published
Weekly downloads
0
-100%
Maintainers
1
Weekly downloads
 
Created
Source

@vistal/core

The authorization layer for AI agents — zero-dependency core.

npm license TypeScript

Reads an ORM schema, generates typed LLM tools, and enforces row-level security and field-level access control server-side on every query — in code, not prompts. Adapter-agnostic: works with any ORM or database through a two-method interface.

Most users should install @vistal/prisma, which wraps this package with a Prisma adapter and schema introspection. Use @vistal/core directly only if you're building a custom adapter.

Installation

npm install @vistal/core

What this package exports

ExportPurpose
VistalMain class — instantiate with an adapter, register policies, get tools
formats.anthropic / openai / geminiTool formatters — convert provider-neutral tools to provider-specific shapes
PolicyViolationError, ValidationErrorError types thrown by the policy engine
serializeResultSerializes Decimal, Date, BigInt in query results
Types: VistalAdapter, SchemaMap, ResolvedQuery, FilterNode, PolicyFn, PolicyResult, …All types needed to build a custom adapter

Building a custom adapter

An adapter is two methods: introspect() returns a SchemaMap describing your resources; execute() runs a ResolvedQuery against your database.

import type { VistalAdapter, SchemaMap, ResolvedQuery } from "@vistal/core"

class MyAdapter implements VistalAdapter {
  async introspect(): Promise<SchemaMap> {
    return {
      resources: {
        order: {
          name: "order",
          tableName: "Order",
          fields: {
            id:        { name: "id",        type: "uuid",   isId: true,  isNullable: false },
            tenant_id: { name: "tenant_id", type: "string", isId: false, isNullable: false },
            total:     { name: "total",     type: "number", isId: false, isNullable: false },
            status:    { name: "status",    type: "enum",   isId: false, isNullable: false, enumValues: ["pending", "shipped", "delivered"] },
          },
          relations: {},
        },
      },
    }
  }

  async execute(query: ResolvedQuery): Promise<unknown> {
    // query.resource  — resource name, e.g. "order"
    // query.operation — "findMany" | "findOne" | "create" | "update" | "delete" | "aggregate"
    // query.filters   — row filters AND-ed from the policy + the model's arguments
    // query.data      — write payload (create/update) with forced fields injected
    // query.include   — relation names to eager-load
    // query.sort / query.limit / query.offset
    // query.aggregations / query.groupBy
    // ... translate this into your ORM/DB call
  }
}

Then pass your adapter to Vistal:

import { Vistal } from "@vistal/core"

const vistal = new Vistal({
  adapter: new MyAdapter(),
  defaultPolicy: "deny-all",
})

Policies

Register policies per resource. Each policy is a function that receives a context object and returns what is allowed:

vistal.policy("order", (ctx) => ({
  read:   { tenant_id: ctx.tenant.id },   // row filter — AND-ed into every read
  write:  { tenant_id: ctx.tenant.id },   // force-injected on INSERT, AND-ed on UPDATE WHERE
  delete: false,                           // delete_order tool never generated
  fields:    { deny: ctx.user.role === "support" ? ["internal_notes"] : [] },
  relations: { items: true, customer: ctx.user.role === "admin" },
}))

// "*" is a wildcard fallback for resources without an explicit policy()
vistal.policy("*", (ctx) => ({
  read:   { tenant_id: ctx.tenant.id },
  write:  false,
  delete: false,
}))

read, write, and delete accept:

ValueMeaning
trueallow
falsedeny — no tool generated for this operation
{ field: value }row filter (read/delete) or force-injected field (write)

Generated tools

For each resource, vistal generates up to six tools depending on policy:

ToolOperation
query_{resource}findMany with filters, sort, pagination, relation includes
get_{resource}findOne by id
create_{resource}insert one row
update_{resource}update by id
delete_{resource}delete by id
aggregate_{resource}count / sum / avg / min / max with optional groupBy

delete: false → no delete_ tool generated. A required write field that is denied and not force-injected → create_ suppressed entirely.

Getting tools for your LLM provider

// Vercel AI SDK (requires `ai` peer dep)
const tools = await vistal.tools.vercel(ctx)
await generateText({ model, tools, maxSteps: 5, prompt })

// Anthropic
const tools = await vistal.tools.anthropic(ctx)
// tools[i].definition → pass to the API
// tools[i].execute(args) → dispatch on tool call

// OpenAI
const tools = await vistal.tools.openai(ctx)

// Gemini
const tools = await vistal.tools.gemini(ctx)

// Custom formatter
const tools = await vistal.tools.format(ctx, (t) => ({
  id: t.name,
  schema: t.parameters,
}))

Type-safe resource names

Use InferResources to derive resource names from an existing typed client (e.g. Prisma):

import { Vistal, InferResources } from "@vistal/core"
import { PrismaClient } from "@prisma/client"

const prisma = new PrismaClient()

const vistal = new Vistal<DefaultContext, InferResources<typeof prisma>>({
  adapter: myAdapter,
  defaultPolicy: "deny-all",
})

// policy() and getTools() autocomplete and type-check resource names
vistal.policy("order", ...)

Observability

new Vistal({
  adapter,
  onQuery: ({ toolName, resource, operation, durationMs, error }) => {
    logger.info({ toolName, resource, durationMs })
    if (error) logger.error({ toolName, error: error.message })
  },
})

License

MIT

Keywords

ai agent

FAQs

Package last updated on 04 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts