# TypeSafe AI / JEV integration research **Checked:** 2026-09-18 **Scope:** current official TypeSafe AI/Jev documentation only. Research was gathered with Firecrawl CLI, using sequential scrapes of the official documentation index/full corpus and JavaScript SDK page (global scrape concurrency was 2). No implementation was added. ## Summary Use JEV as a small, typed intent router in front of the existing asset workflows—not as a chat model, valuation calculator, search engine, authenticator, or function executor. The recommended first call is one JavaScript SDK `Choice` question with exactly `asset_valuation`, `asset_search`, and `unsupported` options. Treat low `Choice.confidence` as an application-level abstention and route to a deterministic clarification/manual path; JEV has no native abstain answer. For an asset-valuation result, code should call the reference-data/valuation service and perform any arithmetic. For an asset-search result, code should query the asset index. For `unsupported`, code should return a fixed product response. Keep authentication, authorization, tool execution, response text, and all side effects in application code. ## Findings 1. **System One is bounded decision infrastructure, not an agent or chat model.** TypeSafe says System One evaluates a `state` and returns typed answers and probabilities; it does not write replies, generate code, explain reasoning, or choose its own next action. The intended architecture is ordinary software control flow with narrow AI decisions embedded in it. [System One](https://docs.typesafe.ai/concepts/system-one) · [How to build with System One](https://docs.typesafe.ai/concepts/how-to-build-with-system-one) Implications for this integration: - **Not generative chat:** use deterministic/canned UI responses or another separately approved component for user-facing text; do not ask JEV to compose the clarification or unsupported message. - **Not calculation:** do not ask JEV to compute a reference valuation, normalize currency, or perform exact arithmetic. Keep those operations in code. TypeSafe explicitly recommends using code for work it can compute exactly. [Advanced structure/failure modes](https://docs.typesafe.ai/primitives/advanced) - **Not authentication or authorization:** the SDK API key authenticates calls to TypeSafe; it does not authenticate the end user or authorize an asset action. Perform application identity, entitlements, and permissions before/after routing in code. - **Not execution:** the function-calling cookbook maps closed-set values to ordinary typed functions, but the functions remain application code. [Function calling cookbook](https://docs.typesafe.ai/cookbooks/function_calling) 2. **State is shared, questions are independent.** A request evaluates one `state` against one or more questions. State may be a string or structured JSON object/array containing the text and relevant application context; JEV currently supports text input and does not support image, audio, or video. Every question sees the same state and one question's answer does not become context for another. [State](https://docs.typesafe.ai/concepts/state) · [API request body](https://docs.typesafe.ai/api#request-body) For the router, send only the user request plus any text/JSON metadata required to disambiguate it, for example `{ message, asset_context }`. Do not send unrelated records. Current docs warn that context rot lowers accuracy and recommend decomposing both state and questions. 3. **The primitives have distinct, typed semantics.** - **Choice:** selects exactly one member of a fixed criteria map and returns `choice`, all option `probabilities`, and `confidence`. It is the correct primitive for the three-way router. Include an explicit `unsupported` option rather than forcing every request into a supported route; TypeSafe recommends an `other`/`none of the above` option when a list may not cover every input. A Choice supports up to 255 options. [Choice](https://docs.typesafe.ai/primitives/choice) - **Noul:** returns `noul`, the probability that a yes/no statement is true. It has no separate `confidence`; values near 0.5 indicate an ambiguous yes/no judgment. Use it for a separate boolean such as “does the request mention a specific asset identifier?”, not as a replacement for the three-way router. [Noul](https://docs.typesafe.ai/primitives/noul) - **Score:** returns an expected numeric position over an ordered list of 2–10 descriptive levels, the level probabilities, a legend, and confidence. It is appropriate for a dimension such as evidence quality or urgency, not for exact valuation arithmetic. [Score](https://docs.typesafe.ai/primitives/score) - **Confidence:** is derived from the Choice/Score probability distribution. It is a useful uncertainty signal, not a correctness guarantee. Full probabilities should be retained for auditing and policy decisions. Noul answers do not carry `confidence`. [Confidence](https://docs.typesafe.ai/confidence) 4. **Exact recommended router labels and boundaries.** Use one `Choice` whose criteria are deliberately contrastive: - `asset_valuation`: the user asks to estimate, compare, look up, or understand the reference worth/valuation of an identified asset or asset candidate. The downstream valuation implementation owns data freshness, methodology, and arithmetic. - `asset_search`: the user asks to find, browse, list, filter, or retrieve assets or reference records; valuation is not the primary requested operation. - `unsupported`: the request is neither supported asset browsing nor reference valuation (including unrelated chat, unsupported transactions, or unrelated domains). Avoid overlapping descriptions such as “anything involving prices” in both supported options. In particular, “find assets priced below X” is primarily `asset_search`; “what is this asset worth?” is `asset_valuation`. A low-confidence result is not a fourth JEV label: it is an application-level abstention. 5. **Confirmed JavaScript package and API.** The official JavaScript SDK docs specify Node.js 20+, package `@typesafe-ai/sdk`, ESM/CommonJS support, and TypeScript declarations. The current SDK reference exposes `VERSION` `0.6.0` and the changelog records v0.6.0 on 2026-09-15. [JavaScript SDK](https://docs.typesafe.ai/sdk/javascript) · [JavaScript changelog](https://docs.typesafe.ai/sdk/javascript/changelog) Confirmed API/type surface: - `new TypeSafeClient(config?)`; `TypeSafeClientConfig` includes `apiKey`, `baseURL`, `defaultModel`, `defaultHeaders`, `fetch`, `logger`, `logLevel`, `retry`, `timeout`, and `dangerouslyAllowBrowser`. - `client.systemOne(request, options?)` returns `APIPromise>`. - `systemOne` request fields are `state: EntryType`, `questions: Q`, and optional `model: string`. `EntryType` is a string, JSON object, JSON array, or `null`. - `choice(instructions, criteria)` returns `ChoiceQuestion`; `noul(instructions?, criteria?)` returns `NoulQuestion`; `score(instructions, criteria)` returns `ScoreQuestion`. - `SystemOneResult.answers` maps each question key to `ResultFor`; `ResultFor` preserves the criteria keys. `ChoiceResponse.choice` is typed as `keyof T & string`, with typed `probabilities`, `confidence`, and `type: "choice"`. - Environment defaults are `TYPESAFE_API_KEY`, `TYPESAFE_BASE_URL`, `TYPESAFE_DEFAULT_MODEL`, and `TYPESAFE_LOG_LEVEL`; default base URL is `https://api.typesafe.ai`, default model is `jev-latest`, default log level is `warn`, and default per-attempt timeout is 10,000 ms. [TypeSafeClient](https://docs.typesafe.ai/sdk/javascript/api/classes/TypeSafeClient) · [choice()](https://docs.typesafe.ai/sdk/javascript/api/functions/choice) · [SystemOneRequest](https://docs.typesafe.ai/sdk/javascript/api/interfaces/SystemOneRequest) · [SystemOneResult](https://docs.typesafe.ai/sdk/javascript/api/interfaces/SystemOneResult) · [ResultFor](https://docs.typesafe.ai/sdk/javascript/api/type-aliases/ResultFor) · [ChoiceResponse](https://docs.typesafe.ai/sdk/javascript/api/interfaces/ChoiceResponse) · [TypeSafeClientConfig](https://docs.typesafe.ai/sdk/javascript/api/interfaces/TypeSafeClientConfig) 6. **Exact typed-router call (SDK-confirmed).** This is the integration shape to implement later; the snippet uses the confirmed package and methods, not an inferred SDK name: ```ts import { choice, TypeSafeClient } from "@typesafe-ai/sdk"; const client = new TypeSafeClient({ // Pin while calibrating thresholds; jev-latest is the SDK default. defaultModel: "jev-1.13.0", }); const result = await client.systemOne({ state: { message: userMessage }, questions: { intent: choice("What is the user's primary supported intent?", { asset_valuation: "The user wants a reference valuation or wants to understand the worth of an identified asset.", asset_search: "The user wants to find, browse, list, filter, or retrieve assets or reference records; valuation is not the primary request.", unsupported: "The request is outside asset browsing and reference valuation, including unrelated chat or unsupported actions.", }), }, }); const answer = result.answers.intent; // answer.choice is inferred as the criteria-key union: // "asset_valuation" | "asset_search" | "unsupported". // answer.probabilities and answer.confidence are also available. ``` The current model page says `jev-latest` currently resolves to `jev-1.13.0`. Use the pinned ID while calibrating the abstention policy; use the alias only when accepting model movement is intentional. The response `model` field reports the version that actually answered. [Models](https://docs.typesafe.ai/models) 7. **Recommended dispatch and abstention policy.** JEV should return a classification, after which code owns the route: - if `answer.confidence < CONFIDENCE_FLOOR`, return an application-level `abstain` result and use a deterministic clarification/manual-review flow; - otherwise, route `asset_valuation` to the reference valuation service, `asset_search` to the asset index, and `unsupported` to a fixed unsupported response. Start with a configurable `CONFIDENCE_FLOOR = 0.80` for this medium/high-consequence router, then calibrate it against labeled requests and the cost of each routing error. This is an application policy, not a TypeSafe default. The official guidance gives examples of 0.5/0.6 floors for human routing and >0.85/>0.9 for higher-risk actions, and says thresholds must match risk and be tested on the application's data. [Confidence](https://docs.typesafe.ai/confidence) · [Confidence-gated routing](https://docs.typesafe.ai/patterns/confidence-routing) · [Intent routing](https://docs.typesafe.ai/patterns/intent-routing) Recommended local result shape (not a JEV response shape): `abstain | { route: "asset_valuation" | "asset_search" | "unsupported", confidence, probabilities, model }`. Do not pretend a low-confidence `choice` is a reliable route. Preserve the raw answer, probability map, model version, usage, and request ID for evaluation. 8. **There is no native abstention field.** A Choice always selects one supplied option. Therefore: - `unsupported` is the explicit semantic escape hatch for out-of-scope input; - low confidence is the uncertainty escape hatch and must be converted to `abstain` by application code; - if a future requirement needs a separate “in-scope but missing details” class, add a fourth criteria key or use the same low-confidence abstention path; do not infer that behavior from a missing SDK field. This is preferable to using an independent Noul for each route: separate Nouls are absolute yes/no judgments and are not guaranteed to be mutually exclusive or comparable. A Choice is the relative one-of-N classifier needed here. [Choice](https://docs.typesafe.ai/primitives/choice) · [Noul](https://docs.typesafe.ai/primitives/noul) · [Confidence](https://docs.typesafe.ai/confidence) 9. **Transport and SDK failure behavior is distinct from semantic abstention.** The HTTP API documents `POST https://api.typesafe.ai/v1/systemone`, bearer authentication, and these error meanings: 401 missing/invalid key, 422 invalid request body, 429 rate limit, and 529 temporary overload. [API reference](https://docs.typesafe.ai/api) The JavaScript SDK exposes: - `AuthenticationError` (401), `BadRequestError` (400), `PermissionDeniedError` (403), `NotFoundError` (404), `RateLimitError` (429), `UnprocessableEntityError` (422), and `InternalServerError` (5xx), all carrying the parsed `body`, headers, status, and usually `requestId` through `APIError`. - `APIConnectionError` for connection/response delivery failures, `APITimeoutError` for a response that exceeds the configured timeout, and `APIUserAbortError` for an `AbortSignal` cancellation. - Default retry policy: connection errors and timeouts are retryable; HTTP statuses 408, 429, and 500–599 are retried, up to 2 retries after the initial attempt, with exponential backoff starting at 500 ms, capped at 5,000 ms, 25% jitter, and `Retry-After` honored up to 60,000 ms. Per-call `timeout`, `retry`, `headers`, and `signal` are supported via `RequestOptions`. [APIError](https://docs.typesafe.ai/sdk/javascript/api/classes/APIError) · [RateLimitError](https://docs.typesafe.ai/sdk/javascript/api/classes/RateLimitError) · [APITimeoutError](https://docs.typesafe.ai/sdk/javascript/api/classes/APITimeoutError) · [RequestOptions](https://docs.typesafe.ai/sdk/javascript/api/interfaces/RequestOptions) · [RetryPolicy](https://docs.typesafe.ai/sdk/javascript/api/interfaces/RetryPolicy) Handle 401/403/422/configuration failures as non-retryable integration failures; let the SDK handle transient 408/429/5xx/connection/timeout retries, then fail closed to an application fallback. A transport error is not an `unsupported` classification and must not be converted into one. Never expose the API key in a browser: the SDK defaults `dangerouslyAllowBrowser` to false. 10. **Fan-out is the right composition strategy for independent router features.** TypeSafe recommends sending independent questions about one state together: they are evaluated in parallel, and code may ignore answers that do not apply. If a later question genuinely depends on the first answer or requires newly fetched state, make a second request in code. [Speculative fan-out](https://docs.typesafe.ai/patterns/fan-out) · [Parallel questions cookbook](https://docs.typesafe.ai/cookbooks/parallel_questions) For the first implementation, keep the request to the single intent Choice. If later needed, add independent questions in the same request—for example a Noul for whether an asset identifier is explicitly present—but do not add an extraction/calculation question merely because it is available. The parallel-questions cookbook reports that batching 13 questions over one document avoids resending the document and, in its dated example, was 12.2× cheaper and 10.0× faster than 13 serial calls with no answer change. The exact benchmark is workload-specific, but the architectural lesson applies. 11. **Composite scoring belongs after classification and remains deterministic.** Break a broad judgment into independent Score dimensions, normalize each score to 0–1, and combine with weights in application code. Preserve each component and its probabilities rather than asking JEV for one opaque “overall valuation.” [Composite scoring](https://docs.typesafe.ai/patterns/composite-scoring) · [Score](https://docs.typesafe.ai/primitives/score) This can support a later reference-ranking policy (for example evidence quality, recency, and match quality), but it must not replace the authoritative valuation formula. Scores are judgments over descriptive levels; they are not exact financial numbers. 12. **Function-calling cookbook: closed sets only, with code in control.** The official example maps a function selector to a Choice, fixed-list arguments to Choice, `bool` flags to Noul, and set-valued `list[Literal]` arguments to one Noul per member. Optional arguments use a separate “was this argument stated?” Noul so the function default can remain in effect. The cookbook reports call confidence as the least certain judgment rather than a product of probabilities. [Function calling](https://docs.typesafe.ai/cookbooks/function_calling) This pattern is useful later for deterministic asset-search/valuation handlers, but it is not a JEV tool-execution API. Do not pass open-ended numeric calculations, authorization decisions, or arbitrary generated function arguments to JEV. Validate every route and argument in application code. 13. **Reranking is a candidate-scoring pattern, not an asset search replacement.** The official reranking cookbook first uses a fast search method (BM25) to produce a shortlist, then scores each query/candidate pair with a Noul and sorts by the returned probability. TypeSafe cannot add a candidate omitted by the fast search stage. The dated example reranked 30 candidates for each of 40 legal queries and improved top-1 from 5% to 18% and top-10 from 38% to 62%. [Reranking cookbook](https://docs.typesafe.ai/cookbooks/rerank_typesafe) If asset search eventually needs semantic ordering, keep the asset index/retrieval system first, then use one Noul per query-candidate pair (or batch independent signals per pair) and sort in code. Do not invoke JEV against the entire asset corpus. 14. **Extraction should be candidate-first and lossless.** The official pre-parsed extraction cookbook uses regex to over-find candidate emails, phone numbers, or amounts; a Choice selects one candidate verbatim; code copies and normalizes it. It adds a `none` Choice option as an escape hatch and notes the 255-option limit. It explicitly says JEV should choose among candidates, not invent an unbounded value or transpose digits. [Pre-parsed value extraction](https://docs.typesafe.ai/cookbooks/pre_parsed_value_extraction_cookbook) Apply this to asset IDs/tickers only when the candidate finder is owned by code or another approved extractor: provide the candidate strings as Choice options, include `none`, copy the selected identifier exactly, and validate it against the asset index. Do not ask JEV to generate a new identifier or exact valuation. 15. **Routing cookbook evidence supports graceful degradation.** The official classification-with-confidence cookbook classifies into a fine-grained Choice taxonomy, then reports a broader parent label when confidence is below a threshold rather than forcing a specific answer. The intent-routing pattern similarly sends low-confidence requests to a human and routes high-confidence labels to deterministic handlers or specialist systems. [Classification using confidence](https://docs.typesafe.ai/cookbooks/classification_using_confidence) · [Intent routing](https://docs.typesafe.ai/patterns/intent-routing) For this scope, the equivalent broader fallback is the local `abstain` route, which can request required asset details through a fixed form or send to manual review. There is no official evidence that JEV itself should generate the clarification text. ## Exact integration recommendation 1. Run the router server-side with `@typesafe-ai/sdk` on Node.js 20+. 2. Pin `model: "jev-1.13.0"` (or `defaultModel`) while measuring; log the returned `model`. Move to `jev-latest` only with an explicit threshold-recalibration policy. 3. Send a minimal state containing the incoming user text and only relevant text/JSON context. 4. Ask one `choice()` question with the three contrastive keys `asset_valuation`, `asset_search`, and `unsupported`. 5. Convert `confidence < 0.80` to a local typed `abstain`; validate/recalibrate this floor with a labeled evaluation set. Keep all probabilities, not only the winner. 6. Dispatch high-confidence results in ordinary code: valuation service, asset index, or fixed unsupported response. Keep user auth, authorization, exact calculations, data fetching, and side effects outside JEV. 7. Treat SDK/API failures as failures, not as semantic `unsupported`. Retry only according to the SDK policy; fail closed after retries and retain the request ID/error body for diagnosis. 8. Add fan-out questions only when independent and useful. Add extraction/reranking/composite scoring as separate, deterministic stages—not inside the initial router. ## Gaps and caveats - **No native abstain result:** the SDK/API always returns a Choice label when the request succeeds. Local `abstain` is required. - **No official asset-domain rubric or benchmark:** current docs provide generic routing examples, not accuracy for asset browsing/reference valuation. Build a labeled test set containing near-boundary requests, missing asset details, price-search wording, unsupported requests, and adversarial/ambiguous text. - **Threshold calibration is application-specific:** official docs give guidance and examples but no universal confidence threshold. Validate the proposed 0.80 floor and separately measure false valuation routing versus false search routing. - **No clarification-label requirement in the chosen scope:** the three requested labels do not include `clarification`. Low-confidence in-domain requests will abstain; if product analytics need a distinct typed `needs_details` outcome, add it deliberately rather than overloading `unsupported`. - **No JEV-generated response or valuation:** current docs do not provide a chat-generation method, calculator, finance data source, valuation methodology, or authorization system. Those are integration responsibilities. - **Current SDK documentation is precise, but cookbook code is mostly Python:** the official JavaScript API confirms the client and types, while the requested cookbooks show Python examples. Porting cookbook architecture to TypeScript will require local implementation and tests; do not infer undocumented helper classes from Python cookbook names. - **Alias movement:** `jev-latest` can point to a future release; thresholds tuned to a pinned model must be retested after any model change. ## Sources All kept sources below are official TypeSafe AI documentation and were current at the checked date. - [System One](https://docs.typesafe.ai/concepts/system-one) — model purpose and distinction from LLM agents/generative output. - [How to build with System One](https://docs.typesafe.ai/concepts/how-to-build-with-system-one) — code-controlled architecture, decomposition, fan-out, confidence gates. - [State](https://docs.typesafe.ai/concepts/state) — supported state shapes and text-only Jev input. - [Choice](https://docs.typesafe.ai/primitives/choice), [Noul](https://docs.typesafe.ai/primitives/noul), [Score](https://docs.typesafe.ai/primitives/score), [Confidence](https://docs.typesafe.ai/confidence) — primitive and uncertainty semantics. - [API reference](https://docs.typesafe.ai/api) and [Models](https://docs.typesafe.ai/models) — HTTP shape, errors, current model ID/alias. - [JavaScript SDK](https://docs.typesafe.ai/sdk/javascript), [JavaScript changelog](https://docs.typesafe.ai/sdk/javascript/changelog), [TypeSafeClient](https://docs.typesafe.ai/sdk/javascript/api/classes/TypeSafeClient), [choice()](https://docs.typesafe.ai/sdk/javascript/api/functions/choice), [SystemOneRequest](https://docs.typesafe.ai/sdk/javascript/api/interfaces/SystemOneRequest), [SystemOneResult](https://docs.typesafe.ai/sdk/javascript/api/interfaces/SystemOneResult), [ChoiceResponse](https://docs.typesafe.ai/sdk/javascript/api/interfaces/ChoiceResponse), [ResultFor](https://docs.typesafe.ai/sdk/javascript/api/type-aliases/ResultFor) — confirmed package/API/types. - [Speculative fan-out](https://docs.typesafe.ai/patterns/fan-out), [Composite scoring](https://docs.typesafe.ai/patterns/composite-scoring), [Confidence-gated routing](https://docs.typesafe.ai/patterns/confidence-routing), [Intent routing](https://docs.typesafe.ai/patterns/intent-routing) — required architecture patterns. - [Function calling](https://docs.typesafe.ai/cookbooks/function_calling), [Parallel questions](https://docs.typesafe.ai/cookbooks/parallel_questions), [Reranking](https://docs.typesafe.ai/cookbooks/rerank_typesafe), [Pre-parsed value extraction](https://docs.typesafe.ai/cookbooks/pre_parsed_value_extraction_cookbook), [Classification using confidence](https://docs.typesafe.ai/cookbooks/classification_using_confidence) — required cookbook coverage. **Dropped by scope:** third-party articles, reviews, hosted-provider documentation, social posts, and non-official examples were not used as evidence.