{"_id":"@attestry/otel-agent-compliance","name":"@attestry/otel-agent-compliance","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.1":{"name":"@attestry/otel-agent-compliance","version":"0.1.1","description":"OpenTelemetry SpanProcessor that emits Attestry ABDR records for compliance record-keeping","license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/attestry-ai/attestry-sdk.git","directory":"packages/otel-agent-compliance"},"type":"module","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","dev":"tsc --watch","test":"vitest run","test:watch":"vitest","prepare":"npm run build"},"dependencies":{"@opentelemetry/api":"^1.9.0","@opentelemetry/sdk-trace-base":"^2.6.1"},"peerDependencies":{"@opentelemetry/sdk-node":"~0.214.0"},"peerDependenciesMeta":{"@opentelemetry/sdk-node":{"optional":true}},"devDependencies":{"@types/node":"^22.0.0","typescript":"^5.7.0","vitest":"^3.0.0"},"engines":{"node":">=18.0.0"},"_id":"@attestry/otel-agent-compliance@0.1.1","gitHead":"9f13fc287a6d4fa0b6afabd133d60d9e76dfc74d","bugs":{"url":"https://github.com/attestry-ai/attestry-sdk/issues"},"homepage":"https://github.com/attestry-ai/attestry-sdk#readme","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-wfBXmSWZDwI8iNoTvfIcq8TKpqG9UsvJQH7JYmn6ElDsTLifg+l69UONVOGpa7vrDAcaJBpr3SwKm7Wzih26JA==","shasum":"32c8cc9c2de86d727c2376597cb505bf20fb1041","tarball":"https://registry.npmjs.org/@attestry/otel-agent-compliance/-/otel-agent-compliance-0.1.1.tgz","fileCount":31,"unpackedSize":97046,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@attestry%2fotel-agent-compliance@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGDQnoS/Xw8f04nKbY6RYo2VscisXB6OwIF7EVOtCAIuAiA11nIN3i0Skj3VkbFgPNURFWptVjFOa9nylg/53EbtSw=="}]},"_npmUser":{"name":"alerterra","email":"lexwhiting@gmail.com"},"directories":{},"maintainers":[{"name":"alerterra","email":"lexwhiting@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/otel-agent-compliance_0.1.1_1780771183913_0.26376065730410625"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-06T18:39:43.760Z","0.1.1":"2026-06-06T18:39:44.096Z","modified":"2026-06-06T18:39:44.540Z"},"maintainers":[{"name":"alerterra","email":"lexwhiting@gmail.com"}],"description":"OpenTelemetry SpanProcessor that emits Attestry ABDR records for compliance record-keeping","homepage":"https://github.com/attestry-ai/attestry-sdk#readme","repository":{"type":"git","url":"git+https://github.com/attestry-ai/attestry-sdk.git","directory":"packages/otel-agent-compliance"},"bugs":{"url":"https://github.com/attestry-ai/attestry-sdk/issues"},"license":"Apache-2.0","readme":"# @attestry/otel-agent-compliance\n\nOpenTelemetry SpanProcessor that emits Attestry ABDR (Agent-Based Decision Record) entries for every AI tool call, LLM completion, retrieval, or policy evaluation that flows through your tracer. Every recorded decision becomes part of a hash-chained, cryptographically-verifiable compliance ledger you can replay against EU AI Act, NIST AI RMF, and ISO 42001 audits.\n\n## Install\n\n```bash\nnpm install @attestry/otel-agent-compliance @opentelemetry/sdk-node\n```\n\n`@opentelemetry/sdk-node` is a peer dependency. Install it alongside if you don't have it already.\n\n## Quick start\n\n```ts\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { AttestryDecisionExporter } from \"@attestry/otel-agent-compliance\";\n\nconst sdk = new NodeSDK({\n  spanProcessors: [\n    new AttestryDecisionExporter({\n      apiUrl: \"https://attestry.app/api/v1/decisions/bulk\",\n      apiKey: process.env.ATTESTRY_API_KEY!,\n      systemId: process.env.ATTESTRY_SYSTEM_ID!,\n    }),\n  ],\n});\n\nsdk.start();\n\nprocess.on(\"SIGTERM\", async () => {\n  await sdk.shutdown(); // flushes the in-memory queue\n});\n```\n\n## What gets recorded\n\nBy default, the exporter records a span as a decision when ANY of the following is true:\n\n| Signal | Source |\n|---|---|\n| `ai.operation` attribute set | OpenInference / OpenTelemetry semantic conventions |\n| `gen_ai.operation.name` attribute set | OTel GenAI semantic conventions |\n| `tool.name` attribute set | LangChain / LlamaIndex tool spans |\n| `policy.outcome` or `decision.outcome` attribute set | Customer policy engines |\n| Span name contains `tool`, `llm`, `completion`, or `policy` | Heuristic on framework span names |\n\nSpans that don't match are dropped without making a network call. Override with `shouldRecordAsDecision`.\n\n## What lands on the wire\n\nEach decision posted to `/api/v1/decisions/bulk` contains:\n\n- `systemId` — the Attestry system this exporter is attached to\n- `inputDigest` — `sha256:` of the canonical-form span input (sorted keys)\n- `outputDigest` — `sha256:` of the canonical-form span output, when present\n- `frameworkClaims[]` — heuristic mappings to EU AI Act / NIST AI RMF / ISO 42001 articles\n- `toolInvocations[]` — `tool.name` if set\n- `policyOutcome` — `permitted` / `denied` / `escalated` if recognized\n- `idempotencyKey` — the OTel span ID (replay-safe)\n\n**The exporter never sends raw span input or output text.** Only digests cross the wire by default. Tools, prompts, and completions stay inside your process.\n\n## PII sanitizer\n\nThe default sanitizer redacts emails, phone numbers, US SSNs, credit card numbers, and IPv4 addresses inside any string-valued span attribute. Override with `config.sanitizer` to plug in HIPAA / GDPR special-category rules:\n\n```ts\nnew AttestryDecisionExporter({\n  // ...\n  sanitizer: (attrs) => {\n    const out = { ...attrs };\n    delete out[\"http.request.body\"]; // never ship request bodies\n    return out;\n  },\n});\n```\n\n## Framework tagger\n\nHeuristic mapping from span context to `frameworkClaims[]`. Customers with curated taxonomies override it:\n\n```ts\nnew AttestryDecisionExporter({\n  // ...\n  frameworkTagger: ({ name, attributes }) => {\n    if (attributes[\"custom.tag\"] === \"loan-decision\") {\n      return [\n        {\n          framework: \"EU AI Act\",\n          article: \"Annex III §5(b)\",\n          claim: \"Creditworthiness evaluation for natural persons.\",\n        },\n      ];\n    }\n    return [];\n  },\n});\n```\n\n## Configuration reference\n\n| Option | Default | Description |\n|---|---|---|\n| `apiUrl` | (required) | Full URL of the bulk ingest endpoint |\n| `apiKey` | (required) | API key with `write:assessments` permission |\n| `systemId` | (required) | Attestry system UUID |\n| `batchSize` | `20` | Flush trigger — records per batch |\n| `batchInterval` | `5000ms` | Flush trigger — max delay before partial flush |\n| `fetchTimeoutMs` | `10_000` | Per-request abort timeout |\n| `sanitizer` | regex stripper | Override PII sanitization |\n| `frameworkTagger` | heuristic mapping | Override claim generation |\n| `shouldRecordAsDecision` | heuristic | Override decision detection |\n| `logger` | console (stderr) | Wire pino/winston/etc. via `warn`/`error` |\n\n## Failure mode\n\nThe exporter never throws to your application. Network errors, 5xx responses, and timeouts go through three retries with capped exponential backoff (250ms → 750ms → 2_250ms + jitter), then log via the configured logger and drop the batch. 4xx responses (other than 429) are treated as permanent — no retry, log + drop.\n\n## Integration examples\n\n### LangChain\n\nLangChain emits OTel spans automatically when `LANGCHAIN_TRACING_V2=true` and an OTel SDK is initialized in the process. Wire the exporter and every chain step shows up in Attestry.\n\n### LlamaIndex\n\nLlamaIndex's OpenInference instrumentation tags spans with `openinference.span.kind`. The default decider catches them via the `ai.operation` / `gen_ai.operation.name` keys.\n\n### OpenAI / Anthropic SDK\n\nWrap the SDK call in a manual span and set `ai.operation` to \"completion\":\n\n```ts\nimport { trace } from \"@opentelemetry/api\";\n\nconst tracer = trace.getTracer(\"my-app\");\nawait tracer.startActiveSpan(\"openai.completion\", async (span) => {\n  span.setAttribute(\"ai.operation\", \"completion\");\n  span.setAttribute(\"input\", prompt);\n  const result = await openai.chat.completions.create({ ... });\n  span.setAttribute(\"output\", result.choices[0].message.content);\n  span.end();\n});\n```\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md","_rev":"1-de0b6730725331cb5567907b816bf24d"}