npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@m6d/cortex

v0.8.0

Published

Cortex for TypeScript backends: serve the server protocol and dataset hosts, fire triggers, and call the management API

Readme

@m6d/cortex

Cortex for TypeScript backends. Agents are defined in Cortex; your backend reaches them through this package:

  • defineServer serves the server protocol: the variables your agents' threads run with, the native tools your backend runs for them, end-user token verification, and the events Cortex tells you about.
  • defineDatasetHost serves the dataset protocol: tables an agent asks grouped and joined questions of, under your own access rules.
  • createTriggerClient fires an agent's triggers from your code.
  • defineManifest types a cortex.manifest.ts: agents and components kept in your repository, applied with cortex apply.

Compiled ESM for Bun and Node. hono and zod are peers; drizzle-orm is an optional peer for @m6d/cortex/drizzle.

bun add @m6d/cortex hono zod

The server protocol

Register the server on the console's Servers page with the URL you mount it at and a secret holding its key, then mount:

import { defineServer, CortexError } from "@m6d/cortex";
import { z } from "zod";

export const cortexServer = defineServer({
    // What Cortex presents as the bearer; checked before any of your code runs.
    key: process.env.CORTEX_SERVER_KEY!,
    variables: {
        // Asked once per thread; `{{userName}}` in prompts, skills and tool inputs.
        userName: {
            description: "Display name of the signed-in user",
            example: "Sara",
            type: "string",
            resolve: ({ user, token }) => lookUpName(user?.id, token),
        },
    },
    tools: {
        cancelOrder: {
            description: "Cancel one of the user's open orders.",
            input: z.object({ orderId: z.string() }),
            readOnly: false,
            // Every call parks until the end user approves it, on a card titled with
            // what the model says that call will do.
            requiresApproval: true,
            run: async ({ orderId }, { user }) => {
                if (!user)
                    throw new CortexError("forbidden", "Only a signed-in user can cancel an order");
                return cancel(orderId, user.id);
            },
        },
    },
    // For agents whose auth is `server`: whose bearer this is; null refuses it.
    verify: async (token) => {
        const session = await sessions.find(token);
        return session ? { sub: session.userId, claims: { name: session.name } } : null;
    },
    events: {
        approval_requested: (event) => notifyReviewers(event),
    },
});

app.route("/cortex", cortexServer);

