Changelog & Migration
Version history, breaking changes, and upgrade instructions.
Current SDK versions and how to upgrade between them.
Current Versions
| Package | Version | Language | Install |
|---|---|---|---|
risicare | 0.6.0 | Python 3.10+ | pip install risicare |
risicare (npm) | 0.9.0 | Node.js 18+ | npm install risicare |
risicare-core | 0.1.8 | Python 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 aftershutdown(). Before, it could returntruewith spans not delivered.- Python: when your own SIGTERM handler is installed before
init(), the SDK drains first and then calls your handler. Aflush()in that handler returnedTruealso when the drain lost spans or the backend rejected some. Aflush()aftershutdown()returnedTrueafter a drop or a rejection. Both now returnFalse, one time. - JavaScript: a
flush()after the SDK's own drain started (your SIGTERM or SIGINT listener, registered afterinit()) returnedtrueat once with the batch still in flight. It now waits for the drain, inside its own deadline, and returns what the drain delivered. Aflush()aftershutdown()returnedtrueafter a drop or a rejection. It now returnsfalse, one time. - To see which loss it was, read
get_metrics()/getMetrics():dropped_spans_by_reason,rejected_spans,failed_scores. Aflush()that isfalseat its deadline while none of them went up since the previousflush()means that the spans are still in flight.failed_exports/failedExportsis not a loss counter: it counts export calls that failed as a whole, and a later round can still deliver those spans.
- Python: when your own SIGTERM handler is installed before
- A shutdown that left spans makes one
flush()false, not everyflush(). The spans thatshutdown()could not deliver count asshutdown_residue. Before, everyflush()after such a shutdown returnedfalsefor ever; now the first one returnsfalseand the next onetrue, as for every other loss. A span that ends after the SDK's drain, or yourshutdown(), has stopped taking spans (for example in your SIGTERM handler) is counted asshutdown_residuetoo, with one WARNING for the first one, and oneflush()returnsfalse. Before, such a span was dropped with no count andflush()returnedtrue. A span that ends while the drain still runs is delivered (in Python, delivered or counted). In Python, a span that you start aftershutdown()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 atruedoes not show that they were traced. (JavaScript makes the span and counts it asshutdown_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, andflush()stayedtrue. It also adds 1 tofailed_scores/failedScoresnow, so the nextflush()returnsfalse, 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()andflush()returnFalse/false.RISICARE_TRACING=true,enabled=Trueandenable()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 returnedTruefor both and sent no request (it built no exporter). JavaScript, withRISICARE_TRACING=true, created spans, queued them and dropped them after four export rounds astransport_refused, andflush()returnedfalseonce and thentrue. An exporter that you name yourself (otlp_endpoint,exporters=[...]in Python) still delivers without a key. flush()returnsFalsebeforeinit()and with tracing off (enabled=False,RISICARE_TRACING=false). Before, it returnedTrue. If you callflush()on purpose with tracing off, checkis_enabled()first.debugwith no key counts nothing. Pythondebug=Truestill prints the spans (unredacted, for local development only), but they are neither counted nor delivered:exported_spansstays 0 andflush()returnsFalse. JavaScriptdebug: trueprints no span (it still writes the SDK's own debug lines). Before, the printed spans counted as exported andflush()returnedTrue.is_enabled()/isEnabled()isFalseafter the SDK's own signal drain (Python: SIGTERM; JavaScript: SIGTERM or SIGINT). Before, it stayedTrueover a processor that was shut down. In Python,enable()afterinit(enabled=False)also no longer makesis_enabled()True, because nothing was started; the JavaScript SDK behaves differently in this case, and that difference is open: do not rely onenable()afterenabledwas false.- Python: an
HttpExporterwith 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. Nowinit()leaves the exporter out,is_enabled()andflush()areFalse, and nothing is sent. Give the exporter its own key:HttpExporter(endpoint=..., api_key=...). Theinit()key goes only to an exporter whose endpoint is the endpoint ofinit(). - 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 blankapi_key/apiKeyargument counts as "not given", soRISICARE_API_KEYapplies. In JavaScript, an emptyapiKeyoption won over the variable before. To turn tracing off, setenabledto 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, callprocess.exit(143)yourself. Register the listener beforeinit()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=trueand 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()andflush()no longer wait for an internal lock. A SIGTERM handler that called one of them while the main thread was insideshutdown()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 raisesTypeError. 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
AgentExecutorrun that calls a tool is one trace, and theAgentExecutorspan is its root. Before, the span was lost and the run arrived as one trace for each tool call plus one. Theagent_actionspan 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_ttlis exported fromrisicare(from risicare import extend_span_ttl). Before, you imported it fromrisicare_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 returnstrueonly 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 previousflush()returned. A loss makes the nextflush()returnfalseone 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, callshutdown(timeout). After ashutdown()that left spans undelivered,flush()returnsfalse. With more than one exporter, a batch counts as delivered when the first exporter takes it, soflush()can returntruewhile another exporter refused the batch. In JavaScript, the rootflush(timeoutMs)andshutdown(timeoutMs)take a timeout.- New counters.
exported_spans/exportedSpanscounts 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 reasonunacknowledged(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 astransport_refused. - Python
init(): every parameter afterapi_keyis keyword-only. A positional call with two or more arguments raisesTypeError. - JavaScript
SpanandTracerare types only. Useimport type { Span, Tracer }. - A traced call returns a proxy. Python: a traced
stream=Truecall to OpenAI, Anthropic, Groq, Cerebras or Together returns a proxy of the provider's stream object, not a generator (inspect.isgenerator()isFalse). JavaScript: a traced call returns aProxyof the provider's promise (OpenAI, Anthropic, Groq, Together, Cerebras, Cohere) or of the stream (Google, Mistral, Ollama), andutil.types.isPromise()isfalsefor it. - Provider states in
get_instrumented_modules(): a patched provider isinstrumentedand one whose patch raised isfailed(before:attemptedfor both). New statepending: 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 examplethink:analyzeQuery(before:phase:think). - The host for scores and fix reads is chosen in this order: the
api_endpointoption; theendpointoption, unless it is the default ingest host;RISICARE_API_ENDPOINT;RISICARE_ENDPOINT, when noendpointoption 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_TRACINGis trimmed and case is ignored.true,1,yesandonturn tracing on;false,0,noandoffturn it off; an empty value is no value; any other value turns tracing off with one WARNING.- Python Tier 0 (
import risicarewithRISICARE_TRACINGon) uses the defaults ofinit(): environmentdevelopmentand no service name. Before, it sentproductionand the service namedefault. - 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.CRASHEDfor "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 inpool.terminate()(a very rare case remains).pool.terminate()and the end of awith 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 withclose()andjoin(), and aProcessPoolExecutor, 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 withimportlib.import_moduleis patched at its nextimportstatement.) - Python: Mistral is traced (chat completions, sync and async; streaming calls are not traced).
- Python: a traced
stream=Truecall keeps the stream object of the provider (OpenAI, Anthropic, Groq, Cerebras, Together; for Google the response object).with ... as stream:works, and LangChainstream()works. score()reports a failed request: one WARNING and thefailed_scores/failedScorescounter. 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()andtraced_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 JavaScriptpatchOpenAIandpatchAnthropiccapture the prompt and completion text (a streamed call gives the prompt only; other JavaScript providers capture no text). - A
401or403is 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:
| Data | Default host | Option (Python / JavaScript) | Environment variable |
|---|---|---|---|
| Spans | https://ingest.risicare.ai | endpoint / endpoint | RISICARE_ENDPOINT |
| Scores (and fix reads) | https://api.risicare.ai | api_endpoint / apiEndpoint | RISICARE_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, variableRISICARE_FIX_RUNTIME). Python0.1.14to0.3.0and npm0.3.0to0.6.0started 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
2xxresponse with no Risicare acknowledgement, is an export failure. Before: npm0.6.0counted a redirect to a page that answers200as a success, and Python0.3.0counted a2xxresponse 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.valueandgen_ai.prompt. - The licence is a proprietary licence (see the
LICENSEfile in the package). It needs an active paid subscription. Python0.3.0, npm0.6.0and 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@traceemit 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 anos.register_at_forkhook (gunicorn--preload/ celery prefork) and a SIGTERM handler so buffered spans flush on k8s / Docker / systemd termination (atexitdoes 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 plusinertandversion_compatkeys (existing keys unchanged). Gate deploy/health checks onstate == "instrumented"rather thanis_instrumented(), which stays attempt-based. - Opt-in strict mode — set
RISICARE_STRICT_INSTRUMENTATION=1to raiseInstrumentationErrorat 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 risicare0.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_evaluationimports, replaced withrisicare.score()+ server-side API patterns - Fixed Fix Runtime import path (
from risicare import init_runtimeinstead offrom 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:
@tracedual-mode: works as both decorator and context manager (with trace("name"):)@sessiongainssession_idandauto_generateparametersManagedSpan:tracer.start_span()now returns aManagedSpanthat supports both context manager and standalone.end()usage- Orphan trace debug warnings when
@traceis not used to group LLM calls service_namepropagation to tracer for span metadata
Bug fixes:
- Import hook deadlock:
threading.Lock→threading.RLockacross 11 framework integrations - Sentinel object filtering: OpenAI
Omit/ AnthropicNotGivenno longer corrupt span attributes and cause span drops @agentand phase decorators always set context (removed early return that skippedagent_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_idinstead 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+
CompletionsResourcedetection
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_iddeprecated — emitsDeprecationWarningwhen passed toinit()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_ratewith 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). Newsrc/lifecycle.ts. Purely additive — an older backend ignores the marker. GuardRejectedErroris 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()andWebhookVerificationErrorare now public root exports. The verifier is synchronous, built on Node'scrypto, and validates the Stripe-stylet={ts},v1={hex}HMAC-SHA256 signature with a configurable timestamp-skew window and constant-time comparison — reaching parity with the Python SDK'sverify_webhook_signature. Available vianpm install risicare.
0.3.0 — 2026-03-28
4 critical parity fixes + stress tests:
- Trace ID consistency (P0):
getTraceContext().traceIdnow matches the next span's trace ID. Pre-allocates_rootTraceIdin context. Fixesscore()targeting wrong trace. - LangChain dedup (P1):
RisicareCallbackHandler.withSuppression()prevents duplicate spans when used alongsidepatchOpenAI(). 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). Includesgen_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 formtraceThink(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:
RisicareCallbackHandlerfromrisicare/langchain - LangGraph.js:
instrumentLangGraph()fromrisicare/langgraph - Instructor:
patchInstructor()fromrisicare/instructor - LlamaIndex.TS:
RisicareLlamaIndexHandlerfromrisicare/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(), andwithPhase()now work without requiringinit(). 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_iddeprecated — emitsconsole.warnwhen passed toinit()- 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)
extractTraceContextkeys normalized to camelCasegetCurrentContext()expanded with all stored fields- Phase decorators use correct SpanKind (THINK/DECIDE/TOOL_CALL/OBSERVE) instead of INTERNAL
REFLECT,COMMUNICATE,COORDINATEadded to SemanticPhase enum
0.1.1 — 2026-02-21
Critical fixes:
- Provider bundle isolation: singletons moved to
globalThisso sub-path exports share tracer state traceContentconfig 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.0follows a redirect. If that address redirects to a page that answers200, npm0.6.0counts it as a success, and the spans are lost with no warning. Python0.3.0does not follow a redirect, and it reports the failure. Other older releases were not checked for this. - A release that has
score()sends scores tohttps://app.risicare.ai/api/v1/scores. That address answers401to an API key, so the scores are not recorded. - Python
0.1.14to0.3.0and npm0.3.0to0.6.0start the fix runtime by default when an API key is set. It reads fixes fromhttps://app.risicare.ai/api/v1/fixes/active, which also answers401.
To upgrade:
- Install the current release:
pip install --upgrade risicareornpm install risicare@latest. - Remove an
endpointoption or aRISICARE_ENDPOINTvalue that nameshttps://app.risicare.ai. (In 0.4.0 and 0.7.0,https://ingest.risicare.aias theendpointsent 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. - 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):
passVersion Compatibility
| Python SDK | Core | Python | Note |
|---|---|---|---|
| 0.6.0 | ≥0.1.8 | ≥3.10 | Current — published on PyPI |
| 0.5.1 | ≥0.1.8 | ≥3.10 | Published on PyPI |
| 0.5.0 | ≥0.1.8 | ≥3.10 | Published on PyPI |
| 0.4.0 | ≥0.1.7 | ≥3.10 | Published on PyPI |
| 0.3.0 | ≥0.1.7 | ≥3.10 | Published on PyPI |
| 0.2.2 | ≥0.1.6 | ≥3.10 | Published on PyPI |
| 0.2.0 | ≥0.1.6 | ≥3.10 | Published on PyPI |
| 0.1.14 | ≥0.1.0 | ≥3.10 | First publish of the fork/SIGTERM flush fix |
| 0.1.13 | — | — | Unpublished (folded into 0.1.14) |
| 0.1.12 | ≥0.1.0 | ≥3.10 | Last 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.10 | Do 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.10 | Minimum recommended |
| 0.1.3 | ≥0.1.0 | ≥3.10 | Same 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 SDK | Node.js | Note |
|---|---|---|
| 0.9.0 | ≥18.0.0 | Current — published on npm |
| 0.8.0 | ≥18.0.0 | Published on npm |
| 0.7.0 | ≥18.0.0 | Published on npm |
| 0.6.0 | ≥18.0.0 | Published on npm |
| 0.5.2 | ≥18.0.0 | Published on npm |
| 0.5.0 | ≥18.0.0 | Published on npm |
| 0.4.2 | ≥18.0.0 | Run-end signal + GuardRejectedError |
| 0.4.1 | ≥18.0.0 | Unpublished (folded into 0.4.2) |
| 0.4.0 | ≥18.0.0 | Webhook signature verifier |
| 0.3.0 | ≥18.0.0 | 4 critical parity fixes + stress tests |
| 0.2.2 | ≥18.0.0 | README + metadata |
| 0.2.1 | ≥18.0.0 | Phase decorator name fix |
| 0.2.0 | ≥18.0.0 | Full provider + framework parity |
| 0.1.5 | ≥18.0.0 | reportError + 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 |