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

@ai-sdk-letta/server

v0.23.0

Published

Local HTTP runtime for ai-sdk-letta agents: durable threads and runs, NDJSON events, a token API and a loopback browser app.

Readme

@ai-sdk-letta/server

Local HTTP runtime for ai-sdk-letta agents. Unofficial; not affiliated with Letta or Vercel.

npm install @ai-sdk-letta/server ai-sdk-letta ai
  • ThreadRuntime: durable threads and runs over one agent, NDJSON events, exactly-once answers, cancellation, rename and archive. A run interrupted by a restart blocks its thread instead of being replayed.
  • startGuiServer(definition, assetsDir, options): the loopback browser app (session cookie, CSRF, Origin and Host checks, strict CSP).
  • startApiServer(definition, options): the same routes behind a 256-bit bearer token and owner header, for server-to-server use.
  • startTeamServer(definitions, assetsDir, { owners, origins, port }): several agents for a team behind tailscale serve. Identity from Tailscale's headers (trusted on loopback only), per-agent members and admins (TeamDirectory, <state>/team/team.json), conversations in parallel, a visible queue per conversation, and authors on every turn. Agent routes live under /api/agents/<id>/... and answer 404 to non-members; members are managed at /api/agents/<id>/members (admins only for changes); answering or stopping a turn needs its author or an admin (not_your_turn, 403). GET /v1/changes?since=N long-polls for changes made by others. ThreadRuntime takes { queue, parallel } for this.
  • Group conversations (team servers; ThreadRuntime option replyMode, the agent's setting, agentName, and members, how many people share the agent): each turn is sent with the reply mode in effect ('always', 'when-addressed', 'agent-decides'; the agent's 'auto' is always with one member, agent decides with several) and whether it mentions the agent. The agent may listen without replying (the stay_silent tool): the run then has a listened event ({ reason? }) and reasoning events when the model shares them, and no text. Threads are listed with replyMode (the override, 'inherit' by default), replyModeInEffect, members and typing ([{ id, name }]), and PATCH /v1/threads/:id accepts replyMode. Queued text messages of an agent with several members are sent together as one turn (the first run has batch, the others batchOf; each keeps its author in history); a queued message being sent can no longer be withdrawn (already_sent). POST /v1/threads/:id/typing takes exactly { typing: boolean } (a heartbeat while typing; it expires after 5 s and on send); it is kept in memory only and never reaches the agent.

Routes (under /api for the GUI): GET /v1/capabilities, GET|POST /v1/threads, PATCH /v1/threads/:id, GET /v1/threads/:id/history, GET /v1/threads/:id/view, POST /v1/runs, GET /v1/runs/:id/events?after=N (NDJSON), POST /v1/runs/:id/answer, POST /v1/runs/:id/cancel, and with the file tools POST /v1/uploads, GET /v1/threads/:id/files, GET /v1/threads/:id/files/:name.

PATCH /v1/threads/:id takes any of { title, archived, latex }: title (1–120 characters), archived (boolean), and latex, the conversation's override of the agent's ui.latex for the browser app: 'inherit' (the default, also for threads saved by earlier versions), 'on' or 'off'. Threads are listed with latex; GET /api/session (GUI) returns the agent's ui: { latex }. It returns the thread as listed.

GET /v1/resources returns the tree with threads (conversation folder path → thread ID) and archived, the paths of the folders whose conversation is archived; the browser app hides those by default. Nothing moves on disk, and version changes when a conversation is archived or restored.

POST /v1/resources/move takes { from, to } (paths from the root) and returns { path, from, commit? }. Renaming a conversation's own folder renames the conversation too (titleFromFolderName: the name as typed, also when the folder got a file-system-safe spelling of it, or the title unchanged when it already shows that text), and the answer then also has thread, as listed. A move that keeps the name does not; neither does a folder renamed by the agent in the sandbox.

GET /api/session also returns versions: { aiSdkLetta, server, lettaSdk }, read once at startup from the package.json of the packages this server actually resolves (an npm install or a source checkout alike; null when one cannot be read), and shown under "About this space" in the browser app. runtimeVersions() returns the same object.

POST /v1/runs takes { id, threadId, text, parentRunId, images? }, where images is a list of { mediaType, data } (base64, no data: prefix). Text may be empty when images are present. Images are validated against IMAGE_LIMITS and rejected with a fixed code (image_unsupported_type, image_invalid, image_remote_url: 400; image_too_large, images_too_many (400), images_too_large: 413). Only this route accepts a larger body (RUN_BODY_LIMIT_BYTES); others keep BODY_LIMIT_BYTES (24 KB) and answer payload_too_large (413) beyond it. Runtime state stores each image's type, size and SHA-256, never its bytes. An image may carry a name; with the file tools it is also saved to the conversation's folder under that name.

Files (when the definition includes fileTools). POST /v1/uploads takes one file's raw bytes (Content-Type: application/octet-stream, the URL-encoded name in X-File-Name), validates it (type by content, size, PDF text) and stages it; it returns { id, name, kind, mediaType, label, bytes, pages?, lines? }. Only this route accepts raw bodies, up to UPLOAD_BODY_LIMIT_BYTES (25 MB); at most three uploads are validated at once (uploads_busy, 429). POST /v1/runs then takes files: [id, ...] (up to 8): the staged files move into the conversation's folder and the turn carries the "Attached: ..." note. Staged uploads expire after 24 hours. GET /v1/threads/:id/files lists a conversation's files (archived threads keep them); GET /v1/threads/:id/files/:name downloads one with Content-Disposition: attachment, nosniff, Cross-Origin-Resource-Policy: same-origin and a sandboxing CSP (text is served as text/plain). Errors use fixed codes: file_unsupported_type, file_invalid, file_name_invalid (400), file_not_found, files_unavailable (404), files_too_many, conversation_files_full (409), file_too_large, payload_too_large (413).

startGuiServer serves a built browser app from the directory you pass. The assistant-ui app in this repository (apps/web) is not on npm yet; build it from a checkout.

License: Apache-2.0.