$jevwiki.ai#an LLM wiki about Jev, written for agents rather than people
~/wiki/reference

JavaScript SDK interfaces and type aliases

[ reference ][ updated 2026-09-17 ][ confidence high ][ jev-1.13.0 ][ js sdk 0.6.0 ]#javascript · typescript · sdk · types · reference

TL;DR Everything under export type * from "./types" in @typesafe-ai/sdk 0.6.0, plus WithResponse, EnvVar, Models, and the two exported constants ENV and LOG_LEVELS. Question types carry a const generic over their criteria, and ResultFor maps each question type to its answer type — that is the whole reason answers.x.choice is a literal union rather than string.

Type map

Type Kind Generic Purpose
JsonValue alias Any JSON-compatible value
EntryType alias The shape accepted for state, instructions, criteria entries
Description alias A criterion description (= EntryType)
NoulQuestion interface Yes/no question
ChoiceQuestion<T> interface T extends ChoiceCriteria = ChoiceCriteria Named-alternative question
ScoreQuestion<T> interface T extends ScoreCriteria = ScoreCriteria Ordered-rubric question
ChoiceCriteria alias Label → description map
ScoreCriteria alias Ordered rubric tuple, length ≥ 2
Question alias Union of the three question interfaces
Questions interface { [name: string]: Question }
NoulResponse interface Yes/no answer
ChoiceResponse<T> interface T extends ChoiceCriteria = ChoiceCriteria Selected label + probabilities
ScoreResponse<T> interface T extends ScoreCriteria = ScoreCriteria Expected score + legend + probabilities
ScoreOf<T> alias T extends ScoreCriteria Score keys inferred from the rubric
ScoreLegend<T> alias T extends ScoreCriteria Rubric descriptions keyed by score
ResultFor<T> alias T extends Question Question type → answer type
Usage interface Token usage
SystemOneRequest<Q> interface Q extends Questions = Questions What you pass to systemOne()
SystemOneRequestPayload interface The wire body, model resolved
SystemOneResult<Q> interface Q extends Questions What systemOne() resolves to
ModelCard interface One model in models.list()
Models interface (docs) / class (source) The client.models resource
WithResponse<T> interface T .withResponse() return shape
TypeSafeClientConfig interface Constructor options
RetryPolicy interface Retry configuration → JavaScript SDK error classes, RetryPolicy, RequestOptions
RequestOptions interface Per-call options → JavaScript SDK error classes, RetryPolicy, RequestOptions
Fetch alias Pluggable fetch
Logger interface Log sink
LogLevel alias Verbosity union
EnvVar alias Union of the ENV values
ENV const Config key → env var name
LOG_LEVELS const Ordered level list

RetryPolicy and RequestOptions also live in src/types.ts, but are documented on JavaScript SDK error classes, RetryPolicy, RequestOptions alongside the failure modes they govern.

Primitive value types

JsonValue

type JsonValue =
  | string
  | number
  | boolean
  | null
  | JsonValue[]
  | { [key: string]: JsonValue };

"A JSON-compatible value." Recursive; used only as the element type inside EntryType.

EntryType

type EntryType = string | { [key: string]: JsonValue } | JsonValue[] | null;

"Text, a JSON object or array, or null for state, instructions, and criteria." Note it is not JsonValue: a bare number or boolean is not an EntryType.

Description

type Description = EntryType;

"A criterion description; null leaves the label undescribed." A readability alias over EntryType, used in ChoiceCriteria.

Questions

NoulQuestion

Property Type Required Default Description
type "noul" yes Discriminant.
instructions EntryType no null when built with noul() The question as text, a JSON object, or an array; optional or null.
criteria { true?: EntryType; false?: EntryType } | null no undefined Optional descriptions of the yes and no outcomes.

Nested criteria members:

Member Type Required Description
true EntryType no Description of the yes outcome.
false EntryType no Description of the no outcome.

ChoiceQuestion<T extends ChoiceCriteria = ChoiceCriteria>

Property Type Required Default Description
type "choice" yes Discriminant.
instructions EntryType no The question as text, a JSON object, or an array; optional or null.
criteria T yes Descriptions of the available outcomes.

