@takk/glasshouse - v1.0.0 - Apache-2.0

See every agent in your swarm think, call, and spend.

Wrap each agent in a span and get a sovereign, tamper-evident, signable trace you can audit on your own infrastructure, including the EU. Glasshouse is the glass house to the closed box: it exposes the agent graph, the per-step decisions, and the cost, OpenTelemetry-native, where a closed multi-agent endpoint hides which model answered and the path it took.

115tests passing
88.48%coverage
0runtime deps
13alert channels
What it is

One library, the whole swarm under glass.

In a few words

Glasshouse is a real-time x-ray of a Massive Intelligence (IM) swarm. You wrap each agent step in observe() and Glasshouse records the span, builds a renderable agent graph, watches the stream for drift, attributes failures to the culprit agent, and seals a tamper-evident audit chain. Your code is two lines: const gh = createGlasshouse(), then gh.observe(name, fn, { kind }).

Technically

Each observe() call emits an OpenTelemetry-compatible span tagged with a kind (agent, reasoning, llm, tool, memory, state, decision, verification, handoff) and becomes a node in the agent graph, which renders to Mermaid or DOT with cycle detection. Streaming detectors (z-score, EWMA, Page-Hinkley, PSI) flag drift, loop detection catches repeats and ping-pong, and the run can be sealed into a SHA-256 audit chain with Ed25519-signed snapshots. The core is node-free over Web Crypto.

Before and after

The same swarm, two very different views.

Take a real production scene: a swarm of agents answers unevenly and runs up a bill, so you want to see which agent answered, the path it took, where it drifted, and what it spent, under your control.

A closed observability box

One opaque box that hides which model answered and the path it took

  1. Black box You cannot see which model answered or audit the path it took through the swarm.
  2. No graph There is no agent graph to render, diff, or review after a run.
  3. No drift A degrading agent slips by; nothing watches the stream for drift or loops.
  4. No attribution A failure is reported, but no one names the culprit agent.
  5. Sovereignty A vendor sits in the middle of every trace, and the EU and EEA may be blocked.
  6. Cost Token spend is hidden, so a runaway agent surprises you on the invoice.
  7. Result Telemetry you cannot inspect, on infrastructure you do not own.
With Glasshouse

Open observability, the glass house shows you exactly how the swarm behaved

  1. Trace Wrap each step in observe() and read an OpenTelemetry-compatible span per agent action.
  2. Graph Every run renders to a Mermaid or DOT agent graph with cycle detection.
  3. Drift Streaming detectors flag drift; loop detection catches repeats and ping-pong.
  4. Attribution A failure names the culprit agent, and 13 alert channels can fire.
  5. Sovereignty It runs in your process, on your infrastructure, with the EU included.
  6. Cost The per-step token spend is on every span, so a runaway agent is visible at once.
  7. The SHA-256 audit chain seals every run, verifiable, for the compliance review.

Open observability is the difference between an opaque telemetry box and a glass house; it keeps the spans, the agent graph, the drift signals, and the cost in your hands, on your infrastructure, with every step exposed and OpenTelemetry-native.

Install

Five minutes from install to your first auditable run.

1. Add the package

pnpm add @takk/glasshouse
npm install @takk/glasshouse
yarn add @takk/glasshouse
bun add @takk/glasshouse

2. Zero required dependencies

The core has no runtime dependencies and a node-free core. Every @takk sibling is an optional peer; install only the companions you observe alongside.

# nothing else is required to start experimenting
              # optional: install a companion you observe alongside, for example
              pnpm add @takk/caduceus @takk/coryphaeus

3. Observe a swarm in a few lines

import { createGlasshouse } from '@takk/glasshouse';

              const gh = createGlasshouse();

              // Wrap each agent step; the span kind labels what the step did.
              await gh.observe('plan', (span) => {
              /* work */
              }, { kind: 'agent', agentId: 'orchestrator' });

              console.log(gh.mermaid()); // render the agent graph to Mermaid

4. Expose the trace as tools a non-human entity can call

import { createTraceTools } from '@takk/glasshouse/adapter';

              // Framework-agnostic descriptors (name, description, JSON Schema, handler) that
              // match what MCP servers and tool-calling APIs expect: glasshouse_trace,
              // glasshouse_graph, and glasshouse_query.
              const tools = createTraceTools(gh);
              const result = await tools.glasshouse_query.handler({
              span: 'agent',
              agentId: 'orchestrator',
              });
              // the host owns the live trace; the caller only asks questions of it
