Changelog

Release history for the Wytness wrapper SDKs: wytness-ai (PyPI, imports as import wytness_ai) and @wytness/ai (npm). Older 0.x entries below preserve the release history under the package's previous names.

Note
Upgrade with pip install --upgrade wytness-ai or npm install @wytness/ai@latest.
1.12.0Feature2026-08-22

Capture coverage reaches your dashboard

  • -Added: the SDK reports its per-surface capture coverage to Wytness — surface names and states only, plus a small delivery-health snapshot — so the dashboard can state which LLM surfaces are being recorded, which are not, and when nothing has reported at all. Reports go out on first coverage, on any coverage change, and every 15 minutes as a liveness signal.
  • -Added: the report rides its own fail-soft background channel — a refused or failed report is logged at debug level and retried on the next pass; it can never affect event capture, and dry-run mode skips it entirely. In TypeScript the timer is unref'd so it never holds your process open.
Migration guide
  • -No code changes required. The report contains no prompts, no payloads, and no credentials — only surface names, capture states, and delivery counters, sent to the same endpoint with the same API key as your events.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.11.0Feature2026-08-19

The SDK tells you what it is recording — and what it is not

  • -Added: one line at init naming every provider surface the SDK patched and every surface it knows about but does not capture, such as `client.chat.completions.parse` and `client.embeddings.create`. Detection runs against the package you actually have installed, so a surface that disappears upstream is reported as missing rather than skipped in silence.
  • -Added: `stats()` carries a per-surface capture block, so you can assert in your own tests that the surfaces you rely on are the ones being recorded.
  • -Fixed: streamed calls made through the `chat.completions.stream()` helper recorded no token usage. The helper passes its own default for `stream_options`, which the SDK read as a deliberate opt-out. A default now counts as unset; an opt-out you set yourself is still never overridden.
  • -Fixed (TypeScript): `wrapAnthropic()` stopped early when `client.messages.create` was absent, leaving `client.beta.messages.*` unpatched with it. The two resources are patched independently now.
Migration guide
  • -No code changes required. If your logs are strict about volume, note the SDK now writes one line per wrapped provider at init.
1.10.0Feature2026-08-18

Two provider surfaces that were never captured, and streamed usage without opt-in

  • -Added: OpenAI Responses API capture. `client.responses.create` is OpenAI's primary API and was captured nowhere — no event, no row, no warning. It is now recorded under its own action `openai.responses.create`. Streaming takes the finished response off the terminal event, and all three endings are captured (completed, incomplete, failed), so a run that was cut short is recorded rather than dropped.
  • -Added: Anthropic beta namespace capture. `client.beta.messages.*` is a different class from the stable Messages resource, so patching the stable one never covered it and calls there produced no events. Now wrapped, under its own action `anthropic.beta.messages.create`.
  • -Changed: streamed OpenAI calls record token usage with no opt-in. The SDK sets `stream_options: {include_usage: true}` when you have not, and hides the resulting usage-only chunk, so your loop yields exactly the chunks it did before. A `stream_options` you set yourself is never overridden.
  • -Fixed: Chat Completions and Responses are patched independently — one going missing upstream no longer silently stops capture on the other.
  • -Fixed: auto-capture documentation no longer claims "every model call"; it names the surfaces that are actually wrapped.
Migration guide
  • -No code changes required. If you set `stream_options` yourself for the sole purpose of getting token usage into Wytness, you can drop it — the SDK now handles it and will not override yours if you keep it.
1.9.0Feature2026-08-16

Transport resilience — endpoint fallback + fail-loud dead-letter

  • -Added: adopted-endpoint fallback. When the SDK follows a server-provided regional ingest endpoint that then goes unreachable, three consecutive failures revert delivery to the last endpoint that worked — a stale or unreachable regional hint can no longer strand the queue. A 4xx or 429 counts as reachable (the endpoint answered), so a schema or rate-limit response never flaps the endpoint.
  • -Changed: the dead-letter path is now checked at startup with a real write and refuses to start if it is unwritable, instead of logging one warning and silently dropping the first failed batch — the common case is a container running unprivileged with no writable volume at ~/.wytness/. dry-run mode skips the check.
Migration guide
  • -No wrapper API changes. If your deployment relied on an unwritable dead-letter path (silently losing failed events), the SDK now refuses to start until the path is writable — mount a writable volume there or set the dead-letter path explicitly.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.8.0Feature2026-08-14

AGT 5.0 train + skill provenance capture

  • -Changed: the wrapped Microsoft AGT dependency moves to the current release train — Python pins agent-governance-toolkit[full]==4.1.0 (the latest artifact Microsoft has published to PyPI) and TypeScript pins @microsoft/agent-governance-sdk@5.0.0. One Microsoft release train; the differing numbers are Microsoft's per-registry publish cadence, not a compatibility split. All wrap points are unchanged — existing integration code runs as-is.
  • -Added: skill provenance capture. AGT's new skill-audit metadata — skill name, origin, metadata-source trust, and context hashes before/after the invocation — is captured through the signed envelope and rendered on the event detail's Skill provenance panel. On TypeScript this field is part of AGT 5.0's hash-covered audit entry; events without it are byte-identical to before.