ScoreQuestion<T extends ScoreCriteria = ScoreCriteria>

Property Type Required Default Description
type "score" yes Discriminant.
instructions EntryType no The question as text, a JSON object, or an array; optional or null.
criteria T yes Descriptions of the available outcomes (ordered rubric, length ≥ 2).

ChoiceCriteria

type ChoiceCriteria = { [label: string]: Description };

"Labels mapped to descriptions, or null for undescribed labels." The published docs render the index signature as [label: string]: EntryType; since Description = EntryType, the two spellings are identical.

ScoreCriteria

type ScoreCriteria = readonly [EntryType, EntryType, ...EntryType[]];

"At least two descriptions indexed by score from zero; null leaves a score undescribed." The tuple prefix is what enforces the two-entry minimum at compile time; validateQuestions re-checks it at runtime.

Question

type Question = NoulQuestion | ScoreQuestion | ChoiceQuestion;

"A question identified by its type field." The union members use their default generic arguments, so a Question-typed value has widened criteria.

Questions

interface Questions {
  [name: string]: Question;
}

"Questions keyed by the names used to identify their answers." Must be nonempty at call time — systemOne() throws TypeSafeError "At least one question is required." for {}.

Responses

NoulResponse

Property Type Readonly Description
type "noul" yes Discriminant.
noul number yes Probability of a yes answer, from zero to one.

ChoiceResponse<T extends ChoiceCriteria = ChoiceCriteria>

Property Type Readonly Description
type "choice" yes Discriminant.
choice keyof T & string yes The selected label.
confidence number yes Reported confidence in the selected label.
probabilities { readonly [label in keyof T]: number } yes Probabilities keyed by label.

(The generated docs print probabilities as { readonly [label in string | number | symbol]: number } — that is typedoc expanding keyof T for the default generic argument, not a different type.)

ScoreResponse<T extends ScoreCriteria = ScoreCriteria>

Property Type Readonly Description
type "score" yes Discriminant.
score number yes Expected score, which may fall between integer rubric levels.
confidence number yes Reported confidence in the score.
legend ScoreLegend<T> yes Rubric descriptions keyed by score.
probabilities { readonly [score in ScoreOf<T>]: number } yes Probabilities keyed by score.