Features

Nine capabilities, every one tied to a measurable outcome.

OpenTelemetry span tracing

An observe() wrapper records an OpenTelemetry-compatible span per step, with span kinds agent, reasoning, llm, tool, memory, state, decision, verification, and handoff to label what each step did.

Trace the swarm in the standard you already collect, not a proprietary format you have to adopt.

Renderable agent graph

Every run is a graph: a node per span with its kind, agent, and cost, and an edge per handoff. It renders to Mermaid or DOT, with cycle detection over the whole trace.

Render, diff, and review exactly how the swarm behaved, instead of a black-box trace you cannot inspect.

Streaming drift detection

Streaming detectors watch the span stream for drift: z-score, EWMA, Page-Hinkley, and PSI, each declarable and tuned to the signal you care about.

Catch a degrading agent while it drifts, instead of reading about it after the incident.

Local-first sovereignty

Observability runs in your process, on your infrastructure. The core makes no model calls of its own; no vendor sits in the middle of every trace, and the EU and EEA are not blocked.

Watch the swarm where a closed, region-locked observability box cannot follow you.

Failure attribution and loops

When a run fails, Glasshouse names the culprit agent from the graph. Loop detection catches repeats and ping-pong, and 13 alert channels can fire on the signal.

Get the agent to blame and the loop it was stuck in, instead of a flat error with no owner.

Sampling and redaction

Trace sampling keeps the volume you can afford, and redaction runs before export so sensitive fields never leave your process. You choose the trade per trace.

Tune cost against fidelity yourself, and keep secrets out of every exported span.

Tamper-evident audit

An append-only SHA-256 hash chain seals every run, and snapshots are signed with Ed25519, both over the Web Crypto API, never node:crypto.

Prove a trace was not altered after sealing, in Node, edge runtimes, or the browser, when a run is questioned.

Exporters everywhere

Edge OTLP and fetch exporters ship spans from Cloudflare Workers, Vercel Edge, Bun, and Deno; a Node file exporter writes them to disk. MCP trace tools expose the live trace to a non-human entity.

Send the trace wherever it needs to go, from any runtime, instead of a single fixed sink.

SLSA provenance

Every published version signed with npm publish --provenance through GitHub Actions OIDC. Lockfile committed, zero required runtime dependencies, node-free core.

Verify in one command that the tarball you installed was built from the source commit you trust.

Model

Span kinds, no black box.

Every observe() call carries a span kind that labels what the step did. There are nine kinds in all; each is declarable and inspectable, and becomes a typed node in the agent graph.

Span kind What it is When you reach for it
agent A whole agent turn, the outer span that wraps an agent's reasoning, tool calls, and handoffs. Marking where one agent in the swarm starts and ends.
reasoning A thinking step inside an agent, captured so the path it took is visible in the graph. Recording how an agent decided, not just what it answered.
llm A model call with its token counts and cost, the unit that the drift detectors and cost tally read. Every call to a model, so spend and drift are on the span.
tool A tool or function invocation; memory, state, decision, verification, and handoff round out the nine kinds. Any side effect, lookup, or handoff you want named in the trace.

The nine span kinds are agent, reasoning, llm, tool, memory, state, decision, verification, and handoff; each maps to an OpenTelemetry span, so the trace renders to a graph and exports to any OTLP backend you run.

Entry points

Subpaths, import only what you need.

Entry point Subpath export Runtime Use it when
Toolkit barrel @takk/glasshouse node-free You want observe(), the agent graph, drift detection, failure attribution, audit, and the adapters.
Edge exporters @takk/glasshouse/edge node-free You want the OTLP and fetch exporters for Cloudflare Workers, Vercel Edge, Bun, or Deno.
Trace tools adapter @takk/glasshouse/adapter node-free You want the MCP trace tools (glasshouse_trace, glasshouse_graph, glasshouse_query) for an MCP server.
Node file exporter @takk/glasshouse/node Node only You need to write spans to disk, or load a saved trace or run graph from JSON files.
CLI