Migration guide
  • -No wrapper API changes. TypeScript apps that also install AGT directly should move their own @microsoft/agent-governance-sdk pin to 5.0.0 — an app resolving AGT 4.x beside the wrapper logs a capture-loss warning (nested-duplicate protection).
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.7.0Feature2026-08-10

Prompt-cache token capture

  • -Added: wrapped Anthropic and OpenAI calls now record prompt-cache token counts — cache-read and cache-write — from the provider's usage on the captured event, alongside input and output tokens. Backwards-compatible: existing code is unaffected, the two fields appear only when the provider reports cache usage, and an older backend simply ignores them.
Migration guide
  • -No breaking changes — additive. Cache-token capture activates automatically wherever `wrap=` / `wrapAnthropic()` / `wrapOpenAI()` is already enabled.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.6.0Fix2026-08-09

Rate-limit resilience

  • -Changed: HTTP 429 (rate-limit) and 408 (timeout) responses from /ingest are now retried with the standard backoff ladder — honouring a numeric `Retry-After` header (capped at 60 s) — instead of being treated as non-retryable and dead-lettered on first sight. The local dead-letter file remains the terminal fallback after the retry budget.
  • -Changed: flushes now POST in `batch_size` / `batchSize` slices. A backlog accumulated during a connectivity blip was previously drained as one monolithic POST, which could trip server body limits and lose the whole backlog to a single terminal response; each slice now fails or succeeds independently.
Migration guide
  • -No breaking changes — behavioural hardening only; no API surface changed.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.5.0Feature2026-08-05

Streaming LLM capture

  • -Added: streamed model calls are now recorded like non-streaming ones. `create(stream=True)` / `create({stream: true})` returns a transparent tee that observes the stream only as your code consumes it and emits a single capture on exhaustion, early close, or mid-stream error; `messages.stream(...)` helpers are captured without touching `.text_stream` / `finalMessage()`. Captured entries carry `parameters.stream: true`. OpenAI usage totals are recorded when the call opts in via `stream_options` `include_usage`.
  • -Fixed: the init banner now shows the effective ingest endpoint after region discovery (e.g. `api.wytness.ai → api-<region>.wytness.ai`) instead of the configured base. Sends already used the effective endpoint — only the banner was misleading.
Migration guide
  • -No breaking changes — additive. Streaming capture activates automatically wherever `wrap=` / `wrapAnthropic()` / `wrapOpenAI()` is already enabled.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.4.0Feature2026-07-31

Automatic LLM capture

  • -Added: `init(wrap=...)` in Python (and `wrapAnthropic()` / `wrapOpenAI()` in TypeScript) records every non-streaming Anthropic and OpenAI model call automatically — prompt, reply, reasoning (Anthropic), latency and token usage — with no `AuditLog().log(...)` call of your own.
  • -Captured calls ride the same signed, PII-scrubbed sink as manual events; the wire event is byte-identical across Python and TypeScript. Streaming calls pass through uncaptured.
Migration guide
  • -No breaking changes — additive. Existing manual `log(...)` calls are unaffected; opt in per client with `wrap=` / `wrapAnthropic()` / `wrapOpenAI()`.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.3.0Feature2026-07-31

Automatic per-run session id

  • -Added: when an event carries no `session_id` or `trace_id`, the SDK now stamps a stable, per-process `session_id` — so every event from one agent run correlates into a single session on your dashboard, with no code change. Runs that set their own `session_id`/`trace_id` are left untouched.
  • -The id is stamped before signing, so the per-event Ed25519 envelope signature covers it.
Migration guide
  • -No breaking changes — additive. Agents that never set a session id now get one automatically; agents that set their own are unaffected.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.2.0Feature2026-07-24

Automatic regional ingest-endpoint discovery

  • -Added: the SDK asks the API for your organisation's ingest endpoint at `init()` and follows server endpoint hints at runtime — organisations homed to a regional engine ingest in-region with no configuration change.
  • -Added: `stats()` reports the active ingest URL (`effective_endpoint` in Python, `effectiveEndpoint` in TypeScript) alongside the configured `endpoint`.
Migration guide
  • -No breaking changes — no configuration change required.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.1.0Feature2026-07-07

