Skip to main content
GitHub

Changelog & Migration

Version history, breaking changes, and upgrade instructions.

Current SDK versions and how to upgrade between them.

Current Versions

PackageVersionLanguageInstall
risicare0.6.0Python 3.10+pip install risicare
risicare (npm)0.9.0Node.js 18+npm install risicare
risicare-core0.1.8Python 3.10+Installed automatically with the SDK

Install unpinned

Always install unpinned — pip install risicare / npm install risicare. Pinning an older version can only hold you back, and several older Python releases drop spans silently (see below). The notes below have the current releases, Python 0.6.0 and npm 0.9.0, and the releases before them, Python 0.5.1, 0.5.0 and 0.4.0 and npm 0.8.0 and 0.7.0. Notes for Python 0.2.0 to 0.3.0 and npm 0.5.0 to 0.6.0 are not written. The versions above are the published ones and are what you should install.

Do not use Python SDK 0.1.2 or earlier

Versions 0.1.2 and earlier are missing critical security fixes (auth hardening, SQL injection prevention, OOM protection). Upgrade immediately: pip install --upgrade risicare

Forked workers silently drop every span before 0.1.13

If you run under a pre-fork server — gunicorn --preload, uWSGI, or Celery prefork — Python SDK releases without the os.register_at_fork hook lose 100% of spans emitted in child workers. There is no error and no warning: the child exits 0 and the dashboard simply stays empty, which reads as "no traffic" rather than "broken telemetry".

The hook landed in 0.1.13 and first reached PyPI in 0.1.14, so every published release up to and including 0.1.12 is affected. Upgrade to the current version; do not pin below it.

Supported versions: Python 0.6.0 and npm 0.9.0

The docs describe Python risicare 0.6.0 and npm risicare 0.9.0; they do not describe older releases. Where a page says what an older release does, it names the release. Python risicare 0.1.0 to 0.4.0 ask for risicare-core with no upper version, so a new install of one of them now installs risicare-core 0.1.8, which has the proprietary licence (see the licence line of the entry for 0.5.0). To upgrade: pip install --upgrade risicare or npm install risicare@latest, then read the changes that can break a caller. A pin such as risicare~=0.5.0 or "risicare": "^0.8.0" does not take the new minor release: change it to risicare>=0.6.0 or risicare@^0.9.0.


Python 0.6.0 and npm 0.9.0 — 2026-10-07

Python risicare 0.6.0 needs risicare-core 0.1.8 or later, the same as 0.5.0 (risicare-core has no new release). Both releases change a return value or a behaviour that an existing caller can see, so the minor number changes: a pin such as risicare~=0.5.0 or risicare@^0.8.0 does not take them. The package change logs list each of these as a change that you can see: the Python change log marks them with a warning sign, and most of the JavaScript ones say "Can break an existing caller".

