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

@rightnowai/system-zero

v0.4.5

Published

RightNow System Zero API client with typed decisions and client-side workflows.

Readme

RightNow System Zero TypeScript SDK

RightNow is the model name; System Zero is its category. The full name is RightNow System Zero.

The API has no tool calling: its request schema is strict and rejects tool-calling fields. The SDK provides "RightNow as a tool" for other models through the function-tool helpers and the MCP server adapter (the server ships in the TypeScript package).

Server-side TypeScript client for the RightNow System Zero API.

Requires Node 22 or later and ES modules (ESM).

Install with npm install @rightnowai/system-zero.

Set SYSTEM_ZERO_API_KEY in the server environment, then call the API:

import { SystemZeroClient } from '@rightnowai/system-zero';

const client = new SystemZeroClient();
const reply = await client.decide({
  model: 'system-zero',
  state: { order_total: 42 },
  questions: {
    eligible: { type: 'boolean', instructions: 'Is the order total at least 25?' },
  },
});
console.log(reply.answers);

const models = await client.models.list();
console.log(models.data);

The client calls POST /system-zero/decide and authenticated GET /models. Boolean, choice and score questions use the same request. decisionFor and decisionsFor read typed answers and per-question refusals. Responses preserve the service's wire fields.

Use withResponse() on either call to access response metadata, including request IDs, rate limits, latency and replay status. Decide options accept idempotencyKey. Both calls accept clientRequestId, extra headers and retryBudget; model listing does not need an idempotency key. Client timeouts and retry waits are milliseconds: timeout: 150000, maxRetries: 5, retryBudget: 60000 and maxRetryWait: 60000.

Media on decide

Check models.list() or the documented models endpoint for current input availability and limits before sending an attachment. SDK types and encoders do not enable a held API class. Use the public system-zero model ID; an unavailable explicit model ID returns model_not_found.

Audio is served like every other attachment kind. For any kind, a 503 with Retry-After is retried after that delay with the same Idempotency-Key; the SDK does this within its retry limit. If the delay exceeds that limit, wait for the reported delay, then retry the same request with the same key, exposed on the error.

attachments is optional; null remains valid. Each inline attachment has a unique name, a type and a canonical base64 data URL in data. Names start with an ASCII letter and contain at most 64 letters, digits, _ or -. Function tools and the MCP adapter carry the inline forms listed by their schema, including PDF and MP4. They use the same attachment kinds as the published request; check model discovery for current API availability.

  • Images: static PNG (image/png) and JPEG (image/jpeg), at most 4 images, 16,777,216 pixels each, with an aspect ratio no greater than 50:1.
  • Audio: one inline WAV (audio/wav) per request, with integer PCM or IEEE float samples at exactly 16,000 Hz, mono or multichannel. The decoded-duration limit is 60 seconds inclusive (60,000 ms), one pass per request; byte limits can be reached earlier. Audio must be the only attachment. A mixed request is refused locally with HTTP 404 audio_not_available, param attachments. Audio answers currently report calibrated: false. Read models.list() for current availability and limits. Other audio formats, sample rates and upload references are not supported by this SDK's public audio preflight.
  • All decoded inline bytes together: at most 2,621,440 bytes. The entire JSON request, including base64, is at most 3,665,920 bytes. The schema has a 16-entry attachment guard; individual kind counts can be stricter.

The helpers check supported MIME types, signatures, canonical encoding, counts, byte limits and WAV structure, sample rate and decoded duration. Preflight is explicit; complete decoding, dimensions, availability and other request checks remain with the API. Signatures alone do not prove a complete file is valid. PDF and video validation remains with the API.

GIF (image/gif), WebP (image/webp), animated PNG and images outside the decoded profile are not yet supported by this SDK version. The image preflight uses media_unpriced, media_decode_failed or media_unsupported for unsupported or mismatched inputs. Upload references are not served: a request naming one answers HTTP 400 media_unsupported at attachments[i].upload. Send bytes inline.