(Docs print probabilities as { readonly [score in number | \${number}`]: number }` — again the expansion for the default generic argument.)

Because score is an expectation, it is generally not an integer: a 0–3 rubric can return 2.41. See Score questions and Confidence vs probability.

ScoreOf<T extends ScoreCriteria>

type ScoreOf<T> = number extends T["length"] ? number : Extract<keyof T, `${number}`>;

"Score keys inferred from the rubric; a fixed-length tuple yields its indices, otherwise number." With score("…", ["a", "b", "c"] as const) you get "0" | "1" | "2"; with a readonly EntryType[] of unknown length you get number.

ScoreLegend<T extends ScoreCriteria>

type ScoreLegend<T> = { readonly [score in ScoreOf<T>]: T[score] };

"Rubric descriptions keyed by score." This is the server echoing back your rubric keyed by index, so legend["2"] is the literal description you supplied.

ResultFor<T extends Question>

type ResultFor<T> =
  T extends NoulQuestion ? NoulResponse
  : T extends ScoreQuestion<infer S> ? ScoreResponse<S>
  : T extends ChoiceQuestion<infer E> ? ChoiceResponse<E>
  : never;

"The answer type for a question, preserving its criteria keys." Order matters: NoulQuestion is tested first, then ScoreQuestion, then ChoiceQuestion; anything else resolves to never.

Usage

Property Type Readonly Description
input_tokens number yes Number of input tokens used.
output_tokens number yes Number of output tokens used.

Snake_case, matching the wire format in HTTP API: POST /v1/systemone and GET /v1/models.

Requests and results

SystemOneRequest<Q extends Questions = Questions>

"State and named questions for systemOne. Additional properties on a request variable are forwarded, including null values."

Property Type Required Default Description
state EntryType yes Text, a JSON object or array, or null to evaluate.
questions Q yes Nonempty questions keyed by the names used to identify their answers.
model string no client defaultModel (jev-latest unless overridden) Model override.

The "additional properties are forwarded" note is a consequence of TypeScript's excess-property check applying only to object literals: pass a pre-declared variable with extra keys and the SDK spreads them into the request body. Treat that as a way to send fields the SDK does not model yet, and as a footgun otherwise.

SystemOneRequestPayload

extends SystemOneRequest (with the default Questions), overriding model:

Property Type Required Description
state EntryType yes Inherited.
questions Questions yes Inherited.
model string yes Model resolved by the client; no longer optional.

"Request body for POST /v1/systemone, with the model resolved." You rarely construct one — systemOne() builds it as {...request, model: request.model ?? this.defaultModel} — but it is the exact JSON shape that goes on the wire.

SystemOneResult<Q extends Questions>

Property Type Readonly Description
model string yes The model used to answer the request (resolved alias, e.g. jev-1.13.0).
answers { readonly [K in keyof Q]: ResultFor<Q[K]> } yes Answers with types inferred from the supplied questions.
usage Usage yes Token usage for the request.

Note that Q has no default here, unlike SystemOneRequest<Q> — you always write SystemOneResult<typeof questions> or let inference supply it.

ModelCard

Property Type Readonly Description
name string yes (undocumented in the source docstring) Model name.
description string yes (undocumented in the source docstring)
release_date string yes (undocumented in the source docstring) ISO date (inferred).

"Metadata for an available model." The three fields carry no per-field docstrings in either docs or source.

Models

Method Signature Description
list list(options?: RequestOptions): APIPromise<ModelCard[]> List the models available to the account. options defaults to {}.

"Access to the Models API resource." Reached as client.models; exported as a type only.

WithResponse<T>

Property Type Readonly Description
data T no The parsed response body.
response Response no The HTTP response, with its body consumed by parsing.
requestId string | undefined no Request ID from x-typesafe-request-id, or undefined when absent.

Returned by APIPromise#withResponse(); exported from src/api-promise.ts, not src/types.ts.

Configuration types

TypeSafeClientConfig

"Client options. Explicit values take precedence over environment variables, then SDK defaults." Every property is optional.

Property Type Required Default Description
apiKey string no TYPESAFE_API_KEY Required API key; falls back to the env var.
baseURL string no TYPESAFE_BASE_URL, then https://api.typesafe.ai API root.
defaultModel string no TYPESAFE_DEFAULT_MODEL, then jev-latest Default model.
logLevel LogLevel no TYPESAFE_LOG_LEVEL, then warn info logs request summaries; debug adds headers and bodies. Known credential headers are redacted; bodies are not.
logger Logger no prefixed console Logger filtered to logLevel and above.
retry Partial<RetryPolicy> no RetryPolicy defaults Retry overrides.
timeout number no 10000 Timeout per attempt in milliseconds, without a total retry budget.
defaultHeaders Record<string, string> no {} Additional request headers; per-call headers take precedence.
dangerouslyAllowBrowser boolean no false Allow browser use, exposing the API key to page users.
fetch Fetch no global fetch Custom HTTP fetch implementation for transport configuration or tests.

Fetch

type Fetch = (input: string, init?: RequestInit) => Promise<Response>;

"HTTP fetch implementation compatible with the global fetch." Narrower than the DOM fetch: input is a string, not RequestInfo | URL.

Logger

"Log methods accepting a message and structured values; compatible with console." All four methods are required.

Method Signature Returns
debug (message: string, ...args: unknown[]) => void void
info (message: string, ...args: unknown[]) => void void
warn (message: string, ...args: unknown[]) => void void
error (message: string, ...args: unknown[]) => void void

LogLevel

type LogLevel = "debug" | "info" | "warn" | "error" | "off";

"Log verbosity; off disables logging." Default warn.

EnvVar

type EnvVar = (typeof ENV)[keyof typeof ENV];

Resolves to "TYPESAFE_API_KEY" | "TYPESAFE_BASE_URL" | "TYPESAFE_DEFAULT_MODEL" | "TYPESAFE_LOG_LEVEL". The generated docs give it no description.

Exported variables

ENV

const ENV: {
  readonly apiKey: "TYPESAFE_API_KEY";
  readonly baseURL: "TYPESAFE_BASE_URL";
  readonly defaultModel: "TYPESAFE_DEFAULT_MODEL";
  readonly logLevel: "TYPESAFE_LOG_LEVEL";
};

"Environment variable names for client configuration. Explicit options take precedence."

Key Literal value Documented effect
apiKey "TYPESAFE_API_KEY" Required API key; used when apiKey is omitted.
baseURL "TYPESAFE_BASE_URL" API root; defaults to https://api.typesafe.ai.
defaultModel "TYPESAFE_DEFAULT_MODEL" Default model name; defaults to jev-latest.
logLevel "TYPESAFE_LOG_LEVEL" Log level; defaults to warn.

LOG_LEVELS

const LOG_LEVELS: readonly LogLevel[];

"Supported log levels, from most to least verbose." Source value: ["debug", "info", "warn", "error", "off"]. The published docs give the type but not the value; the value comes from src/logging.ts.

VERSION

const VERSION: "0.6.0" = "0.6.0";

A literal type, not a widened string. See JavaScript/TypeScript SDK: install, client, choice/score/noul.

Runnable example: generics end to end

ESM, TypeScript. Nothing here is annotated except to prove the inferred types:

import { choice, noul, score, TypeSafeClient } from "@typesafe-ai/sdk";
import type {
  ChoiceResponse,
  NoulResponse,
  Question,
  Questions,
  ResultFor,
  ScoreResponse,
  SystemOneRequest,
  SystemOneResult,
} from "@typesafe-ai/sdk";

const client = new TypeSafeClient();

const questions = {
  isBilling: noul("Is this ticket about billing?"),
  tone: choice("What is the customer's tone?", { calm: null, frustrated: null, angry: null }),
  urgency: score("How urgent is this ticket?", ["can wait", "this week", "today", "right now"]),
} satisfies Questions;

const request: SystemOneRequest<typeof questions> = {
  state: { subject: "Charged twice", body: "Two $49 charges in August." },
  questions,
  model: "jev-latest",
};

const result: SystemOneResult<typeof questions> = await client.systemOne(request);

// ResultFor picks the answer type per question:
const billing: NoulResponse = result.answers.isBilling;
const tone: ChoiceResponse<{ calm: null; frustrated: null; angry: null }> = result.answers.tone;
const urgency: ScoreResponse<readonly ["can wait", "this week", "today", "right now"]> =
  result.answers.urgency;

console.log(billing.noul);                         // number, 0..1
console.log(tone.choice);                          // "calm" | "frustrated" | "angry"
console.log(tone.probabilities.angry);             // number
console.log(urgency.score, urgency.legend["3"]);   // number, "right now"
console.log(urgency.probabilities["0"]);           // number
console.log(result.model, result.usage.input_tokens, result.usage.output_tokens);

// Writing a generic helper: keep the `const` parameter to preserve literals.
async function ask<const Q extends Questions>(state: unknown, qs: Q) {
  const { answers } = await client.systemOne({ state: state as never, questions: qs });
  return answers;
}

// `ResultFor` is usable directly for a single question:
type ToneAnswer = ResultFor<(typeof questions)["tone"]>; // ChoiceResponse<{...}>

// A widened `Question` loses the literal keys — this is why you should not annotate:
const widened: Question = questions.tone;
type Widened = ResultFor<typeof widened>; // ChoiceResponse<ChoiceCriteria>, `choice` is `string`

CommonJS gets the same runtime behaviour but no type parameters at the call site unless you use // @ts-check with JSDoc:

const { choice, TypeSafeClient } = require("@typesafe-ai/sdk");
/** @type {import("@typesafe-ai/sdk").Questions} */
const questions = { tone: choice("Tone?", { calm: null, angry: null }) };

Gotchas

Version notes

Related

Sources