A command-line tool that runs the real tracer.

The glasshouse binary runs the compiled engine. graph renders a saved trace to DOT or Mermaid; drift runs the detectors over a span file; attribute names the culprit agent for a failed run; audit-verify checks a sealed chain. Live traces use the library API with observe().

Render a saved trace as a graph

# render the agent graph from a saved trace, with cycle detection
              npx @takk/glasshouse graph trace.json --format mermaid

Run drift detection, then attribute a failure

# scan a span file for drift, then name the culprit agent of a failed run
              npx @takk/glasshouse drift trace.json --detector page-hinkley
              npx @takk/glasshouse attribute trace.json
          

Verify a chain, inspect version and help

npx @takk/glasshouse audit-verify chain.json
              npx @takk/glasshouse --version

              # exit codes: 0 ok, 2 usage or input, 10 drift detected, 20 broken chain
Audit and transparency

Prove what was traced, and read the whole graph.

Seal every run into a tamper-evident, hash-chained log, and sign a snapshot with Ed25519. And because each trace is explicit, the run carries the spans, the per-step cost, the agent graph, and the drift signals, so the swarm is never a black box. Both are node-free over Web Crypto and need no external service.

import { AuditChain, verifyChain } from '@takk/glasshouse/audit';

              const chain = new AuditChain();

              // Seal each run into the chain as it completes, then record a decision.
              await chain.append(gh.snapshot());
              await chain.append({ kind: 'decision', approved: true });

              await verifyChain(chain.toArray()); // { valid: true }, until any entry is altered

Every trace is explicit

const trace = gh.export();
              // {
              //   run: 'a1b2c3...',
              //   spans: [ ... ],                                 // one OpenTelemetry span per step
              //   graph: { nodes: [ ... ], edges: [ ... ] },      // the renderable agent graph
              //   cost: { costUsd: 0.0031, inputTokens: 494, ... },
              //   drift: { detector: 'page-hinkley', flagged: false },
              //   attribution: { culprit: null }                  // set on a failed run
              // }

The trace carries the spans, the per-step cost, the agent graph, and the drift signals, so you can see how the swarm behaved and what it spent. Seal each run into the audit chain with no runtime dependency, the governance seam for a non-human entity that watches a swarm of models it operates.

Compare

Glasshouse vs the alternatives.

The other approaches solve part of the problem. The contrast clarifies where Glasshouse sits.

Capability Glasshouse Closed box (Fugu) Agent framework OpenTelemetry SDK Hand-rolled
Distribution npm library hosted service npm library npm library your repo
Transparent agent graph renderable graph black box hand-wired spans only varies
Drift and loop detection built in hidden, if any none none none
Failure attribution names the agent flat error manual no varies
OpenTelemetry-native yes proprietary varies yes varies
Local-first sovereignty your infra region-locked yes yes yes
Tamper-evident audit yes no no no no
Zero deps, node-free yes n/a deps deps varies
License Apache-2.0 proprietary mixed Apache-2.0 your call

The honest summary: a closed box is fine when you do not need to see inside, and a raw OpenTelemetry SDK is fine when spans are all you want. Pick Glasshouse when you want the agent graph, drift and loop detection, failure attribution, sovereignty over where it runs, and a tamper-evident trail, all in one node-free TypeScript library you embed in production.

The tracer, honestly

What the tracer assumes, and how it stays honest.

Glasshouse is transparent observability over your own swarm, and that has boundaries. They are documented, and where reality bends them, there is an explicit choice rather than a silent fudge.