PDF is supported through ordinary inline attachments. Text PDFs and readable scans are read as text. Only PDF is supported by this SDK version; consult discovery for document formats. Send an unencrypted PDF of at most 500 pages within the common 2,621,440-byte decoded limit. Other document formats answer HTTP 415 document_unsupported. Put plain text in state. Use type: "document" and a canonical data:application/pdf;base64,... URL. inlineAttachment and inline_attachment can encode a PDF, or construct its ordinary inline request directly as shown below.

Video is supported through ordinary inline attachments. Send one silent H.264 MP4 video as an inline base64 data URL. Maximum duration: 10 seconds. Maximum dimensions: 1920 x 1080. Maximum decoded file size: 2,621,440 bytes (2.5 MiB). The complete JSON request must fit 3,665,920 bytes, including base64 and questions. Rotated videos, soundtracks, separate attachments and video upload references are not supported.

Use videoAttachment({ name: 'clip', data: bytes }) in TypeScript or video_attachment(name='clip', data=bytes) in Python. Video helpers encode the bytes; the API performs complete-file validation. A video_not_available response becomes VideoNotAvailableError, preserving status, code, message and request ID.

import { readFileSync } from 'node:fs';
import { SystemZeroClient, inlineAttachment, validateAttachments } from '@rightnowai/system-zero';

const client = new SystemZeroClient(); // Create once and reuse for the process lifetime.
const attachments = validateAttachments([
  inlineAttachment({ name: 'picture', type: 'image', mime: 'image/png', data: readFileSync('picture.png') }),
]);
const reply = await client.decide({
  model: 'system-zero', state: 'Use the picture.', attachments,
  questions: { ready: { type: 'boolean', instructions: 'Is the item ready?' } },
});
console.log(reply.answers);

// PDF uses the ordinary inline request form.
await client.decide({
  model: 'system-zero', state: 'Read the PDF.',
  questions: { ready: { type: 'boolean', instructions: 'Is the item ready?' } },
  attachments: [{ name: 'document', type: 'document',
    data: 'data:application/pdf;base64,' + readFileSync('document.pdf').toString('base64') }],
});

MEDIA_MIMES, MEDIA_LIMITS and attachmentMagicMatches expose the SDK encoding registry, limits and signature screen. inlineAttachment encodes one input; validateAttachments also checks aggregate counts and decoded bytes.

Ordinary HTTP retries

The defaults are five retries, 150 seconds per attempt, a 60-second total retry budget and a 60-second maximum wait. The retry count is capped at five, so one call makes at most six attempts. The recovery budget starts at the first failure and includes retry requests and waits. The first attempt keeps its normal timeout; later attempts are limited by the remaining budget. A wait above the maximum wait, or one that exhausts the remaining budget, raises the last error without waiting. A retry needs at least one millisecond remaining.

Only GET requests or requests carrying a non-empty idempotency key can retry. With retries enabled, decide POSTs receive a generated key when none is supplied. Outside the 1M class, an explicit blank key omits the header and disables decision retries. The 1M class rejects blank keys and generates an omitted key even with retries disabled. Each retry retains the same key and serialized body. Setting the retry count or budget to zero disables retries.

Ordinary HTTP calls retry these marked responses:

  • HTTP 429: capacity_exhausted, rate_limit_exceeded, concurrency_limit_exceeded or workspace_limit_exceeded.
  • HTTP 5xx except 504 and maintenance, including service_unavailable and system_zero_unavailable.
  • HTTP 409: idempotency_conflict.

Marked means error.retryable is true or the x-should-retry header is true. An explicit false in either marker vetoes retry. A cold pool answers marked 503 system_zero_unavailable with its wait in Retry-After.

A qualifying connection failure or timeout before any response byte can also retry, even if the request was sent. Failures after a response byte never retry. Errors of authentication and permission types never retry.