Developer tooling — dry-run, PII validation, CLIs

  • -Added: dry-run mode — `init({ dryRun: true })` (or `WYTNESS_DRY_RUN=1`) builds and signs each envelope but skips the POST to the collector and logs it to stderr, so you can inspect exactly what would be sent without registering a key or hitting the network.
  • -Added: PII-field validation — `init` now warns when a declared PII field path never matches any event after a short sample window, catching typo'd tokeniser paths early.
  • -Added: `health()` now names the specific missing `WYTNESS_*` environment variables (with the portal link to fix each) instead of a bare not-initialised state.
  • -Added: `tail` CLI — stream recent events to your terminal (read-only, API-key auth).
  • -Added: `validate-chain` CLI — walk the AGT hash chain locally and prove integrity without trusting the server's own verdict.
  • -Added: framework integration recipes (FastAPI, Express, Next.js, Django, AWS Lambda, Vercel) in the SDK docs.
Migration guide
  • -No breaking changes — all additions are backwards-compatible.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.0.2Feature2026-06-05

AGT 4.0.0 dependency pin

  • -Changed: `agent-governance-toolkit` pin 3.7.0 → 4.0.0 (Python); `@microsoft/agent-governance-sdk` pin 3.7.0 → 4.0.0 (TypeScript). New 4.0 `AuditEntry` fields (`arguments_hash`, `approver_did`, `policy_version`, `issued_at`, `completed_at` on Python; `argumentsHash`, `approverDid`, `policyVersion`, `issuedAt`, `completedAt` on TypeScript) pass through the wrap path unchanged.
  • -Changed (Python): `cryptography` constraint widened from `>=42.0.7,<43` to `>=46.0.7,<49` per AGT 4.0 transitive requirement.
  • -Fixed (Python): `_try_resolve_event_processor()` resolves `agent_os.event_sink.GovernanceEventProcessor` as the primary candidate. Previous candidate list checked `agent_os_kernel.event_sink` and `agent_governance.event_sink`, neither of which exists in any released AGT version. Event-sink auto-wire (`event_wired`) was a silent `False` for every installation using `init(auto_wire=True)` since the wrapper shipped. Audit-log monkey-patch was unaffected; only the second event stream was missing.
Migration guide
  • -No code change required. AGT 4.0 stub redirects keep existing import paths functional.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.0.1Feature2026-06-04

Init-time signing-key validation

  • -Added: `init()` validates that the configured signing key is registered for the org before any events are sent. Derives the Ed25519 public key from `WYTNESS_SIGNING_KEY`, computes its SHA-256 fingerprint, calls `GET /v1/keys/signing/validate?fp=<hex>`. Unrecognised fingerprint raises `WytnessInitError`; network errors raise `WytnessTransportError`.
  • -Added: backend endpoint `GET /v1/keys/signing/validate?fp=<sha256-hex>` returns 200 for active registered fingerprints, 404 with distinct reasons (`not_found` vs `revoked`), 400 on bad fingerprint shape.
  • -Fixed: locally-generated Ed25519 keys (via `openssl genpkey`, `Ed25519PrivateKey.generate()`, or `@noble/ed25519`) were accepted by `init()` and only failed at first `/ingest` POST with `envelope_key_not_registered`. Now rejected at startup with an error message pointing at the dashboard Keys page.
  • -Unchanged: `/ingest` semantics, envelope shape, chain-hash construction. Init-time check only.
