Skip to content

v1.6.1

Choose a tag to compare

@U-C4N U-C4N released this 13 Jul 19:46
· 40 commits to main since this release

Observability Hardening

A patch release closing every actionable finding from the post-1.6.0 adversarial review: two security fixes in the observation pipeline, plus small additive controls for draining, budgeting and tracing. No public type changes beyond append-only additions.

Security fixes

  • Redaction final barrier. The built-in observation redaction profile now also runs after your custom redact hook and after structural truncation — a buggy or malicious redactor can no longer reintroduce a secret into an event, and truncation can never split a secret into a decodable prefix. The JWT pattern additionally catches tokens embedded mid-string. (maskSecret and every 1.5 log/error path are untouched — the P0 pin holds.)
  • Composite observers: per-sink capture projection. composeObservers children now each receive only what their own capture options allow: disallowed captured* fields are stripped, error.message is gated per child on capture.errorMessages, and a child redact hook applies only to that child's view.

Important

Behavior change (security-motivated): a composed observer with no options no longer receives captured content from its siblings' opt-ins — it now matches a standalone observer's privacy defaults. If a sink should see prompts/tool payloads, give it its own capture options.

Additive

  • result.observation?.settled on generateText / streamChat / embed / embedMany — await it before observer.close() so async cost.calculated enrichments (an async priceProvider) aren't dropped. Nothing about the run itself ever waits on it (G2 holds).

    const res = await generateText({ model, messages, deps: { observer } });
    await res.observation?.settled;
    await observer.close(); // JSONL now contains cost.calculated
  • createMemoryObserver({ maxBytes }) — a total byte budget alongside maxEvents, evicting by the existing overflow strategy (drop-oldest default) and counting into droppedCount.

  • deps.tracerMode: 'legacy' — opt back into the 1.5 flat span topology (one parent-less invoke per model call, deuz.step.count: 1, retries on that call's own span) for dashboards built against it. The default remains 'hierarchical'.

Notes

  • eventId is now derived as `${executionId}:${sequence}` — one fewer id draw per event; the format was never part of the contract.
  • Release workflow: actions/*@v5, an idempotent GitHub-release step, and dual-auth publish — NPM_TOKEN if present, otherwise npm trusted publishing (OIDC); --provenance on both paths (npm pinned to the 11 line — npm 12 changed pack --json output).

Quality: 575 tests / 63 files (17 new: malicious-redactor + truncation-boundary secret matrix, per-sink projection splits, settled→close JSONL drains, legacy-tracer span shape), full 10-stage npm run check + docs build green. Bundle budgets not raised (core 99 547 / 100 000 raw bytes).

Docs: Observability guide · Event catalog · CHANGELOG

Full Changelog: v1.6.0...v1.6.1