Retry-After-Ms takes precedence; only when it is absent does Retry-After provide seconds or an HTTP date. The wait is never below the full-jitter floor min(8 seconds, 0.5 seconds * 2^attempt) * random, with attempt zero for the first retry and random drawn from zero to one. A malformed millisecond header does not fall back to the seconds header.

Workflow recovery and call limits

runBatch and runFlow use ordinary POST /system-zero/decide calls with a stricter recovery policy. Function tools and the MCP adapter use that policy for decide and model listing. Only a marked HTTP 429 refusal with one of these code/details.cause pairs proves that no work ran and permits recovery:

  • rate_limit_exceeded with key_rate or preauth.
  • concurrency_limit_exceeded with account_concurrency.

The marker is error.retryable=true or x-should-retry: true; an explicit false in either vetoes recovery. All connection failures and timeouts, HTTP 503 (including warm-up), HTTP 409 and every other code/cause pair are terminal. The ordinary HTTP retry allowlist above does not expand this workflow policy. Retries retain one key and one logical call admission and use the client's retry count, wait and time bounds. Batch retry options can only lower the client's retry count; they cannot enable retries disabled on the client.

| Helper | Call-cap option | Default | Maximum | | --- | --- | --- | --- | | runBatch | maxDecideCalls | 1,000 | Nonnegative safe integer | | runFlow | max_decide_calls | 32 | 32 | | createToolHandler | maxDecideCalls | 100 | Nonnegative safe integer |

The flow option is spelled max_decide_calls even in TypeScript. The MCP adapter uses createToolHandler by default and inherits its 100-call cap. It permits two concurrent tool calls; extra concurrent calls receive local call_limit_reached without dispatch.

Flow and batch reports record admitted decide calls; retries do not consume additional logical admissions. Batch receipts also record actual HTTP attempts. Flows admit a step only when its bounded call fits the remaining deadline; batches also respect their deadline and the original key's replay lifetime. Evaluation compares caller-supplied labels locally and adds no network calls. Explicit call caps must be nonnegative safe integers; zero is allowed. A tool handler retains its count across calls, so reuse one handler for the tool loop. Model listing does not consume a decide count. The module-level tool helper has a zero decide-call limit. A depleted tool limit returns local call_limit_reached without dispatch; this is separate from API errors.

Function tools are exported from @rightnowai/system-zero/integrations/function-tools; the MCP adapter is exported from @rightnowai/system-zero/integrations/mcp-server. Both share the API request validation and error vocabulary. Tool catalog messages are SDK summaries; HTTP refusal messages are preserved with credential redaction.

Response metadata

SDK errors and response metadata expose the request's idempotency key as idempotencyKey (null when no key was sent). All HTTP errors expose the response's retry delay as retryAfterMs in milliseconds, or null when no valid retry delay is available.

Every HTTP attempt sends X-SDK-Retry-Count, starting at 0, and a User-Agent containing only the SDK name, SDK version and runtime version. A blank or whitespace-only clientRequestId omits the X-Client-Request-Id header.

Keep API keys on trusted servers. Don't enable DEBUG logging for the HTTP or WebSocket libraries in production; it can record request headers, including your API key. See the security policy for private vulnerability reporting.

Latency

Create one client per process and reuse it across calls. Do not create a client per call. The default HTTP transport keeps idle connections for up to 240 seconds, subject to the server closing sooner, so calls after a pause can skip connection setup. The first call after a long pause opens a new connection. An open stream skips connection setup for subsequent decisions; reuse it while sending state changes.

Function tool formats

Breaking: the three function-tool lists were renamed by their JSON shape: NESTED_FUNCTION_TOOLS (nested function objects), FLAT_FUNCTION_TOOLS (flat function objects), INPUT_SCHEMA_TOOLS (input_schema objects). Update imports; the payloads are unchanged.