Assumption or property What it means How Glasshouse handles it
You instrument the swarm Glasshouse sees only the steps you wrap in observe(); it does not auto-discover untraced calls. Wrap each agent step and tag a span kind; an unwrapped call simply does not appear in the graph, by design.
Observability, not control Glasshouse watches the swarm; it does not route, retry, or call any model itself. It records spans, builds the graph, and detects drift; control belongs to your orchestrator, not to the tracer.
Nine span kinds A span is one of agent, reasoning, llm, tool, memory, state, decision, verification, or handoff. The fixed taxonomy keeps the graph typed and renderable; arbitrary custom kinds are a documented non-goal for 1.0.
Cost is read from the span Per-step cost comes from the token counts your client reports on the llm span. When no counts are reported a step shows zero cost; the tally never invents a number it cannot read.
Statistical drift detection Drift is flagged by z-score, EWMA, Page-Hinkley, or PSI over the span stream. A detector reports a signal, not a verdict; you set the threshold and decide what a flag means for your run.
Attribution from the graph The culprit agent is inferred from the failed run's graph, not asserted by the agents. It is read off the edges by walking back from the failing node, so attribution is grounded in the trace.
Redaction before export Sensitive fields are stripped before a span leaves the process. Redaction runs in the export path, so an exporter never ships a field you marked for redaction.
Integrity, not identity The audit seal proves a log was not altered after sealing. A SHA-256 hash chain via Web Crypto, an integrity seal; Ed25519 signs a snapshot for stronger evidence.
Quality and validation

The receipts behind v1.0.0.

Tests & coverage

115 tests passing under Vitest 4 with coverage about 88.48% of statements, across 22 suites on the Node 20, 22, and 24 matrix. Runnable examples and a benchmark trace against the built artifact end to end. Run pnpm test on a fresh clone to reproduce.

Type safety

TypeScript in maximum strict mode (exactOptionalPropertyTypes, useUnknownInCatchVariables, noUncheckedIndexedAccess, noImplicitOverride, noImplicitReturns). Zero errors under tsc --noEmit.

Lint & types-correctness

Biome clean across src and tests. publint clean and @arethetypeswrong/cli green across every subpath. Dual ESM + CJS with separate .d.ts and .d.cts per subpath.

Behavior, verified

Each span kind lands on the right node, the agent graph detects a cycle, every drift detector flags its planted change, failure attribution names the seeded culprit agent, and the audit chain catches a tampered entry at exactly its index. Every invariant is a test.

Distribution smoke

A smoke test exercises the compiled ESM and CJS artifacts and spawns the compiled CLI as a single Node process, run on the Node 20, 22, and 24 matrix.

Supply chain

Committed pnpm lockfile, zero required runtime dependencies, node-free core, and SLSA provenance attestation on every published version. Verify with npm view @takk/glasshouse@1.0.0 --json | jq .dist.attestations.

Roadmap

What is shipped, what is next, what is later.

Now (1.0)

Shipped in v1.0.0

  • OpenTelemetry span tracing with observe() and nine span kinds
  • Renderable agent graph, Mermaid and DOT, with cycle detection
  • Drift detection (z-score, EWMA, Page-Hinkley, PSI) and loop detection
  • Failure attribution, 13 alert channels, trace sampling, redaction
  • SHA-256 audit chain, Ed25519-signed snapshots, MCP trace tools
  • Edge OTLP and fetch exporters, a Node file exporter, a self-contained HTML viewer, Glasshouse CLI
  • Dual ESM + CJS, node-free core, SLSA provenance
Next

Planned next

  • More OTLP backends and a hosted span viewer
  • Auto-instrumentation helpers for common agent frameworks
  • Adaptive drift thresholds tuned per signal
  • Richer per-agent cost and latency dashboards
  • Timestamped and notarized audit seals
Later

On the horizon

  • Deep integration with the transport satellite (caduceus)
  • Trace replay and time-travel over a sealed run
  • A detector and alert-channel marketplace
  • Hosted, multi-swarm observability dashboards
FAQ

Common questions.

Is Glasshouse production-ready at 1.0.0?

Yes. 115 tests pass across 22 suites under Vitest 4 with coverage about 88.48% of statements; TypeScript maximum strict mode is clean; Biome lint is clean; publint and @arethetypeswrong/cli are clean across every subpath. The suite and the distribution smoke test are run on the Node 20, 22, and 24 matrix. Every published release carries SLSA provenance produced by GitHub Actions.

Why not just read the raw spans yourself?

You can, and a raw OpenTelemetry SDK gives you spans. But a swarm is a graph, not a flat list: you want to see which agent handed off to which, where a run looped, and which agent to blame when it failed. Glasshouse turns the spans into a renderable agent graph, runs drift and loop detection over the stream, and attributes failures, so you read behavior instead of scrolling a span dump.

Why not use a closed observability box?