Changes that can break an existing caller:

  • flush() also tells the truth in a signal handler and after shutdown(). Before, it could return true with spans not delivered.
    • Python: when your own SIGTERM handler is installed before init(), the SDK drains first and then calls your handler. A flush() in that handler returned True also when the drain lost spans or the backend rejected some. A flush() after shutdown() returned True after a drop or a rejection. Both now return False, one time.
    • JavaScript: a flush() after the SDK's own drain started (your SIGTERM or SIGINT listener, registered after init()) returned true at once with the batch still in flight. It now waits for the drain, inside its own deadline, and returns what the drain delivered. A flush() after shutdown() returned true after a drop or a rejection. It now returns false, one time.
    • To see which loss it was, read get_metrics() / getMetrics(): dropped_spans_by_reason, rejected_spans, failed_scores. A flush() that is false at its deadline while none of them went up since the previous flush() means that the spans are still in flight. failed_exports / failedExports is not a loss counter: it counts export calls that failed as a whole, and a later round can still deliver those spans.
  • A shutdown that left spans makes one flush() false, not every flush(). The spans that shutdown() could not deliver count as shutdown_residue. Before, every flush() after such a shutdown returned false for ever; now the first one returns false and the next one true, as for every other loss. A span that ends after the SDK's drain, or your shutdown(), has stopped taking spans (for example in your SIGTERM handler) is counted as shutdown_residue too, with one WARNING for the first one, and one flush() returns false. Before, such a span was dropped with no count and flush() returned true. A span that ends while the drain still runs is delivered (in Python, delivered or counted). In Python, a span that you start after shutdown() has returned is not made, and a call to a traced provider in your handler after the drain makes no span; nothing counts them, so a true does not show that they were traced. (JavaScript makes the span and counts it as shutdown_residue.)
  • A score that score() refuses counts as a failed score. A value outside 0 to 1 (or not a number), an empty trace id or an empty name was logged and not sent, and flush() stayed true. It also adds 1 to failed_scores / failedScores now, so the next flush() returns false, one time. It does not count when the SDK is not active.
  • With no API key (and, in Python, no exporter of your own), nothing is sent, tracing is off, and is_enabled() / isEnabled() and flush() return False / false. RISICARE_TRACING=true, enabled=True and enable() cannot turn tracing on without a key. init() writes one WARNING that says so (none when you turned tracing off on purpose), and nothing is counted as exported or dropped. Before: Python returned True for both and sent no request (it built no exporter). JavaScript, with RISICARE_TRACING=true, created spans, queued them and dropped them after four export rounds as transport_refused, and flush() returned false once and then true. An exporter that you name yourself (otlp_endpoint, exporters=[...] in Python) still delivers without a key.
  • flush() returns False before init() and with tracing off (enabled=False, RISICARE_TRACING=false). Before, it returned True. If you call flush() on purpose with tracing off, check is_enabled() first.
  • debug with no key counts nothing. Python debug=True still prints the spans (unredacted, for local development only), but they are neither counted nor delivered: exported_spans stays 0 and flush() returns False. JavaScript debug: true prints no span (it still writes the SDK's own debug lines). Before, the printed spans counted as exported and flush() returned True.
  • is_enabled() / isEnabled() is False after the SDK's own signal drain (Python: SIGTERM; JavaScript: SIGTERM or SIGINT). Before, it stayed True over a processor that was shut down. In Python, enable() after init(enabled=False) also no longer makes is_enabled() True, because nothing was started; the JavaScript SDK behaves differently in this case, and that difference is open: do not rely on enable() after enabled was false.
  • Python: an HttpExporter with no key sends nothing and is left out. init() logs one WARNING that names the cause. A setup that worked can stop: init(api_key=KEY, exporters=[HttpExporter(endpoint=<your own relay>)]) sent spans to your relay in 0.5.1, because the exporter had no key of its own. Now init() leaves the exporter out, is_enabled() and flush() are False, and nothing is sent. Give the exporter its own key: HttpExporter(endpoint=..., api_key=...). The init() key goes only to an exporter whose endpoint is the endpoint of init().
  • A key that is only whitespace is no key, in both SDKs. The SDK trims the key (the characters that JavaScript's trim() removes, a byte order mark included). An empty or blank api_key / apiKey argument counts as "not given", so RISICARE_API_KEY applies. In JavaScript, an empty apiKey option won over the variable before. To turn tracing off, set enabled to false, not an empty key.
  • JavaScript: your SIGTERM or SIGINT listener runs once, in both orders of registration. When your listener was registered after init(), the SDK raised the signal again after its drain, your listener ran twice, and, if it was still running 100 ms later, the SDK ended the process with exit code 143. Now, when your application has a listener at the moment the signal arrives, the SDK does not raise the signal again and does not end the process: your listener decides the exit. If you relied on the exit code 143, call process.exit(143) yourself. Register the listener before init() if it makes spans that must be delivered.
  • The WARNING for "no API key" has new words (both SDKs). Python no longer prints the "Tracing active" line with no key, and an import with RISICARE_TRACING=true and no key now logs the same WARNING (before, it logged nothing). Code that matches the old text must change.

Fixed:

  • Python: get_metrics(), is_enabled() and flush() no longer wait for an internal lock. A SIGTERM handler that called one of them while the main thread was inside shutdown() waited for ever.

Documented: a new init() starts a new count: report a loss with flush() or get_metrics() before you call init() again. In Python, with more than one exporter that counts as delivery (otlp_endpoint, an exporter of your own, the Risicare exporter), the first exporter that succeeds decides a batch, so flush() returning True means that at least one of them acknowledged each span.


Python 0.5.1 — 2026-10-04

A patch release of the Python SDK (npm stays 0.8.0, risicare-core stays 0.1.8). It fixes four faults of 0.5.0:

  • Pydantic AI: Agent.run_stream() no longer raises TypeError. The call works with the SDK active, and you get the Pydantic AI result object. The agent span opens when the block starts and ends when the block ends. Its status is error when your code raises inside the block.
  • LangChain: a classic AgentExecutor run that calls a tool is one trace, and the AgentExecutor span is its root. Before, the span was lost and the run arrived as one trace for each tool call plus one. The agent_action span is a point in time: it starts and ends when the agent chooses the tool.
  • LlamaIndex: the spans of one run are one trace. Before, every span was the root of its own trace. A span with no LlamaIndex parent joins the active Risicare span, session or agent.
  • extend_span_ttl is exported from risicare (from risicare import extend_span_ttl). Before, you imported it from risicare_core.

Not fixed in 0.5.1, and still true in 0.6.0: Pydantic AI tool executions have no span, and the agent span does not record the output type; a streamed LlamaIndex LLM call still gets a provider span next to the LlamaIndex span (see LlamaIndex).


Python 0.5.0 and npm 0.8.0 — 2026-10-04

Python risicare 0.5.0 needs risicare-core 0.1.8 or later (the three packages were published on 2026-10-04). Python 0.4.1 and npm 0.7.1 were not published: their changes are in these releases.

Changes that can break an existing caller (a selection; the change logs of the packages mark each one "Can break an existing caller"):

  • flush() tells the truth (0.5.0 and 0.8.0; the signal-handler, shutdown() and no-key cases changed again in 0.6.0 and 0.9.0, see above). It returns true only when no span is in the queue or in an export, and no span was dropped, rejected or left out of an acknowledgement, and no score failed, since the previous flush() returned. A loss makes the next flush() return false one time. flush() returns at its timeout (default 5000 ms), and it only waits: it does not stop a request that is already sent. To end a process in a fixed time, call shutdown(timeout). After a shutdown() that left spans undelivered, flush() returns false. With more than one exporter, a batch counts as delivered when the first exporter takes it, so flush() can return true while another exporter refused the batch. In JavaScript, the root flush(timeoutMs) and shutdown(timeoutMs) take a timeout.
  • New counters. exported_spans / exportedSpans counts only the spans that the backend accepted. A partly rejected batch no longer counts in full. New: rejected_spans / rejectedSpans, failed_scores / failedScores, and the drop reason unacknowledged (spans that could not be serialized, or that the acknowledgement left out). An acknowledgement with 0 accepted and 0 rejected is a failure: the batch is tried again, and then dropped as transport_refused.
  • Python init(): every parameter after api_key is keyword-only. A positional call with two or more arguments raises TypeError.
  • JavaScript Span and Tracer are types only. Use import type { Span, Tracer }.
  • A traced call returns a proxy. Python: a traced stream=True call to OpenAI, Anthropic, Groq, Cerebras or Together returns a proxy of the provider's stream object, not a generator (inspect.isgenerator() is False). JavaScript: a traced call returns a Proxy of the provider's promise (OpenAI, Anthropic, Groq, Together, Cerebras, Cohere) or of the stream (Google, Mistral, Ollama), and util.types.isPromise() is false for it.
  • Provider states in get_instrumented_modules(): a patched provider is instrumented and one whose patch raised is failed (before: attempted for both). New state pending: the module is imported, but the module that holds its patch points is not.
  • Guard fixes (only with the fix runtime on, which is held during the beta): Python: a guard that fails now stops the call. Python and JavaScript: a blocked pattern that is not usable rejects the call.
  • JavaScript stored names change. An agent() with no name uses the function name (before: agent). A phase span with no name is <phase>:<function>, for example think:analyzeQuery (before: phase:think).
  • The host for scores and fix reads is chosen in this order: the api_endpoint option; the endpoint option, unless it is the default ingest host; RISICARE_API_ENDPOINT; RISICARE_ENDPOINT, when no endpoint option is given and it is not the default ingest host; https://api.risicare.ai. The default ingest host never names the scores host. For the hosted service, set no endpoint.
  • RISICARE_TRACING is trimmed and case is ignored. true, 1, yes and on turn tracing on; false, 0, no and off turn it off; an empty value is no value; any other value turns tracing off with one WARNING.
  • Python Tier 0 (import risicare with RISICARE_TRACING on) uses the defaults of init(): environment development and no service name. Before, it sent production and the service name default.
  • JavaScript error codes are the same as the Python codes on a shared set of 147 error inputs. Some JavaScript errors get a more specific code, for example ORCHESTRATION.LIFECYCLE.CRASHED for "worker process crashed" (before: TOOL.EXECUTION.CRASHED). Dashboards and alerts that use the old JavaScript codes can change.
  • Python process pools: in a pool worker, call risicare.flush() at the end of each task. A forked child no longer keeps the SIGTERM handler of the parent, so a pool no longer hangs in pool.terminate() (a very rare case remains). pool.terminate() and the end of a with Pool() block end the workers with SIGTERM, and without that call the spans that a worker made since its last export (every 1 s by default) are lost. A pool that ends with close() and join(), and a ProcessPoolExecutor, were not affected in our test (macOS, Python 3.13). The main process keeps its exit handler and its SIGTERM flush.

Fixed (selection):

  • Python: a provider library that you import after init() is traced, so the import order no longer matters. (A library that you import only with importlib.import_module is patched at its next import statement.)
  • Python: Mistral is traced (chat completions, sync and async; streaming calls are not traced).
  • Python: a traced stream=True call keeps the stream object of the provider (OpenAI, Anthropic, Groq, Cerebras, Together; for Google the response object). with ... as stream: works, and LangChain stream() works.
  • score() reports a failed request: one WARNING and the failed_scores / failedScores counter. A redirect counts as a failure. In Python, a normal exit and SIGTERM wait up to 2.5 s for score requests that are not complete.
  • Python: traced_stream() and traced_stream_sync() end the span when the stream completes, raises or is closed.
  • Python: the OTLP exporter lets one call through after the cooldown of its circuit breaker.
  • With traceContent: true, the JavaScript patchOpenAI and patchAnthropic capture the prompt and completion text (a streamed call gives the prompt only; other JavaScript providers capture no text).
  • A 401 or 403 is not sent again inside one export round: one span against such an endpoint gives 4 requests (before: 12). The SDK removes its API key from the text that it logs (a secret that you put in the path of a URL is logged as you gave it).

Known issue, Python only (in 0.4.0 and every release before it; not changed in 0.5.0, 0.5.1 or 0.6.0): an ended span can stay the current span. When a generator that holds an open span is closed in another task or thread (for example an async generator that is not read to its end), or when a span that was opened inside another one exits after it, later spans in that thread or task become children of the ended span. Read a traced stream or generator to its end inside the span that started it, or close it there.

Licence: risicare-core 0.1.8 has the proprietary licence of risicare 0.4.0 and later (see the LICENSE file in the package). risicare-core 0.1.7 and earlier were published under the MIT licence.

The full lists are in the change logs of the packages.


Python 0.4.0 and npm 0.7.0 — 2026-09-26

The two releases have the same behaviour changes. Read them before you upgrade.

Two hosts. The SDK sends spans to the ingest gateway and scores to the API:

DataDefault hostOption (Python / JavaScript)Environment variable
Spanshttps://ingest.risicare.aiendpoint / endpointRISICARE_ENDPOINT
Scores (and fix reads)https://api.risicare.aiapi_endpoint / apiEndpointRISICARE_API_ENDPOINT

During the beta, an API key can send scores to the API host. Fix reads are not reachable with an API key.

Earlier releases sent all data to https://app.risicare.ai, which is the dashboard. The /api path prefix is gone: a score goes to {api_endpoint}/v1/scores, and a fix read goes to {api_endpoint}/v1/fixes/active. If a proxy of yours removes the /api prefix, remove that rule. The SDK no longer sends the prefix.

In these releases the SDK found the scores host in this order: the api_endpoint option, RISICARE_API_ENDPOINT, the endpoint option, RISICARE_ENDPOINT, the default. So if you set only endpoint or RISICARE_ENDPOINT, the SDK sent scores to that host also. Python 0.5.0 and npm 0.8.0 use a new order (see above). For the hosted service, set neither — see Configuration (Python) and JS Configuration.

Other changes:

  • The fix runtime is off by default (option fix_runtime / fixRuntime, variable RISICARE_FIX_RUNTIME). Python 0.1.14 to 0.3.0 and npm 0.3.0 to 0.6.0 started it by default when an API key was set. Keep it off during the beta: the route that it reads is not reachable with an API key.
  • A redirect, or a 2xx response with no Risicare acknowledgement, is an export failure. Before: npm 0.6.0 counted a redirect to a page that answers 200 as a success, and Python 0.3.0 counted a 2xx response with no acknowledgement as a success.
  • When content capture is off, the SDK also removes the OpenTelemetry GenAI, OpenInference and CrewAI content keys, for example input.value, output.value and gen_ai.prompt.
  • The licence is a proprietary licence (see the LICENSE file in the package). It needs an active paid subscription. Python 0.3.0, npm 0.6.0 and earlier releases stay under the MIT licence.

To upgrade from an older release, see Upgrading from Python 0.3.0 or npm 0.6.0 or earlier.


Python SDK (risicare)

The release dates on this page are the registry upload dates (UTC).

0.1.14 — 2026-06-20

First publish to PyPI since 0.1.12 — folds in the unpublished 0.1.13 (F-704) plus framework-instrumentation truthfulness work and the run-end signal. Backward-compatible — no public symbol removed and no signature changed (top-level __all__ and all integration entry points verified identical to 0.1.12). All new behavior is additive, opt-in, or log-only.

Fixed:

  • F-A-023 — framework instrumentation now tells the truth (and works where it silently didn't). Some integrations attached wrappers that never sat on the framework's live call path (base/override shadowing, interface drift, moved patch targets) yet self-reported "instrumented" while emitting zero spans. Reworked all 10 framework integrations (langchain, autogen, langgraph, litellm, llamaindex, dspy, instructor, crewai, pydantic_ai, openai_agents) so that what is reported as instrumented actually emits spans — verified by execution on the published-install path.
  • #186 — silent span-drops are now surfaced as a rate-limited WARNING (warn-once per outage) when spans are created but no exporter is configured or the backend is unreachable. No exception is raised.
  • F-B-009 / F-B-011 — the import latch now retries after a failed instrumentation attempt, and patch functions are idempotent on retry (no double-wrapping).

Added:

  • F-B-012 — SDK run-end signal. @session, @agent, and @trace emit a lifecycle-end marker on context-manager exit (risicare.lifecycle.event=end, entity=session|agent|trace, status=ok|error). Normal exit ⇒ ok; an exception propagating out ⇒ error (the exception is re-raised unchanged). Lets the platform mark sessions ended instead of leaving them implicitly "active forever." Purely additive — an older backend ignores the marker.
  • F-704 — graceful-shutdown span flush. init() installs an os.register_at_fork hook (gunicorn --preload / celery prefork) and a SIGTERM handler so buffered spans flush on k8s / Docker / systemd termination (atexit does not fire on SIGTERM). The handler is chained (your existing handler still runs), main-thread-only, and a no-op on Windows.
  • Honest instrumentation introspection. get_instrumented_modules() now reports a richer per-module record — state ∈ attempted plus inert and version_compat keys (existing keys unchanged). Gate deploy/health checks on state == "instrumented" rather than is_instrumented(), which stays attempt-based.
  • Opt-in strict mode — set RISICARE_STRICT_INSTRUMENTATION=1 to raise InstrumentationError at boot on partial/failed instrumentation instead of proceeding best-effort. Off by default — existing behavior is unchanged unless you set it.

0.1.13 — unpublished (folded into 0.1.14)

Internal version bump for F-704 (os.register_at_fork + SIGTERM-safe flush). Never published to PyPI — its changes shipped as part of 0.1.14.

0.1.11 – 0.1.12 — maintenance releases

Incremental fixes between 0.1.10 and the 0.1.14 publish; 0.1.12 was the last release on PyPI prior to 0.1.14. See the SDK repository CHANGELOG.md for commit-level detail.

0.1.10 — 2026-03-24

Critical fix:

  • score() was blocking for up to 5 seconds per call in 0.1.9. Now uses a daemon thread with a shared persistent HTTP client — returns in under 1ms regardless of server availability.

Also includes:

  • Client-side validation: score() rejects values outside [0.0, 1.0] with a warning instead of sending to server.
  • Server-side validation: out-of-range score values now return HTTP 422 with a clear message.
  • All 13 evaluation scorers verified in production (10 pass immediately, 3 require additional configuration).

Upgrade from 0.1.9: If you installed 0.1.9, upgrade immediately to avoid blocking score() calls. Upgrade to the current release — do not pin to 0.1.10:

pip install --upgrade risicare

0.1.9 — 2026-03-22

New features:

  • risicare.score(trace_id, name, value): Record custom evaluation scores from your code. Scores appear in the dashboard linked to traces.

Documentation fixes:

  • Scorer docs rewritten — removed broken risicare_evaluation imports, replaced with risicare.score() + server-side API patterns
  • Fixed Fix Runtime import path (from risicare import init_runtime instead of from risicare.runtime import init_runtime)
  • Removed phantom from risicare import diagnose — manual diagnosis uses the REST API

0.1.8 — 2026-03-19

New features:

  • report_error(exception): report caught exceptions to the self-healing pipeline. Works both inside and outside traced contexts.
  • Client-side error deduplication: standalone report_error() calls suppress duplicate errors (same type + message) for 5 minutes
  • In-context error detection: spans worker detects errors reported via report_error() for automatic diagnosis

0.1.7 — 2026-03-07

  • Infrastructure-only release (PG least-privilege, diagnosis pipeline fixes)
  • No SDK behavior changes from 0.1.6

0.1.6 — 2026-03-03

New features:

  • @trace dual-mode: works as both decorator and context manager (with trace("name"):)
  • @session gains session_id and auto_generate parameters
  • ManagedSpan: tracer.start_span() now returns a ManagedSpan that supports both context manager and standalone .end() usage
  • Orphan trace debug warnings when @trace is not used to group LLM calls
  • service_name propagation to tracer for span metadata

Bug fixes:

  • Import hook deadlock: threading.Lock → threading.RLock across 11 framework integrations
  • Sentinel object filtering: OpenAI Omit / Anthropic NotGiven no longer corrupt span attributes and cause span drops
  • @agent and phase decorators always set context (removed early return that skipped agent_context() when tracer disabled)

0.1.4 — 2026-02-25

Security fixes (critical — upgrade required):

  • 20 security findings remediated (auth, SQL injection, OOM protection)
  • Session auth added to 27 previously unprotected dashboard routes
  • ClickHouse queries converted to parameterized placeholders (13 functions)
  • Gateway rejects spans missing project_id instead of pooling into shared bucket
  • Gateway buffer max capacity (100K) with 503 when full

Bug fixes:

  • AgentRole enum converted to string in set_attribute() (silent span loss)
  • 4 missing LLM fields added to to_dict(): temperature, max_tokens, stop_reason, confidence
  • JSON serialization errors now logged instead of silently swallowed

0.1.2 — 2026-02-23

  • Together AI v2.2+ CompletionsResource detection

0.1.3

Version 0.1.3 is published on PyPI (2026-02-25, the same day as 0.1.4). Its code is the same as 0.1.4 apart from the version string.

0.1.1 — 2026-02-23

  • project_id deprecated — emits DeprecationWarning when passed to init()
  • hash() → hashlib.md5() for deterministic A/B bucketing across restarts
  • Fix content truncated to 10K characters

0.1.0 — 2026-02-21

Initial release.

  • Auto-instrumentation via import hooks for 12 LLM providers
  • 10 framework integrations (LangChain, LangGraph, CrewAI, AutoGen, etc.)
  • Progressive integration Tiers 0–5
  • Batch span export with configurable batch_size (clamped to 1–10,000)
  • sample_rate with deterministic head-based sampling

JavaScript SDK (risicare npm)

0.4.2 — 2026-06-20

First publish to npm since 0.4.0 — folds in the unpublished 0.4.1 (PR #155 hardening + GuardRejectedError export) plus the run-end signal. Backward-compatible — no export removed; all subpath exports and decorator signatures unchanged; package exports map identical to 0.4.0.

Added:

  • F-B-012 — SDK run-end signal (parity with Python 0.1.14). agent(), session(), traceThink/Decide/Act/Observe, and the multi-agent wrappers emit a lifecycle-end marker on completion (risicare.lifecycle.event=end, entity=session|agent|trace, status=ok|error). Normal completion ⇒ ok; a thrown error ⇒ error (the error is re-thrown unchanged). New src/lifecycle.ts. Purely additive — an older backend ignores the marker.
  • GuardRejectedError is now a public root export (import { GuardRejectedError } from 'risicare') — thrown by the fix interceptor when a guard rejects an LLM call (fail-closed), and re-thrown by provider patches so the call is never made. (Originally the 0.4.1 intent.)

Fixed:

  • PR #155 — JS SDK hardening (16 audit findings closed). Input and robustness hardening across the SDK. (Originally the 0.4.1 intent; released here.)

0.4.1 — unpublished (folded into 0.4.2)

Internal bump for PR #155 (JS SDK hardening, 16 findings) + the public GuardRejectedError export. Never published to npm — released as part of 0.4.2.

0.4.0 — 2026-05-23

Webhook signature verification:

  • verifyWebhookSignature() and WebhookVerificationError are now public root exports. The verifier is synchronous, built on Node's crypto, and validates the Stripe-style t={ts},v1={hex} HMAC-SHA256 signature with a configurable timestamp-skew window and constant-time comparison — reaching parity with the Python SDK's verify_webhook_signature. Available via npm install risicare.

0.3.0 — 2026-03-28

4 critical parity fixes + stress tests:

  • Trace ID consistency (P0): getTraceContext().traceId now matches the next span's trace ID. Pre-allocates _rootTraceId in context. Fixes score() targeting wrong trace.
  • LangChain dedup (P1): RisicareCallbackHandler.withSuppression() prevents duplicate spans when used alongside patchOpenAI(). Same pattern as LlamaIndex handler.
  • reportError() dedup (P1): SHA256 fingerprint with 5-minute TTL, 1000-entry cap. 10x same error creates 1 span. Matches Python SDK.
  • Provider attribute depth (P1): All 12 providers now capture 11 gen_ai.* attributes (was 4). Includes gen_ai.request.model, gen_ai.request.temperature, gen_ai.usage.prompt_tokens, gen_ai.usage.completion_tokens, gen_ai.usage.total_tokens, gen_ai.response.finish_reasons, llm.cost.total_usd.
  • score() NaN fix: Number.isFinite() check rejects NaN and Infinity values.
  • FixRuntime subsystem: FixLoader, FixApplier, FixCache, FixInterceptor implemented (not connected to init()).
  • 58 stress tests: Wire format, lifecycle edge cases, concurrency (100 tasks), context propagation (async generators, EventEmitter), memory safety, provider edge cases. Total: 483 tests, 51 files, 0 failures.

0.2.2 — 2026-03-27

  • npm README rewritten for developer onboarding (conversion-focused with quickstart, all 12 providers, 4 frameworks)
  • package.json metadata updated (description, keywords, homepage, bugs URL)

0.2.1 — 2026-03-25

Bug fix:

  • Phase decorators (traceThink, traceDecide, traceAct, traceObserve) now accept an optional name parameter: traceThink("analyze", fn) in addition to the bare form traceThink(fn).

0.2.0 — 2026-03-24

Major release — full provider and framework parity with Python SDK.

Providers (3 → 12):

  • Added 9 native providers: Google Gemini (patchGoogleAI), Mistral (patchMistral), Groq (patchGroq), Cohere (patchCohere), Together AI (patchTogether), Ollama (patchOllama), HuggingFace (patchHuggingFace), Cerebras (patchCerebras), AWS Bedrock (patchBedrock)
  • 8 OpenAI-compatible hosts detected by base URL via patchOpenAI(): DeepSeek, Together AI, Groq, xAI, Fireworks, Baseten, Novita, BytePlus (any other compatible endpoint, e.g. self-hosted vLLM, is traced generically)

Frameworks (0 → 4):

  • LangChain.js: RisicareCallbackHandler from risicare/langchain
  • LangGraph.js: instrumentLangGraph() from risicare/langgraph
  • Instructor: patchInstructor() from risicare/instructor
  • LlamaIndex.TS: RisicareLlamaIndexHandler from risicare/llamaindex

Other:

  • tracedStream() utility for tracing async iterables
  • Dedup infrastructure (suppressProviderInstrumentation)
  • AgentRole expanded to 14 values, MessageType to 18 values
  • 404 tests passing

0.1.5 — 2026-03-24

Bug fixes:

  • Fixed context propagation — withSession(), withAgent(), and withPhase() now work without requiring init(). Previously, context was silently skipped when the SDK wasn't initialized, causing unreliable session/agent tracking.

New features:

  • reportError(error, options?) — report caught exceptions for diagnosis. Creates an error span that triggers the self-healing pipeline. Never throws.
  • score(traceId, name, value, options?) — send custom evaluation scores. Validates range [0.0, 1.0], non-blocking. Never throws.

0.1.4 — 2026-03-07

  • project_id deprecated — emits console.warn when passed to init()
  • Version bump to align with Python SDK audit fixes

0.1.2 — 2026-02-22

  • Agent role, parent agent ID, session user ID propagated as span attributes (Python parity)
  • extractTraceContext keys normalized to camelCase
  • getCurrentContext() expanded with all stored fields
  • Phase decorators use correct SpanKind (THINK/DECIDE/TOOL_CALL/OBSERVE) instead of INTERNAL
  • REFLECT, COMMUNICATE, COORDINATE added to SemanticPhase enum

0.1.1 — 2026-02-21

Critical fixes:

  • Provider bundle isolation: singletons moved to globalThis so sub-path exports share tracer state
  • traceContent config wired through to Tracer
  • Process listener leak fixed (.once() + cleanup on shutdown)
  • Failed export batches re-queued with 3-retry limit before drop
  • Shutdown race condition: Promise-based dedup replaces boolean flag
  • Circuit breaker: half-open state sends 1 probe, not full retry loop
  • Anthropic/Vercel AI SpanKind: CLIENT → LLM_CALL
  • getMetrics() exposed in public API

0.1.0 — 2026-02-21

Initial release.

  • OpenAI, Anthropic, Vercel AI SDK provider instrumentations
  • AsyncLocalStorage-based context propagation
  • agent(), session(), phase decorators as higher-order functions
  • BatchSpanProcessor with HTTP exporter

Migration Guides

Upgrading from Python 0.3.0 or npm 0.6.0 or earlier

What an older release does today:

  • It sends spans to https://app.risicare.ai/v1/spans. The gateway accepts spans at that address through a temporary route. Risicare will remove this route. It has not announced a date.
  • npm 0.6.0 follows a redirect. If that address redirects to a page that answers 200, npm 0.6.0 counts it as a success, and the spans are lost with no warning. Python 0.3.0 does not follow a redirect, and it reports the failure. Other older releases were not checked for this.
  • A release that has score() sends scores to https://app.risicare.ai/api/v1/scores. That address answers 401 to an API key, so the scores are not recorded.
  • Python 0.1.14 to 0.3.0 and npm 0.3.0 to 0.6.0 start the fix runtime by default when an API key is set. It reads fixes from https://app.risicare.ai/api/v1/fixes/active, which also answers 401.

To upgrade:

  1. Install the current release: pip install --upgrade risicare or npm install risicare@latest.
  2. Remove an endpoint option or a RISICARE_ENDPOINT value that names https://app.risicare.ai. (In 0.4.0 and 0.7.0, https://ingest.risicare.ai as the endpoint sent scores to the ingest gateway, which has no scores route; 0.5.0 and 0.8.0, and the releases after them, send them to the API host.) With no value, the SDK uses the two default hosts.
  3. Do not start the fix runtime during the beta. The new releases do not start it by default, and the route that it reads is not reachable with an API key.

Upgrading from 0.1.2 or earlier to 0.1.4+

project_id deprecated:

# Before (0.1.2)
risicare.init(
    api_key="rsk-...",
    project_id="my-project",  # ⚠️ DeprecationWarning
)
 
# After (0.1.4+)
risicare.init(
    api_key="rsk-...",
    service_name="my-agent",
    environment="production",
)

The gateway determines your project from the API key. Use service_name and environment for organization within a project.

Upgrading from 0.1.4 to 0.1.6+

No breaking changes. New features are additive:

# New: @trace as context manager
from risicare import trace
 
with trace("my-operation"):
    result = do_work()
 
# New: @session with fixed ID
from risicare import session
 
@session(session_id="fixed-id")
def handle(query):
    pass
 
# New: @session with auto-generated ID
@session(auto_generate=True)
def handle(query):
    pass

Version Compatibility

Python SDKCorePythonNote
0.6.0≥0.1.8≥3.10Current — published on PyPI
0.5.1≥0.1.8≥3.10Published on PyPI
0.5.0≥0.1.8≥3.10Published on PyPI
0.4.0≥0.1.7≥3.10Published on PyPI
0.3.0≥0.1.7≥3.10Published on PyPI
0.2.2≥0.1.6≥3.10Published on PyPI
0.2.0≥0.1.6≥3.10Published on PyPI
0.1.14≥0.1.0≥3.10First publish of the fork/SIGTERM flush fix
0.1.13——Unpublished (folded into 0.1.14)
0.1.12≥0.1.0≥3.10Last PyPI release before 0.1.14
0.1.11≥0.1.0≥3.10
0.1.10≥0.1.0≥3.10
0.1.9≥0.1.0≥3.10Do not use (blocking score())
0.1.8≥0.1.0≥3.10
0.1.7≥0.1.0≥3.10
0.1.6≥0.1.0≥3.10
0.1.5≥0.1.0≥3.10
0.1.4≥0.1.0≥3.10Minimum recommended
0.1.3≥0.1.0≥3.10Same code as 0.1.4
≤0.1.2——Do not use

The Core column is the risicare-core requirement that each release declares on PyPI.

JS SDKNode.jsNote
0.9.0≥18.0.0Current — published on npm
0.8.0≥18.0.0Published on npm
0.7.0≥18.0.0Published on npm
0.6.0≥18.0.0Published on npm
0.5.2≥18.0.0Published on npm
0.5.0≥18.0.0Published on npm
0.4.2≥18.0.0Run-end signal + GuardRejectedError
0.4.1≥18.0.0Unpublished (folded into 0.4.2)
0.4.0≥18.0.0Webhook signature verifier
0.3.0≥18.0.04 critical parity fixes + stress tests
0.2.2≥18.0.0README + metadata
0.2.1≥18.0.0Phase decorator name fix
0.2.0≥18.0.0Full provider + framework parity
0.1.5≥18.0.0reportError + score
0.1.4≥18.0.0
0.1.3≥18.0.0
0.1.2≥18.0.0
0.1.1≥18.0.0
0.1.0≥18.0.0

Next Steps