The three lists describe the same functions. Choose the shape your tool runner accepts:

  • NESTED_FUNCTION_TOOLS: {"type":"function","function":{"name":"...","description":"...","parameters":{},"strict":true}}.
  • FLAT_FUNCTION_TOOLS: {"type":"function","name":"...","description":"...","parameters":{},"strict":true}.
  • INPUT_SCHEMA_TOOLS: {"name":"...","description":"...","input_schema":{}}.

Each list owns its schema objects. Pass the function name and its JSON arguments to the tool handler.

Streaming decisions

Supply SYSTEM_ZERO_API_KEY in the server environment before running this example.

The root package and @rightnowai/system-zero/stream export SystemZeroStreamClient and its frame and configuration types. The platform WebSocket supports the required headers on Node 22.23.3 and 24.21.0. No additional runtime package is needed. An older runtime must supply a compatible server-side constructor through the WebSocket option; unsupported native implementations fail before sending the key.

import { SystemZeroStreamClient } from '@rightnowai/system-zero';

const stream = new SystemZeroStreamClient();
stream.on('decision', event => console.log(event.reply, event.request_id));
stream.on('error', event => console.error(event.envelope));
try {
  await stream.connect();
  await stream.start({
    model: 'system-zero', state: 'Order received.',
    questions: { ready: { type: 'boolean', instructions: 'Is the order ready?' } },
  }, { decisionMode: 'on_request', minIntervalMs: 100 });
  const ack = await stream.appendText(' Payment received.');
  await stream.decide();
  const measured = await stream.waitForDecision(ack);
  console.log(measured.decision.reply);
} finally {
  await stream.close();
}

The client sends Authorization: Bearer <key> during the RFC 6455 version 13 handshake and selects only system-zero.stream.v1. Keep the key on a trusted server. The connection uses wss://api.rightnowai.co/v1/system-zero/stream.

Start with state and questions, then append text, append JSON items, append attachments, or replace the state. A mutation resolves when its acknowledgement arrives. In on_change mode, accepted mutations schedule decisions; in on_request mode, call decide to schedule one explicitly and read its decision event. One decision can cover several accepted mutations.

Reconnect restores the latest accepted state using stable idempotency keys; it never resends old wire frames. The default latest policy resumes the selected decision mode. The never policy restores state in on_request mode, leaving the next decision to the caller. Neither policy retries authentication or protocol failures. Idle closure reopens lazily when new work arrives. Listen for possibly_decided: a lost connection can leave accepted work whose outcome is unknown, and reopening does not prove that it was uncounted. Opening attempts are bounded by the retry count and elapsed-time options.

Limits are server-advertised in the hello frame. The SDK paces outgoing state events using max_events_per_second and the advertised event_burst (or burst) capacity; when no burst is supplied it conservatively allows one event at a time. Await each mutation to observe queue backpressure. Waiting for a token does not start its receipt timeout or block heartbeat replies. The SDK rejects messages above max_message_bytes before sending. Read hello for session lifetime, idle, heartbeat, accumulated request and decision interval limits; these can change. The SDK answers heartbeats automatically. A stream_limit_exceeded refusal supplies the effective account limit in its details. Respect retry hints.

Billing is per decision, not per connection, heartbeat, acknowledgement, or mutation. Several mutations can be covered by one decision. A decision may be counted even if the connection closes before its event arrives; use the event's usage and request identifier when reconciling completed work.

Tool selection

Tool selection: RightNow System Zero picks which of your tools to call next, or none, with a probability for each. It does not write arguments or messages, and it does not run tools.

Use toolQuestions / tool_questions and selectTool / select_tool. See Tool selection for examples and results.

Text, PDF and video fit 9 tools plus none, or 10 when required. Image or audio fits 25 tools plus none, or 26 when required. One question fits 32,768 bytes and 4,096 tokens. A single required tool is forced locally without a request. Each selection sent is an ordinary answered question on the current prices. Tool selection has not been measured. The caller runs the selected tool and sends its result back in the next request.