You can, and many do. The cost is a black box that hides which model answered and the path it took, on infrastructure you do not own and that may block the EU. Glasshouse is the glass house: it exposes the agent graph, the per-step decisions, and the cost, it is OpenTelemetry-native, and it runs in your process on your own infrastructure, so the trace is yours to inspect.

Does Glasshouse call any model itself?

No. Glasshouse is observability over your own swarm. You wrap each agent step in observe() and it records the span, builds the graph, watches for drift, and seals the audit chain. It makes no model calls of its own, which keeps it dependency-free and testable with no network, and it means your provider keys live in your client, never in Glasshouse.

Does this work in Cloudflare Workers, Vercel Edge, Bun, or Deno?

Yes. The core is node-free; it uses the Web Crypto API for SHA-256 and Ed25519, not node:crypto. Import @takk/glasshouse or @takk/glasshouse/edge on any runtime with Web Crypto, and ship spans with the OTLP or fetch exporter. Only @takk/glasshouse/node, the file exporter and JSON loaders, requires the Node standard library.

How does Glasshouse handle my data?

Glasshouse holds the spans you record, the graph it builds, and the audit chain it seals, nothing else. It makes no outbound network calls of its own beyond the exporter you configure, and redaction runs before any span leaves the process. With the file loaders, only the trace or run files you point it at are read from disk, and no provider credentials are ever held by Glasshouse.

What does a span kind actually mean?

A span kind labels what a step did, so the graph is typed and the detectors know what to read. The nine kinds are agent, reasoning, llm, tool, memory, state, decision, verification, and handoff. You pass the kind to observe(), and Glasshouse uses it to render the node, attribute a failure, and tally cost on the llm spans. Read the kinds off the graph to see exactly how the swarm behaved.

Is the audit trail a digital signature?

The chain itself is an integrity seal: each entry is chained to the previous one through a SHA-256 hash of its canonical form, so any later edit breaks the chain. On top of that, a snapshot can be signed with Ed25519 over the Web Crypto API, which does prove who produced it. Use the hash chain for tamper-evidence and the signature when you need third-party attribution.

Which providers and frameworks does it support?

Any of them. Because you call observe() around your own steps, Glasshouse traces a swarm built on OpenAI, Anthropic, Google, a local model, or any framework, treated uniformly as agents in the graph. Spans are OpenTelemetry-compatible, so they export to any OTLP backend you already run.

Where does the state live?

In-process memory by default, with portable JSON snapshots via snapshot() and fromSnapshot(), the spans and the scalar configuration. For disk-backed output, write spans or load a saved trace from JSON with @takk/glasshouse/node. For an edge runtime, snapshot to your own KV store between invocations, or stream out through the OTLP exporter.

How does it keep cost visible?

Every llm span carries the token counts your client reports, so the per-step dollar cost is on the node and the run-level tally sums it. A runaway agent shows up at once as a costly node in the graph. Glasshouse does not cap spend itself, it makes it visible, so your orchestrator can act on the signal, and trace sampling keeps the telemetry volume affordable.

How do I verify a published version's provenance?

Every release is published with npm publish --provenance. Check the attestations with npm view @takk/glasshouse@<version> --json | jq .dist.attestations. The attestation links the tarball you installed to the GitHub Actions workflow that built it from a specific source commit.

What is the policy on breaking changes?

Strict SemVer 2.0.0, starting from 1.0.0. The binding stability surface is documented in SPEC.md section 5. Major bumps require a deprecation cycle; security fixes follow the disclosure flow in SECURITY.md.

Author

Built and maintained by David C Cavalcante.

David C Cavalcante

Founder, Takk Innovate Studio

Product Engineer, ML Engineer, LLM Engineer, LLM Architect, Massive Intelligence (IM) Researcher. Builder of the @takk family of NPM packages for Massive Intelligence (IM) native infrastructure.

Glasshouse is part of a planned portfolio of NPM libraries targeting Massive Intelligence (IM) native infrastructure for 2026 to 2030. Adjacent research by the author covers systemic intelligence frameworks (MAIC, HIM, NHE) published independently of this codebase, with research notes on PhilPapers and PhilArchive linked from the repository README.

If Glasshouse gave you a real-time x-ray of your swarm this quarter, the most useful thing you can do is open a GitHub issue when you find an edge case the test suite missed. The runbook for releases, the threat model, and the contributor agreement all live in the repository.