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

@odla-ai/chat

v0.13.4

Published

Realtime messaging and project discussions on odla-db: project channels, post threads, bottom thread cards, scoped live updates, DMs, and AI participants over one default-deny graph.

Readme

@odla-ai/chat

Realtime messaging and project discussions built natively on odla-db's realtime engine. Chat is a schema + default-deny CEL rules + injectable clients; humans and agents use the same rule-governed graph. No server to run — it rides the existing odla-db Durable Object.

import { createChatClient } from "@odla-ai/chat";
import { init } from "@odla-ai/db/client";

const db = init({ appId, endpoint: "wss://db.odla.ai", getToken });
const chat = createChatClient(db, { selfId: myAuthId, selfEmail, displayName });

const { id } = await chat.createChannel({ slug: "general", name: "General", kind: "public" });
const channel = await chat.getChannel(id);
if (!channel) throw new Error("channel creation did not become visible");
const unsub = chat.subscribeChannel(id, (messages) => render(messages));
await chat.sendMessage(channel, { body: "hello" });

sendMessage also accepts structured refs and durable attachments. They are written in the initial message transaction, so realtime consumers and mention responders never observe a message before the content it refers to:

await chat.sendMessage(channel, {
  body: "Review this logo",
  attachments: [{
    id: brandAsset.id,
    name: "logo.png",
    contentType: "image/png",
    size: brandAsset.size,
    path: brandAsset.path,
  }],
});

Persist stable private handles, not signed download URLs. The consuming application must reauthorize and resolve a handle before presenting bytes.

Ask the runbooks first. odla's operational procedures live in a database, not in this file: npx @odla-ai/cli runbook ask "<question>" returns the current steps, and unlike anything written here it cannot be out of date. Use it before searching the web or working from memory. This README and the JSDoc in the shipped .d.ts are the version-matched API reference; a runbook is the procedure. Most tasks need an answer from both.

Model