Migration guide
  • -No code change required. Customers already generating signing keys on the dashboard see no behaviour change.
  • -Upgrade: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`.
1.0.0Feature2026-05-25

Initial release of wytness-ai / @wytness/ai

  • -Added: AGT `AuditLog.log()` / `AuditLogger.prototype.log` auto-wire via monkey-patch in `init(auto_wire=True)` / `init({ autoWire: true })`.
  • -Added (Python): `WytnessEventSink` registration on AGT's `GovernanceEventProcessor` for the second event stream.
  • -Added: per-event Ed25519 envelope signing with customer-held key. Signature covers canonical-JSON of `{data, id, time, type}` — the full CloudEvents envelope identity, not the data payload alone.
  • -Added: customer-held PII pseudonymisation. HMAC-SHA256 pseudonyms + X25519 key agreement + ChaCha20-Poly1305 token-map encryption. Backend stores the encrypted blob; only the customer's secret can reverse pseudonyms.
  • -Added: typed exception hierarchy. `WytnessSDKError` → `WytnessConfigError` / `WytnessInitError` / `WytnessTransportError`.
  • -Added: `stats()`, `health()`, `wire_status()` / `wireStatus()` runtime introspection. Safe to call before `init()`.
  • -Added (Python): dead-letter persistence at `~/.wytness/wytness-deadletter.jsonl`; init-time write-probe surfaces unwritable directories in `health()`.
  • -Added: ±25% jittered backoff on POST retries.
  • -Added (Python): `wytness-ai verify` CLI for end-to-end synthetic event.
  • -Changed: public surface narrowed to package root. Lower-level primitives moved to `wytness_ai.internal` (no semver promise).
  • -Configuration: `signing_key` / `signingKey` required (no unsigned mode); `pii_pubkey` + `pii_secret` paired (both or neither).
  • -Cross-SDK parity: equal public symbols, env-var names, and exception hierarchy across Python and TypeScript.
Migration guide
  • -From any 0.x release: `pip install --upgrade wytness-ai` / `npm install @wytness/ai@latest`. No code change required for the canonical init shape; signature payload widens automatically.
  • -If you previously called `init(..., sign_envelope=False)`: that opt-out has been removed. Generate a signing key on the dashboard Keys page.
0.2.0-dev.0 (@wytness/agt)Feature2026-05-15

TypeScript wrapper SDK — first real wrap of AGT-TS

  • -The TypeScript wrapper now wraps `@microsoft/agent-governance-sdk@3.7.0` cleanly — call `init({ autoWire: true })` and every AGT audit entry is automatically forwarded into the Wytness envelope path. Framework adapters work unchanged.
  • -Full 3-layer evidence on every event: AGT identity + AGT hash chain + Wytness envelope.
  • -Byte-for-byte wire-format parity with the Python wrapper — the same event from either SDK produces the same payload on the wire.
  • -ESM + CJS dual output, targets Node 18+ (uses global fetch).
0.1.0.dev0 (wytness-agt)Feature2026-04-25

Python wrapper SDK — initial scaffold

  • -wytness-agt 0.1.0.dev0 ships the Python wrapper SDK built on Microsoft's Agent Governance Toolkit 3.7.0 (hard dependency — installed automatically).
  • -Adds the Wytness layers AGT leaves out: per-event Ed25519 envelope signing (non-repudiation) and customer-held PII pseudonymisation (HMAC-SHA256 + X25519 + ChaCha20-Poly1305 — Wytness never sees the raw values).
  • -`init()` / `shutdown()` lifecycle, LangChain hello-world example included.
0.14.0Breaking2026-05-22

Deprecation release — migrate to `wytness-ai` / `@wytness/ai`

  • -Deprecated: `wytness-sdk` and `@wytness/sdk` are deprecated in favour of `wytness-ai` / `@wytness/ai`, the wrapper SDK built on Microsoft's Agent Governance Toolkit (AGT, MIT-licensed, released April 2026). AGT now provides agent identity, delegation, and tamper-evident hash chaining as open source. The wrapper SDK layers on top: customer-held PII pseudonymisation, per-event Ed25519 envelope (non-repudiation), and bring-your-own-storage routing.
  • -Sunset timeline: no new features past 2026-12-31. Security-only patches committed through 2027-06-30. After that the packages go read-only on PyPI / npm.
  • -README sunset banner on both packages points at the wrapper SDK + migration code samples.
Migration guide
  • -Python: `pip install wytness-ai`, then call `wytness_ai.init(api_key=..., signing_key=..., pii_pubkey=..., pii_secret=...)` with values from the Keys page. Generate the keys in your browser, save the private halves, and copy the displayed env vars into your secrets manager. (AGT is a hard dependency — installed automatically. The PII pair is optional but must be supplied together; signing is always required.)
  • -TypeScript: `npm install @wytness/ai` then call `await init({ apiKey, signingKey, piiPubkey, piiSecret })`.
  • -v1 SDK callers can continue running unchanged until the security-only window expires (2027-06-30) — no forced migration before then.
0.13.3Feature2026-05-20

Layer 2 (Authorization) capture — `scope_permissions` + `WYTNESS_AGENT_PERMISSIONS`

  • -Added: `@audit_tool(scope_permissions=[...], scope_token_id="...")` (Python) and `auditTool(client, fn, { scopePermissions: [...], scopeTokenId: "..." })` (TypeScript) populate Layer 2 of the audit event. Per-tool granularity; defaults to nothing recorded so existing callers don't change behaviour. Closes the gap where the field was in the schema since day one but the SDK never populated it.
  • -Added: `WYTNESS_AGENT_PERMISSIONS` env var as a process-wide default for permissions. Comma-separated list (same convention as `WYTNESS_SIGNING_KEY`). Precedence: explicit decorator value > env var > `[]`. Set once on the process (Docker env / .env / k8s manifest) and every event from that process attaches the list. Per-tool decorator value overrides the env var when both are present.
  • -Permissions captured this way are *exercised*, not *granted* — the dashboard PermissionsPanel surfaces this caveat alongside a coverage strip (`X of Y events carry permission data`). Auto-capture providers (MCP, OAuth introspection, LangChain) ship in a later release.
0.13.2Fix2026-05-19

Tokenizer mode now applies key-name sanitisation

  • -Fixed: tokenizer (pseudonymization) mode previously skipped the regex-by-key-name sanitisation that legacy mode applied. A developer who enabled the tokenizer but forgot to declare a sensitive kwarg in `pii_fields` (e.g. `password`, `api_key`, `secret`) would see the raw value signed and stored. Now both modes apply the key-name floor before anything else; tokenizer pseudonymisation operates on top of the already-redacted payload. No customer code change required — upgrade to 0.13.2.
0.13.1Fix2026-05-18

Response field on Anthropic / OpenAI typed responses

  • -Fixed: when the wrapped function returned an Anthropic Message or OpenAI ChatCompletion object, the `response` field on the recorded event captured the Python repr / `[object Object]` instead of the actual model response text. Same duck-typing that 0.13.0 introduced for `reasoning_summary` now also feeds the `response` field, capped at the 5000-char response limit; falls back to `str(result)` / `String(result)` for non-typed return values.
0.13.0Feature2026-05-18

Auto-capture: prompt, reasoning, classification + agent_instance_id

  • -New: prompt is auto-extracted from the wrapped function's first matching parameter — `user_input`, `prompt`, `question`, `query`, `input_text`, `message`, `user_message` (in that order). Python introspects parameter names; TypeScript reads keys off the first object argument. Customer code stays untouched. Truncated to 2000 chars.
  • -New: reasoning_summary is auto-extracted from the return value when it duck-types as an Anthropic-style response (`.content` with text blocks) or an OpenAI-style response (`.choices[0].message.content`). Truncated to 500 chars. Plain string returns are ignored so there are no false positives.
  • -New: inputs_classification is auto-set to `restricted` when any argument name contains `ssn`, `tfn`, `credit_card`, `card_number`, or `passport` (case-insensitive). Otherwise stays at the default `internal`. Conservative — never downgrades a more-sensitive explicit classification.
  • -New: every event now carries `agent_instance_id` — a UUID generated when AuditClient is constructed and persisted to `./keys/.instance_id` so warm restarts keep the same ID. Different hosts running the same agent produce different IDs. The Agents page uses this to count distinct instances under each agent name (and the upcoming `agent_identity_collision` rule flags accidental name reuse between unrelated agents).
  • -Changed: LangChain integration now captures the agent's reasoning. WytnessCallbackHandler.on_agent_action stores the `AgentAction.log` string and attaches it to the very next tool event under that parent run. No customer code change — just upgrade.
Migration guide
  • -No code change required. Existing explicit prompt=… / prompt: callable arguments keep winning precedence over auto-extraction. Set `auto_extract=False` (Python) or `autoExtract: false` (TypeScript) on @audit_tool to opt out entirely.
  • -Customers wanting more deterministic behaviour can keep passing prompt= explicitly — auto-extract only fills empty fields.
0.12.3Feature2026-05-18

SDK auto-reads WYTNESS_SIGNING_KEY env var

  • -New: AuditClient auto-reads WYTNESS_SIGNING_KEY from the environment when no explicit signing_key=/signingKey: arg is passed AND no file exists at signing_key_path. Matches the standard pattern of every other comparable SDK (Stripe, Anthropic, OpenAI). Customer code collapses from `AuditClient(agent_id='x', signing_key=os.environ['WYTNESS_SIGNING_KEY'])` to just `AuditClient(agent_id='x')` once the env var is set.
  • -Changed: precedence chain in the AuditClient constructor is now (1) explicit signing_key/signingKey arg, (2) file at signing_key_path/signingKeyPath, (3) WYTNESS_SIGNING_KEY env var, (4) raise/throw WytnessSetupError. Backwards-compatible — customers with an existing keys/signing.key from any 0.12.x install keep working unchanged.
  • -Changed: WytnessSetupError message rewritten to mention all three options (env var recommended; file for local dev with gitignore note; explicit arg for power users).
  • -Also bundles the 0.12.2 fix that made the on-disk path accept the portal-emitted `wyt_pk_sign_<base64>` text file.
Migration guide
  • -No code change required for existing customers — explicit signing_key= calls still work and still win precedence.
  • -New customers: set WYTNESS_SIGNING_KEY env var in your secrets manager / .env / deployment platform's env config, then construct AuditClient(agent_id='x') with no key-related kwargs. The SDK reads the env var automatically and verifies signatures against the matching public PEM the portal registered when you generated the key.
0.12.2Fix2026-05-18

On-disk signing-key path accepts portal-emitted format

  • -Fixed: load_private_key() (Python) and the file-path branch of client.ts (TypeScript) now accept the wyt_pk_sign_<base64> text file that the portal Keys page writes when you click Download. Pre-fix the on-disk loader was PEM-only, even though the constructor's signing_key=/signingKey: arg already auto-detected every format. Customers saving the portal-emitted file to ./keys/signing.key — the path the docs and the portal modal both recommended — hit ValueError: Could not deserialize key data at AuditClient construction.
  • -Both SDKs now try parse-direct on the file bytes first (preserves PEM + raw-binary formats), then fall back to base64-text decode with optional `wyt_` prefix stripping. The portal download flow now works end-to-end with no manual conversion step.
  • -All on-disk formats are pinned by tests: PEM, raw 32-byte seed, raw 64-byte NaCl secretKey, `wyt_pk_sign_<base64>` text, and bare base64 text. Every format produces a working AuditClient.
Migration guide
  • -No action required if you already have keys/signing.key working from a 0.12.0+ install.
  • -New installs: download the key from https://app.wytness.ai/keys and save it directly as keys/signing.key — no base64 conversion, no env-var setup required. The SDK reads the file as-is.
0.12.1Breaking2026-05-18

Removed auto-generation of signing keypairs

  • -Breaking: the SDK no longer auto-generates an Ed25519 keypair on first run when no signing key is configured. Constructing AuditClient without a key file at signing_key_path (default ./keys/signing.key) and without a signing_key= argument now raises WytnessSetupError instead, with a link to the portal Keys page.
  • -Why: the old auto-gen flow created orphan keys the platform had never seen, so the very first /ingest call would 412 and the customer was stuck. Every subsequent event was also rejected as invalid_signature. Forcing explicit setup eliminates the footgun entirely.
  • -New: WytnessSetupError class exported from the top-level wytness / @wytness/sdk modules. Thrown when something the customer must configure is missing.
  • -Note: 0.12.0 was tagged but never published. 0.12.1 is the first public 0.12.x release.
Migration guide
  • -If you already have a ./keys/signing.key file from a 0.11.x install, no action is required — the SDK loads it as before.
  • -New installs: generate a keypair at https://app.wytness.ai/keys (the private key is created in your browser and offered for download — Wytness never sees it; the public PEM auto-registers). Save the downloaded file to ./keys/signing.key, or base64-encode it and pass via signing_key= / signingKey:.
  • -Serverless / edge deployments: always pass signing_key explicitly via env var — the SDK can no longer paper over a missing key on cold start.
0.11.3Fix2026-04-28

Cross-SDK hash parity + task_id default

  • -Fixed: hashValue (TypeScript) now sorts object keys recursively before hashing, matching Python SDK's json.dumps(sort_keys=True). Previously, the same input produced different inputs_hash and outputs_hash across SDKs.
  • -Fixed: task_id now defaults to an empty string in the TypeScript SDK, matching Python SDK behavior. TypeScript users can now omit task_id without a validation error.
  • -Fixed: all fallback SDK version strings aligned to 0.11.3 across both SDKs.
0.11.2Fix2026-04-28

User-Agent header fix

  • -Fixed: added User-Agent header to all API requests. Requests without a User-Agent were blocked by the Cloudflare WAF rule protecting the ingest endpoint.
0.11.1Feature2026-04-24

Chain state persistence + production domain migration

  • -New: chain state persistence — the SDK now saves hash-chain state to ./keys/.chain_state and restores it on restart, eliminating false chain breaks after process restarts.
  • -New: sdk_init event emitted on startup — provides a clear audit trail of when each agent process started, including SDK version and configuration.
  • -Fixed: sdk_version in sdk_init event now reads from package metadata instead of being hardcoded.
  • -Changed: default API endpoint updated from api.wytness.dev to api.wytness.ai.
  • -Changed: default dashboard URL updated from app.wytness.dev to app.wytness.ai.
0.10.0Breaking2026-04-18

End-to-end signature verification + cross-SDK compatibility

  • -Breaking (TypeScript only): computeEventHash and signEvent now both use pyJsonDumps() matching Python's json.dumps(sort_keys=True) — recursive key sorting, Python-style separators, \uXXXX escaping. Events sent by older TS SDK versions will show a single chain break at the version boundary.
  • -Fixed: TS SDK signature verification now works end-to-end through the full pipeline (SDK → API → ledger). Previous versions produced signatures the ledger could not verify due to JSON serialization differences.
  • -Fixed: ledger now strips org_id from the verification payload — this field is added by the ingest API after the SDK signs.
  • -Both SDKs now auto-detect signing key format: 32-byte seed, 64-byte NaCl secretKey, PKCS8 DER, PEM text, and base64-encoded PEM. A key generated by either SDK or the dashboard works everywhere.
  • -wyt_pk_sign_ / wyt_hmac_ / wyt_pk_enc_ prefixes are stripped automatically before key loading in both SDKs.
Migration guide
  • -Upgrade both SDKs to 0.10.0 simultaneously to maintain cross-SDK compatibility.
  • -TypeScript users: expect a single chain break at the boundary between 0.8.x and 0.10.0 events. Resolve as 'sdk_restart' in the Verify page.
  • -Events sent by 0.9.0 were rejected by the ledger and not stored — no data to migrate, just re-send after upgrading.
  • -No changes needed to your WYTNESS_SIGNING_KEY — existing keys work as-is regardless of format.
0.8.1Feature2026-04-17

Pluggable logger in the TypeScript SDK

  • -New: setLogger(), silentLogger, consoleLogger, and the Logger type are exported from @wytness/sdk. All SDK diagnostics (HTTP ingest errors, fallback replay info, key-generation warnings, record-path errors) now flow through this logger instead of calling console.* directly.
  • -Default behaviour is unchanged — out of the box the SDK still writes to console.error / console.warn / console.log. Nothing to do if you were happy with that.
  • -Swap in your own logger (pino, winston, bunyan) or call setLogger(silentLogger) to fully suppress SDK output.
  • -Python SDK: no code change — version bumped for parity. Python SDK users already control verbosity through the standard logging module (logging.getLogger('wytness').setLevel(...)).
0.8.0Feature2026-04-16

Per-org signing keys + show-public CLI helpers

  • -New: register your org's Ed25519 signing public key on the Keys page. Without registration, /ingest now returns 412 Precondition Failed.
  • -New CLI: python -m wytness.show_public (Python) and npx wytness-show-public (TypeScript) print the public PEM matching your local keys/signing.key — paste it into the Signing Key section of the portal.
  • -First-run warning: when the SDK auto-generates a new signing key, you'll see a clear log message telling you to register the public key before sending events.
  • -SDK code itself is unchanged — only the operational requirement is new (a one-time portal action per environment).
  • -Keys page now has three independent sections — HMAC secret, encryption keypair, and signing keypair — each with its own Generate / Rotate flow. Rotate one without affecting the others.
Migration guide
  • -Upgrade to 0.8.0 in your application.
  • -Run python -m wytness.show_public (or npx wytness-show-public) to print your existing signing public key, OR generate a fresh keypair on the Keys page.
  • -Paste the PEM into the Signing Key section at https://app.wytness.ai/keys.
  • -Existing HMAC and encryption pseudonymization keys are auto-migrated into the new tables — no action required for those.
0.7.0Fix2026-04-16

Pseudonymization fixes: record() auto-applies, nested paths, dotted piiFields

  • -client.record() now auto-applies the tokenizer when configured — previously only @audit_tool / auditTool invoked it, so direct record() calls silently bypassed pseudonymization
  • -pseudonymize_params now walks nested objects and arrays, preserving structure — before, nested values were stringified to '{...}' or '[object Object]' and all data was lost
  • -pii_fields / piiFields now supports dotted paths for nested keys (e.g. 'fields.abn', 'entities.name') and wildcard forms ('entities[].name') — previously only top-level keys matched
  • -audit_tool / auditTool now passes raw params and lets record() tokenize — no double application
  • -No changes required in your code — upgrading should just start protecting PII you've already declared in pii_fields
Migration guide
  • -Upgrade to 0.7.0 and verify your pii_fields entries match the actual (possibly nested) parameter paths
  • -If you were calling client.record() directly (not via @audit_tool), the tokenizer will now apply — double-check your events contain pseudonymized data as expected
0.6.0Fix2026-04-16

SDK accepts 202 Accepted from /ingest

  • -The /ingest endpoint now returns 202 Accepted (event queued for async processing) instead of 201 Created (synchronous insert)
  • -SDK 0.6.0 accepts either 201 or 202 — stays compatible with both old and new API versions
  • -Transparent change — no code changes required in your application
0.5.1Feature2026-04-16

PII reveal in dashboard

  • -Browser-side decryption of pseudonymized PII — load your X25519 private key on the Keys page to reveal original values inline across Events and Sessions pages
  • -Cross-SDK support — decrypts token maps from both Python SDK (ChaCha20-Poly1305) and TypeScript SDK (NaCl box) events automatically
  • -Private key never leaves the browser — held in memory only, cleared on tab close. Never transmitted to Wytness servers
  • -Reveal toggle on event detail panel and session conversation timeline — switch between pseudonymized and revealed views
  • -Keys page: generate pseudonymization keys in the browser, load private key for PII reveal
0.5.0Feature2026-04-16

Zero-knowledge PII pseudonymization

  • -HMAC-based consistent pseudonyms across sessions — same PII value always produces the same token (e.g. EMAIL_3c866e6f), enabling correlation without storing raw data
  • -Encrypted token map attached to each event — maps pseudonyms back to original values, encrypted with your X25519 public key. Only you hold the decryption key; Wytness cannot reverse the mapping
  • -New AuditClient parameters: pii_hmac_secret / piiHmacSecret, encryption_public_key / encryptionPublicKey, pii_fields / piiFields
  • -Declared PII fields via pii_fields config — pseudonymize known PII fields (names, addresses) alongside auto-detected patterns, with reverse map propagation
  • -Backward compatible — without keys configured, the existing regex redaction behavior is preserved. No code changes required to keep current behavior
Migration guide
  • -Add pii_hmac_secret (your HMAC secret for pseudonym generation) to AuditClient
  • -Add encryption_public_key (your X25519 public key for token map encryption) to AuditClient
  • -Optionally add pii_fields to declare additional PII fields beyond auto-detected patterns
0.4.0Feature2026-04-15

Response capture with automatic PII redaction

  • -New response field on AuditEvent — captures the AI agent's response text (truncated to 5,000 chars)
  • -Automatic PII redaction: email addresses, SSN, TFN, credit card numbers, and phone numbers are redacted from response text and parameter values before signing and storage
  • -outputs_hash is computed on the full response before truncation — cryptographic integrity preserved
  • -LangChain integration: tool output text captured automatically as response
  • -CrewAI integration: task output captured automatically as response
  • -Dashboard: conversation timeline now shows the AI's response bubble below tool calls
  • -Admin: Event Inspector shows response in the Action layer
  • -Auto-migration adds response column to the analytics store on startup
  • -Zero code changes required — the SDK already intercepts function return values
0.3.1Fix2026-04-15

Prompt field wired end-to-end

  • -prompt parameter added to @audit_tool decorator (Python) and auditTool options (TypeScript)
  • -Backend: schema, ingest, queries, search, and export all include prompt
  • -Consumer: prompt field included in analytics-store inserts
  • -Dashboard and admin: prompt displayed in event detail panel
  • -Auto-migration adds prompt column to existing analytics-store tables on startup
0.3.0Feature2026-04-15

LangChain & CrewAI integrations, prompt capture

  • -New prompt field on AuditEvent — captures the user question that triggered each tool call
  • -LangChain integration: WytnessCallbackHandler automatically logs prompts, tool calls, duration, and errors
  • -CrewAI integration: WytnessCrewCallbacks with LangChain callback support plus native step_callback/task_callback
  • -Both frameworks are optional dependencies — install with pip install wytness-sdk[langchain] or wytness-sdk[crewai]
  • -Integration errors are logged and swallowed — never crashes the agent
0.2.3Fix2026-04-15

Automatic fallback replay on API recovery

  • -Events buffered in the fallback file during an API outage are now automatically replayed when the API recovers
  • -Replay triggers after each successful HTTP emit, with a 10-second cooldown to avoid hammering the API
  • -Successfully replayed events are removed from the file; failed ones stay for the next attempt
  • -Fallback file is deleted once fully drained
  • -No data loss during transient API outages — all events eventually reach the platform
0.2.2Feature2026-04-15

flush(), hash_value export, session_id consistency

  • -New flush() method — wait for pending HTTP requests before process exit (serverless, Lambda, edge)
  • -Exported hash_value() / hashValue() — compute SHA-256 hashes for manual event recording (inputs_hash, outputs_hash)
  • -Python: session_id is now a public read-only property (was private _session_id)
  • -Improved docs: serverless cold-start guidance, signing key generation, manual recording with hash helpers
  • -AuditClient reference page: new Methods section, expanded serverless guide, direct recording examples
0.2.1Feature2026-04-15

Async support, serverless keys, improved docs

  • -Decorator now works with async functions in both Python and TypeScript
  • -record() is guaranteed to never throw — errors are logged and swallowed
  • -New signing_key / signingKey parameter for serverless environments (base64 key, no filesystem needed)
  • -Rewritten AI Integration Instructions with framework-specific examples
  • -Added LangChain and CrewAI reasoning_summary extraction examples
  • -Documented session_id vs task_id semantics
  • -Documented AuditEvent required vs optional fields
  • -Added local development pattern (no API key needed)
0.2.0Breaking2026-04-15

HTTP transport, Kafka removed

  • -HTTP ingest is now the primary transport — events POST directly to api.wytness.ai
  • -Automatic local file fallback when the API is unreachable (./audit_fallback.jsonl)
  • -Removed Kafka dependencies (kafka-python-ng for Python, kafkajs for TypeScript)
  • -Lighter install — fewer dependencies, smaller bundle
  • -TypeScript: auditTool() now supports the direct calling style: auditTool(client, fn, { toolName, taskId })
  • -TypeScript: the legacy curried style auditTool(client)(fn, name) still works
  • -Parameter names aligned to documentation: http_api_key / httpApiKey and http_endpoint / httpEndpoint
  • -New signing_key / signingKey parameter — load Ed25519 key from base64 string instead of a file path
  • -Serverless support — works in Lambda, Vercel Functions, and Edge environments with no filesystem
Migration guide
  • -Replace kafka_bootstrap_servers / kafkaBootstrapServers with http_api_key + http_endpoint
  • -Replace kafka_topic / kafkaTopic — no longer needed
  • -Python: api_key is now http_api_key, api_url is now http_endpoint
  • -If you were using Kafka directly, switch to HTTP ingest or contact us for self-hosted options
0.1.0Feature2026-03-28

Initial release

  • -Ed25519 event signing with automatic key generation
  • -SHA-256 hash chaining for tamper detection
  • -Automatic secret redaction (keys, tokens, passwords)
  • -Python SDK: AuditClient + @audit_tool decorator
  • -TypeScript SDK: AuditClient + auditTool wrapper
  • -Published to PyPI (wytness-sdk) and npm (@wytness/sdk)

We set no cookies. Sign-in and preferences use essential first-party browser storage only — no tracking, advertising, or third-party analytics. Privacy Policy

Changelog
TABLE OF CONTENTS