LongContextRequest opt-in

Select the class for the state size explicitly. In TypeScript:

await client.decide(request, { dangerZone: 'long_context_64k' });
await client.decide(request, { dangerZone: 'long_context_128k' });
await client.decide(request, { dangerZone: 'long_context_1m' });

In Python:

client.decide(request, danger_zone="long_context_64k")
client.decide(request, danger_zone="long_context_128k")
client.decide(request, danger_zone="long_context_1m")

long_context_64k covers 38,001 to 65,536 state tokens; long_context_128k covers 65,537 to 131,072. The option sends SZ-Danger-Zone with that exact value. State size never selects a class by itself. Send the text in state with no attachments. A nonempty attachment list with either class is refused locally as HTTP 404 long_context_not_available. LongContextRequest excludes attachments. For the 64k and 128k classes, at most 32 questions are allowed, with 2 to 25 choice options or 2 to 4 score levels. The API checks token bounds.

Use the exact class values above. With accept, text answers HTTP 400 invalid_request, param SZ-Danger-Zone, when more than one native class is served.

With accept, PDF answers HTTP 404 long_context_not_available; image, audio or video drops the long grants and runs as an ordinary request.

Select long_context_1m explicitly with dangerZone / danger_zone for a state above 131,072 through 1,048,576 tokens. accept and body size never select it. Use one string state and exactly one question, with 2 to 25 choice options or 2 to 4 score levels. Send no attachments, policy, mode, route, session_id or prompt_cache_key. Empty or null attachments are allowed. The rendered request is at most 1,048,575 tokens and the body at most 8,388,608 bytes. Use ordinary API-key HTTP calls; this class has no stream or sessions. Every request carries its full context. Stored answers replay for 24 hours.

The SDK sends your Idempotency-Key or generates one even when retries are disabled; a blank key is refused locally as HTTP 400 invalid_request, param Idempotency-Key. Invalid shapes are refused locally as HTTP 404 long_context_not_available; a question count other than one uses questions_too_large. Enable access per workspace in the Console: make one completed payment, then open Settings, Workspace, Context access. Before access, HTTP 404 long_context_not_available is uncharged; details.eligibility names the first unmet step and may contain new strings.

The maximum charge is held before work and settled to exact usage. Insufficient credit returns HTTP 402 insufficient_credits before work. A daily class limit returns HTTP 429 long_context_daily_budget_exhausted until the next UTC day; neither error is retried automatically. A cold pool returns HTTP 503 service_unavailable before any hold or charge; the SDK retries within its normal budget using the same key. The first call after an idle period can take several minutes to become available: the answer names the wait in Retry-After; keep retrying for up to ten minutes. One reply runs at a time per pool; a concurrent second call returns HTTP 429 capacity_exhausted with Retry-After.

No selector is sent by default. Do not supply the header through custom SDK headers. Retries, batch, flow and their resume paths retain the exact selected class.

Check models.list() and the documented models endpoint for current classes and workspace requirements. Ordinary limits.state_tokens / rendered_tokens are the direct window (38,000 / 40,960). Optional limits.long_context lists each served band with header_value, min_state_tokens, state_tokens, rendered_tokens and attachments. An ordinary over-length refusal names the direct window and a selector that covers the state, if available. A selected class with state outside its band returns non-retryable HTTP 404 long_context_not_available, param state, naming that band; it is not charged. A held or ineligible class, or missing workspace opt-in, can return HTTP 404 long_context_not_available, raised as LongContextNotAvailableError. Its eligibility preserves details.eligibility when supplied, including future rule strings. Held routes are never retried.

Replies type usage.long_input_tokens. Response metadata exposes the server's sz-danger-zone values as dangerZone in TypeScript and danger_zone in Python. LongContextLimits describes discovery limits; openapi.json describes the served contract and enabled.openapi.json contains the additional request shapes.