Cortex reads GET / (the manifest, versioned by its own content) on every sync, and calls POST /variables, POST /tools/:name, POST /verify and POST /events/approval_requested behind it. A run-scoped call carries the run's token, which resolve and run receive as token: the end user's bearer in a chat, the trigger's credential in a trigger run. A tool's input is an object schema (any other fails at startup, since every call's input is an object), parsed before run sees it. A refusal answers as described in Errors.

A name Cortex would refuse fails defineServer at startup: a tool or variable name that is no identifier, and Cortex's built-in names (utcTime, locale, timezone, and the built-in tools such as executeCode and requestLocation), and so does an input of a tool that needs approval that names approvalTitle or closes a sub-schema to it (additionalProperties: false inside allOf, anyOf or oneOf), since Cortex adds the card's title to it. The protocol is described in full in apps/cortex/docs/cortex-integration.md.

Dataset hosts

import { defineDatasetHost } from "@m6d/cortex";
import { drizzleDataset } from "@m6d/cortex/drizzle";

const orders = drizzleDataset<User>({
    name: "orders",
    description: "One row per order: who bought what, for how much, and when.",
    table: ordersTable,
    dialect: "postgres",
    db,
    // The rule the application already has; undefined means every row.
    scoped: (user) => eq(ordersTable.customerId, user.customerId),
});

export const datasets = defineDatasetHost({
    expose: [orders],
    resolveUser: ({ token }) => verifyUser(token),
});

app.route("/datasets", datasets);

Register the host on the console's Datasets page with that URL. The host checks every query against its own manifest, as Cortex does, and runs it under the user resolveUser returns. A body past 256 KiB is refused before it is read, one nested past 32 levels as malformed, and a drizzle dataset refuses a query whose SQL would be written out past 50,000 parts before it reaches the database.

values: { status: ["pending", "shipped", "cancelled"] } lists every value a string column can hold: the model reads them, and a query comparing the column to any other value is refused. A column drizzle knows as an enum (a pgEnum, or text({ enum })) lists its own values.

A query can carry the IANA time zone of the user it runs for, and run gets it as context.timeZone. A drizzle dataset reads its timestamps on that zone's clock, at the zone's offset now. A bare date in a filter is the user's midnight. A month or weekday is the user's. A timestamp answers with the offset, as in 2024-03-31T14:00:00-10:00. A calendar date, a datetimeoffset, a timestamp read as text (mode: "string") and a date kept as text stay as stored.

Errors

Both protocols refuse in one envelope, { error: { kind, detail } }, under the kind's HTTP status:

| Kind | Status | When | | ----------------- | ------ | ------------------------------------------------------ | | invalid_request | 400 | The body, the tool input or the query does not fit | | unsupported | 400 | The query is valid, but this host cannot translate it | | forbidden | 403 | The caller may not make this call or read these rows | | timeout | 504 | The call did not finish in time | | internal | 500 | Anything else, answered without the underlying message |

Throw new CortexError(kind, detail) from a tool, a variable resolver, verify, an event handler, a dataset source or resolveUser to answer that kind with your detail, which the model reads. A timeout or an abort answers timeout, and any other error answers internal. The package answers invalid_request itself for a malformed body, an input that fails its schema, a query the manifest refuses, and an unknown dataset or tool. An unknown tool answers under 404, a body over the size limit under 413, and a wrong server key forbidden under 401.

Triggers

import { createTriggerClient, isFiringRunning } from "@m6d/cortex";

const triggers = createTriggerClient({
    url: "https://engine.cortex.example.com", // the engine's public origin, ENGINE_PUBLIC_URL
    agent: "reviewer",
    secrets: { "pr-opened": process.env.PR_TRIGGER_SECRET! },
});

const firing = await triggers.fire("pr-opened", { pull_request }, { wait: 10_000 });
if (isFiringRunning(firing)) {
    // Outlasted the wait: read it back later.
    await triggers.read("pr-opened", firing.firingId);
}

A firing parked on a tool that needs approval is resumed with triggers.answer. Until it is, a firing on the same thread (a single or keyed policy) is dropped with reason approval_pending (409). A status other than 2xx, 409 or 422, or a body that is no firing, throws TriggerClientError.

Management API

import { createManagementClient, ManagementClientError, type ManagementOutput } from "@m6d/cortex";

const cortex = createManagementClient({
    url: "https://cortex.example.com", // the console's origin
    key: process.env.CORTEX_MANAGEMENT_KEY!, // a management key, cortex_mgmt_…
    locale: "ar", // optional: the language Cortex words its refusals in
    timeZone: "Asia/Dubai", // optional: the zone a day range is read in, else UTC days
});

const agent: ManagementOutput<"agents.get"> = await cortex.agents.get({ id });
try {
    // Each setting sent is replaced whole, so the other locales go along; the
    // settings left out keep their values.
    await cortex.agents.saveSettings({
        id,
        greeting: { ...agent.greeting, en: "How can I help?" },
    });
} catch (error) {
    if (!(error instanceof ManagementClientError)) throw error;
    // error.status 403, error.code "FORBIDDEN", error.message "read_only",
    // error.detail { error: "read_only", fields: [...] }, error.refusal: Cortex's sentence, when it has one
}

Every procedure of the management API is a typed call, cortex.<router>.<procedure>(input): a query is sent as a GET, a mutation as a POST, its input and output typed from the procedures of the Cortex release this package ships with (ManagementInput<"…"> and ManagementOutput<"…"> name them). A refusal throws ManagementClientError; a Cortex that cannot be reached rejects as fetch does. A server acting for many users builds a client per request, with each caller's locale and timeZone. M6d.Cortex has the same client for .NET.

Manifests

import { defineManifest } from "@m6d/cortex";

export default defineManifest({
    id: "orders", // every row an apply creates belongs to this manifest
    agents: [
        {
            name: "support",
            displayName: { en: "Support" },
            server: "orders-backend", // registered by `cortex setup` or in the console
            prompt: "You help customers with their orders.",
            auth: { kind: "server" },
        },
    ],
    skills: [
        {
            name: "refunds",
            description: "How refunds work",
            body: "Refunds go to the original payment method within five days.",
            audience: { agents: ["support"] },
        },
    ],
    collections: [
        {
            name: "Order policies",
            // Read from beside this file by `cortex apply`.
            documents: [{ path: "./docs/returns.pdf" }],
            audience: { agents: ["support"] },
        },
    ],
});

cortex apply cortex.manifest.ts makes the deployment match: it creates what is new, updates what changed, and deletes what this manifest created and does not list. A manifest names servers, model providers, credentials and secrets, and never holds a secret value.

The server protocol, dataset hosts and the trigger client for ASP.NET Core ship in the M6d.Cortex NuGet package.