Access is denormalized (no graph edges, to fit odla-db's single-hop ref and pre-commit rule evaluation): a channel's memberIds (json) is the auth roster for private/dm channels; messages and reactions carry a denormalized visibility + audience. chat_membership rows are the per-user index and read-state, not the source of truth for access. Message ordering + pagination use the indexed createdAt cursor.

Pieces

  • CHAT_SCHEMA — push to /app/:id/schema.
  • CHAT_RULES / chatRules({ orgDomain }) — install at /app/:id/admin/rules.
  • createChatClient(db, self) — the UI-facing client (channels, DMs, messages, feeds, threads, reactions, read-state, typing/presence).
  • chatSkill({ db, channelId, self }) — a @odla-ai/ai Skill so an agent reads and posts as a bot member.
  • botTrigger({ id, agentId, persona, mention }) — the commit-trigger config to register at /app/:id/admin/triggers; the odla-db commit hook fires it on human messages and dispatches to the chat-agent worker.
  • chatIntegration — a descriptor bundling the schema, rules, and provisioning steps.
  • createDiscussRoutes({ db, authz, ... }) — mountable project discussion routes. When the injected admin client supports changes(), GET /watch provides authorized topic/inbox events from a server-issued cursor. Topic reads return bounded forward pages; DiscussClient.getTopic() follows them to preserve its whole-conversation contract without a hidden 200-post ceiling, verifies completeness against the atomic reply count, and fails closed after bounded retries/size. getTopicPage() is the explicit bounded primitive.

Hosted app backend

initDiscuss connects a running app backend to Registry's brokered Discussion surface with the app's existing full backend key. Keep the key server-side. The client may list, read, start, and reply to topics in its own project. On odla-ai, it can reach only topics created by that exact app lifetime, while a topic read still includes later replies from odla agents and collaborators.

import { initDiscuss } from "@odla-ai/chat";

const discuss = initDiscuss({
  endpoint: process.env.ODLA_PLATFORM!,
  appId: process.env.ODLA_APP_ID!,
  env: process.env.ODLA_ENV as "dev" | "prod",
  appKey: process.env.ODLA_API_KEY!,
});

Hosted Code agents use the narrower runtimeDiscussSkill through Registry's trusted tool broker. The model and its networkless runtime receive only tool descriptors and results—never the app key or a shared-tenant credential.

Per-topic GET, PATCH, reply, and read-acknowledgement routes accept an optional app query assertion. A mismatch returns 404 before an effect. Successful reply responses include the authorized appId and topic subject, allowing a trusted embedding proxy to scope and acknowledge a write without a separate thread preflight.

Resumable project discussions

The Discuss route factory exposes one cursor stream for a topic (?topic=<id>) or the caller's owned-app inbox (?app=<id> or all owned apps). The first request to GET /watch returns an empty checkpoint. Pass its opaque cursor on the next request; returned events have stable IDs and their own cursors, so a consumer can persist progress midway through a page and receive the remainder at least once.

The route rechecks app ownership before projecting transaction metadata into messages/topics. Cursors are bound to the exact stream. A database restore, copy, or truncated checkpoint returns HTTP 409 with code: "checkpoint_required" and a replacement checkpoint—never an empty success. The optional onWatch hook reports only low-cardinality scope, outcome, counts, backlog state, and event age; it carries no app, topic, user, event, or cursor identifiers.

Project discussions and browser UI

@odla-ai/chat/discuss is a Gmail-style project conversation model: one project-backed channel contains root posts with replies under each post. The optional @odla-ai/chat/ui browser kit renders a full-width inbox and pins independently minimizable or dismissible thread cards along the bottom edge. Several pins can remain open; a controlled topicId focuses one deep-linked card without closing the others.

npm i @odla-ai/chat @odla-ai/ui preact

The browser kit version containing principal badges requires @odla-ai/ui >=0.16.0 and Preact 10.

import "@odla-ai/ui/index.css";
import "@odla-ai/chat/ui.css";
import { DiscussSuite } from "@odla-ai/chat/ui";

const { groups } = await discuss.listGroups();
const authorizedAppIds = groups.map((group) => group.appId);

<DiscussSuite client={discuss} live={db} liveScope={authorizedAppIds} />;

A host may pass initialCompose={{ appId, subject, body, refs }} to open an editable new-post draft with structured references already pinned to the body. Nothing is written until the viewer posts. When the draft is durable host route state, use onComposeDraftClose to remove it after cancellation so reload does not resurrect discarded work.

Each group includes the viewer's durable lastReadAt cursor and an exact unreadCount of open, non-archived topics whose activity is later than that cursor, matching the inbox's default Open view. The cross-project rail renders those counts and refreshes them on the same live or fallback signal as the inbox. A successful focused-thread read refreshes the cursor and badges; a speculative or failed read never clears them.

live is the scoped odla-db WebSocket/query client. Complete bounded topic and post snapshots update the inbox, unread counts, and mounted threads directly; searches, later pages, oversized windows, and incomplete frames reconcile through the authoritative routes. liveScope only narrows those queries by logical app id; it grants no access. The short-lived socket ticket carries the Registry-signed discussionProjectScopes claim for exact app incarnations, and tenant rules gate every row's projectScope against it. The suite reads the client's content-free connectionHealth() snapshot and reports Connecting, Live updates, Reconnecting, delayed/stale updates, or an offline connection without exposing query or message content. Without a client—and while a client is unhealthy—the REST-backed views refresh every LIVE_FALLBACK_MS (15 seconds) as a safety net. The inbox keeps one fallback loop and refreshes every retained thread card from that same tick, so minimized/background cards stay current without multiplying degraded-mode load.

Shared-tenant hosts must call ensureProjectLifetimeWritesOpen only after resolving current, exact Registry write authority. It creates a new active lifetime when needed and atomically upgrades an exact legacy active lifetime whose write-state fields are still absent. Read paths use requireActiveProjectLifetime (or getProjectLifetime) and never perform that upgrade. Archive flows call suspendProjectLifetimeWrites; when no lifetime exists it seeds the exact project directly as suspended, and when a legacy row exists it normalizes directly to suspended without a transient open state.

Topic and list responses include a principals map of DiscussPrincipalProfile values keyed by immutable principal id. A topic read projects both visible authors and referenced agent targets, so activity rows can name an agent before its first reply. The host's DiscussAuthz.principalProfiles resolves those registry-authoritative friendly names, handles, kinds, managed-agent purpose, and manager metadata; they override mutable chat participant labels. Purpose is passed only to that exact responder's system identity as quoted focus context. It cannot add tools, project access, or authority, and mutable chat rows never supply it. @odla-ai/chat/ui renders profiles with the shared PrincipalBadge, including the friendly manager name for an agent. A host resolveAuthor remains only a compatibility fallback, and unknown long identity ids render as Member rather than leaking an opaque account subject into the conversation.

The discussSkill responder applies that same authoritative profile projection to read_topic, including each agent's manager, and never substitutes raw principal ids when the directory is unavailable. Attached files appear as bounded name/type/size descriptors with links rebuilt from validated tenant paths through the existing authenticated /registry/discuss/attachments/* route; persisted attachment URLs are not trusted. read_attachment accepts only an attachment id first exposed by read_topic. The host then reloads the exact topic and post, rechecks current project access, and performs a bounded private-storage read—never a model-supplied path or URL. Raster images and PDFs use capability-checked multimodal tool-result blocks; UTF-8 text has a smaller context cap. The broker keeps ordinary text/control JSON under the configured input budget while separately bounding aggregate decoded media (4.5 MiB) and encoded base64 overhead. SVG, unsupported, spoofed, cross-topic, malformed, and oversized content fail closed, and all returned content carries tool_untrusted:read_attachment taint. A dispatched reply keeps the topic's threadRootId while setting replyToId to the exact source post.

Every successful hosted read_topic appends an opaque, replay-stable contextReceipt inside the existing 64 KiB tool-output cap. The host derives it from the deterministic turn mutation plus topic/source and context-output ordinal. That mutation identity is unavailable to the model before the tool result, but it is not a secret or cryptographic authority. Each later hosted context tool (read_attachment, search_collaborators, or inspect_reference) invalidates the current receipt before its handler and appends a rotated receipt to its returned success or safe error. Multimodal outputs retain their media and receive the marker as a trailing text block; a thrown context failure clears grounding and requires read_topic again.

The hosted reply_to_topic and propose_brand_change schemas require the latest receipt, and the wrapper consumes an exact match before the outcome handler. A model therefore cannot batch context production and an outcome in one assistant response; it must observe the latest result in a later inference. A failed effect requires another read. Final-prose fallback uses the same latest-context generation boundary and the reply sink's taint allowlist, so prose beside a context call or after forbidden tool output is not posted. This is only a model-turn barrier, never identity, authorization, idempotency, or a content signature; Registry still rechecks the exact turn, source, project authority, and effect digest. Receipt replay is deterministic, but dynamic broker tool payloads are still live reads rather than byte-for-byte captured replay snapshots. The discuss.reply policy therefore requires at least two model calls per run; allow at least three for read → inspect/search → outcome workflows (the default remains six).

Hosted product tools use the same discovery boundary. While rendering read_topic, the skill remembers only complete structured Brand, CRM, and Code refs that actually fit in the returned context; literal markup and truncated or omitted refs confer nothing. inspect_reference accepts only the exact { kind, id } of one of those refs—never a URL, app id, path, or row key—and returns a bounded product-owned summary after the host rechecks the running turn and product authority. A Brand palette inspection also returns at most 24 validated semantic roles with normalized #rrggbb values and optional names; the message card renders that same product-owned projection with @odla-ai/ui's PaletteStrip. An exact brand:asset inspection can return bounded private PNG, JPEG, GIF, WebP, or PDF bytes to a capable hosted model. The reader mints its own short-lived object URL, never accepts or returns a URL/path/app/storage id, and rechecks book membership, the linked asset row, content type and magic, digest, size, product authority, and running turn after I/O. Registry and chat-agent validate the closed result again, and inference admits media only from the exact paired inspect_reference tool use under the existing 4.5 MiB aggregate media budget and untrusted-tool taint. Discussion does not persist private previews; until a current-auth proxy exists, the human card truthfully opens the native Brand surface instead.

propose_brand_change is the sole product mutation: it accepts a discovered brand:book, one closed Brand facet (palette, typography, voice, logo, or imagery), and bounded payload and rationale. The host commits an open brand:proposal and the turn's one visible Discussion reply before the tool succeeds. A later reply is rejected, and approval remains an exact human action in Brand. There is no CRM/Code mutation, Brand approval, or arbitrary product-call tool in Discussion. Inspection and attachment results carry distinct tool taints. Only the bounded topic reply/delegation sink and inert Brand-proposal sink explicitly accept those two labels plus the model-inherited label that every runAgent tool call necessarily carries. That model label is safe here only because the sinks cannot escape the exact topic/project or human-review-only proposal boundary; web, operator-pasted, and proposal-result taints remain outside their allowlists and fail before a handler can write.

DiscussOperator is likewise credential-bound: the host derives its principal and credential from the request's bearer credential, so clients never choose the author. Manager metadata explains who is accountable for an agent but does not confer project authority. Each route resolves live, action-level project authority; accepted agent writes persist that exact grant's grantor, id, and version rather than credential-level grant hints.

Group rosters remain display state, never capability state. Discussion re-resolves the host's exact app incarnation and action-level authority after awaited roster/content work, immediately before each store effect, and again before releasing read or write results. A revoked or suspended managed agent therefore cannot continue through a stale channel member row. The people resolver likewise offers an agent only while its principal, exact-project write grant, and private Chat runtime admission are all live; human collaborators do not require runtime admission.

Cross-project rails should implement DiscussAuthz.revalidateProjectScopes with the host authority store's set lookup. Discussion still performs the same post-read exact-incarnation check, but one set revalidation replaces one remote authority request per project. Shared-tenant lifetime admission uses getProjectLifetimes for the same reason: project count does not multiply database round trips. Hosts that omit the optional set hook keep the conservative per-project fallback.

The @ picker opens above the bottom-edge composer and returns authorized people and named agents. Selecting an @agent candidate writes a structured agent/<principal-id> reference; selecting odla writes agent/agent_odla, while an isolated human-authored plain @odla remains compatible. Agent-authored delegation is stricter: it requires one exact structured agent target, the host resolves the sender's live write grant, and the resulting envelope is bound to the source principal, credential, message, grant revision, and depth 1. Any distinct active named agent with a live discussion.write grant for that exact project may be the target. Ordinary agent prose, self-delegation, and unbound replies cannot cascade or impersonate another principal.

Hosted responders receive search_collaborators, a read-only broker tool bound to the exact running turn and project lifetime. It returns only non-self agents whose principal, project grant, and private runtime binding are all currently live, with Registry-owned display name, handle, purpose, accountable manager, and exact paste-ready structured agent markup. A reply naming one result goes through the broker rather than a responder's direct Chat credential. Every hosted odla-pm Discussion responder is keyless: authenticated Registry state, thread, and reply operations re-admit the exact dispatch, project lifetime, source, and running turn. Registry reconstructs both principals and runtime provenance. Before a reply, delegation, or Brand-proposal effect, one atomic Registry D1 batch claims the exact service lifetime, dispatch, live authority, and request digest. Before any Chat, Brand, or provider effect, Registry asks Server to atomically bind that id and digest to the exact running job attempt, source, target, trigger, project, and service lifetime. The same claim remains replayable after the turn becomes terminal; stale attempts and conflicting ids fail closed. The exact Chat outcome is stamped with that authorization id and digest, so Registry can commit the claim or reconcile it after an ambiguous response. Reply and delegation content still commit under exact project, source, and outcome guards in one Chat transaction. An exact replay returns the same durable outcome without another effect. Stale turns, conflicting replay content, authority changes before the claim, self/multi-target handoffs, and agent-authored cascades fail closed. Brand changes remain open proposals until a human explicitly approves them. Non-identity refs in this machine-authored bridge are reduced to safe unlinked typed chips; native Brand, CRM, and Code resolution keeps its own user and product authority boundary.

Discussion references are also typed across product boundaries. Known kinds include Brand books/assets/palettes/proposals/receipts, CRM records/activities, and Code sessions/candidates/publications/deployments. Agents and the CLI write the same structured markup as every other reference, including compound native ids when the product needs them:

@[Homepage candidate](code:candidate/ccand_…)
@[Palette proposal](brand:proposal/book-id/proposal-id)
@[Lead record](crm:record/person/record-id)

The server discards caller-supplied href and meta before storage and asks a host resolver to rebuild presentation under the current project authority. odla's Registry resolver can discover Code because Code state is Registry-owned; it returns only the direct signed-in human owner's rows in the current app lifetime. A managed-agent credential cannot inherit its manager's private Code sessions. Publications open the exact native GitHub review, while candidate and deployment refs open the owning Code workspace for fresh review.

Brand and CRM are app-owned integrations, so Registry never scans their databases with infrastructure authority and never invents presentation for an opaque id. Search and exact resolution call the current production app with a 30-second, read-only assertion bound to the exact product, product capability, canonical endpoint audience, principal, app incarnation, and environment. Agent assertions also bind their manager and live grant id/version. The native route checks its product ACL and exact row existence, returning only a same-origin link plus bounded product-authored label, summary, status, and destination. Registry rechecks the assertion after product I/O, so a revoked grant or replaced app/link cannot leak the response. Ordinary agent handshakes do not include CRM access; an owner must explicitly select the read-only crm.read option. Brand Studio selects the book and proposal review; Chapter CRM selects the record and focuses an activity in its notes tab. The ref is navigation, never proof of approval. Brand proposal decisions stay on Brand's exact human receipt route, Code publication and deployment stay on Code's native owner-review routes, and CRM mutations stay behind the app's CRM authorization. Discussion exposes no generic approve or write-through tool.

The current owner/co-owner-configured HTTPS app link is a trusted outbound product destination. Request input cannot choose an origin. Provisioning must attest that link to the deployed app; Registry uses manual redirects, a short deadline, a streamed byte ceiling, JSON/item/field limits, and post-I/O authority checks. This is an explicit owner trust boundary, not a general URL fetcher or private-network discovery API.

Every exact structured managed-agent mention (and the compatible human @odla handle) invokes discussion replies—there is no feature flag or enable switch. The System AI discuss.reply policy configures the provider, model, prompt, and per-run budgets. Accepted writes visibly name the target and show queued/working activity. For a human post, DiscussAuthz.resolveAgentDispatch returns each Registry-validated exact trigger, target principal, and project lifetime. The generated source id binds that complete batch to the admin transaction. odla-db re-matches every entry against the final row and current trigger, then inserts the source, turn ledger, and durable outbox in one SQLite transaction. If a trigger is replaced or any target no longer matches, HTTP 503 leaves the source absent; a successful post cannot lose a requested turn in the readiness-to-write gap. Ordinary discussion and reads remain available during a responder outage.

odla-db retains a durable per-dispatch outbox row until chat-agent acknowledges an explicit terminal outcome. The payload-free agent-turn ledger is keyed to the exact source message and reports queued, running, retrying, succeeded, failed, or cancelled, with attempts and the next retry time. Status projection selects the newest accepted turn independently for each exact target agent, and the dock derives those current requests from thread order. An old failure therefore cannot mask newer running or successful work, while different requested agents retain independent current states. Every retained card header shows one compact count per represented lifecycle—or an explicit mixed summary—while minimized. It polls only non-terminal work through the exact-topic status route. The expanded thread renders one row per target with its authoritative friendly principal badge and newest exact source/target state. A succeeded row clears only when that exact target's reply to that exact source is visible. Background replies mark that card unread; focusing it acknowledges the topic. Realtime subscriptions drive normal refresh, while one suite-owned degraded-mode timer refreshes every retained card instead of starting a timer per card. The UI never infers failure or continued work from elapsed wall-clock time. Drains are bounded and stable effect identities make retries and one final ambiguous-response reconciliation safe. Missing credentials, unavailable model configuration, denied delegation, and exhausted or non-retryable delivery errors become durable failures. Generic AgentJobs remain separately inspectable and retryable by an operator.

Chat-agent credentials

The optional @odla/chat-agent Worker fails closed unless CHAT_DISPATCH_SECRET is configured. It does not accept a platform-wide DB admin token.

Every hosted odla-pm Discussion responder uses ODLA_PLATFORM plus the dedicated DISCUSS_AI_SECRET shared with Registry, with no direct tenant bot key. Every broker request re-admits the accepted dispatch and exact project lifetime. The bounded authenticated surface covers policy and inference; runtime state, bounded thread, and reply; principal profiles and attachment inspection; collaborator search and one-hop delegation; product reference inspection; and open-only Brand proposal brokerage. The state, thread, and reply operations are the direct Chat data operations; they are not the broker's entire surface. Registry alone holds DB_ADMIN_SECRET and the exact dispatch/project/turn authority. Do not mint or configure any ODLA_BOT_TOKENS["odla-pm"][agentId] entry for hosted Discussion, including agent_odla.

Generic per-tenant Chat responders and Brand triggers outside hosted odla-pm Discussion still use ODLA_BOT_TOKENS. Store it as a Worker secret containing a nested JSON map from exact tenant id, then exact agent principal, to a scoped app key:

{
  "support-chat--prod": {
    "agent_support": "odla_sk_..."
  },
  "brand-studio--prod": {
    "agent_brand": "odla_sk_..."
  }
}

For a full generic Chat responder, mint one key for that exact agent and the six agent-visible Chat content namespaces:

{
  "mode": "rules",
  "agentId": "agent_support",
  "namespaces": [
    "chat_channel",
    "chat_membership",
    "chat_message",
    "chat_reaction",
    "chat_participant",
    "chat_topic"
  ]
}

chat_project_lifetime is deliberately absent: it is an admin-only hosted Discussion synchronization namespace, not agent data-plane authority. Brand trigger keys use the same nested tenant/principal map, plus only the exact Brand semantic profile and namespaces documented by @odla-ai/brand; never widen them to raw Brand writes. agentId accepts only an agent_* identity, so a scoped credential cannot impersonate a human.

Set DISCUSS_AI_SECRET to the same dedicated random value on odla-chat-agent and odla-apps, and verify chat-agent /health before enabling writes. /health reports separate capabilities.hostedDiscussion and capabilities.genericAgent readiness. Dispatch authentication plus the configured hosted broker determine the HTTP status, so hosted Discussion remains 200 when the generic token map is absent. A missing or malformed-present ODLA_BOT_TOKENS map is nevertheless reported as generic-agent unready with a content-free reason; a valid nested map is ready. The private /svc/agent-admission check proves one exact runtime without returning configuration or keys. Neither route makes a remote broker call. Production verification must exercise an authenticated state read and a bounded thread read, then one canary reply when it is safe to create visible content. Rotate CHAT_DISPATCH_SECRET with odla-db and DISCUSS_AI_SECRET with odla-apps as two independent pairs. The worker has no wildcard tenant or platform-machine-token fallback.

See this installed README and the exported TypeScript declarations/JSDoc for the version-matched API. The rendered public reference is at https://odla.ai/docs/packages/chat.

License

MIT