v1.6.0 — Observable Runtime
Observable Runtime
Every Deuz run now emits a versioned observation event protocol (ObserveEvent, schemaVersion: 1) through the new Dependencies.observer seam — local-first, zero runtime dependencies, no hosted service, no OpenTelemetry dependency, and nothing recorded by default.
What you can see
- Runs & models — start/end, provider/model/surface, TTFT (tool-call-first responses now count), per-retry reason + backoff delay, finish reason, full token usage, sync/async USD cost via
priceProvider - Agent steps — effective per-step model (post-
prepareStep), per-step duration (new), per-step + cumulative usage,endReasonincl. the previously invisible runaway-tool-error stop - Tools — per-call timing in real parallel completion order, original thrown errors captured at the only site that still has them, machine-readable denial causes
- Approvals — requested/resolved with source (
server/client-response/default-deny) and resume wait time - Durable runs — checkpoint saves/loads/failures; one
runIdacross suspend/resume legs, freshexecutionIdper leg,checkpoint.loadedbeforerun.started { resumed: true } - Compaction — per-layer events with estimate token + message counts; the summarize side-call is a visible, tagged model call (usage counted exactly once)
- Sub-agents — full tree under the parent
runId, keyed byagentPath, with durablechildRunId
New subpaths
import { createMemoryObserver, summarizeRun } from '@deuz-sdk/core/observe';
import { createJsonlObserver, readJsonlEvents } from '@deuz-sdk/core/observe/node';
const observer = createMemoryObserver();
await generateText({ model, messages, tools, maxSteps: 5, deps: { observer } });
console.log(summarizeRun(observer.latestRun() ?? []));Built-ins: createMemoryObserver · createCallbackObserver · composeObservers · filterObserver · summarizeRun · Node JSONL persistence (one valid JSON line per event, binary-safe, bounded queue — a slow disk can never affect a run).
Privacy & safety guarantees
- Events carry only counts/ids/names/durations/enums; prompt/tool/reasoning/error content is opt-in per field and always passes a
[REDACTED]redaction profile + structural limits (regression-tested against planted secrets in every channel) - Observers can never break a run: isolated, never awaited; with no observer the hot path pays a single boolean branch and draws no ids
- Deterministic per-run sampling (
sampleRate) — a run is all-in or all-out streamChatstays synchronous and lazy (G2); exactly one terminal event per execution leg
Tracer bridge — the span hierarchy, completed
An injected Dependencies.tracer now receives the documented invoke → step → execute_tool hierarchy driven by the same events (1.5 emitted only flat per-model-call invoke spans). Span names and attribute keys are unchanged; agentic loops now produce one invoke with step/tool children instead of N flat invokes. A user abort still ends the span without an exception.
Behavior fix
A tool-call-first response now clears the TTFT timer — previously only text/reasoning deltas did, so a tool-first stream could falsely trip the 60-second ttft timeout.
Quality: 558 tests (147 new across 14 observation suites, including the repo's first span tests and a P0 secret-leak matrix), full 10-stage npm run check + docs build green. Bundle budgets raised once with measurement: core 86 000 → 100 000 raw bytes (measured 97.7 KB fully instrumented), edge 76 000 → 90 000.
Docs: Observability guide · Event catalog · Design spec (code-verified) · CHANGELOG