npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atensec/thoth

v0.1.18

Published

AI agent governance SDK by Aten Security

Readme

@atensec/thoth

AI agent governance SDK for JavaScript/TypeScript by Aten Security.

Thoth instruments your AI agents for governance, policy enforcement, and behavioral monitoring. Every tool call is evaluated against your organization's security policies — blocking, stepping up for human approval, or silently observing based on your configured enforcement mode.


Table of Contents

  1. Installation
  2. Quick Start
  3. Legacy ThothClient
  4. How It Works
  5. LangChain.js Example
  6. Framework Integrations
  7. Configuration Reference
  8. Error Handling
  9. Environment Variables
  10. Enforcement Modes
  11. Policy Decisions
  12. Dashboard

Installation

npm install @atensec/thoth
pnpm add @atensec/thoth
yarn add @atensec/thoth

Requirements: Node.js 18+, TypeScript 5.x (if using types)


Quick Start

1. Get your API key from your Thoth dashboard at https://<tenant>.<apex-domain> under Settings → API Keys.

2. Set environment variables:

export THOTH_API_KEY="thoth_live_..."
export THOTH_API_URL="https://enforce.<tenant>.<apex-domain>"
export THOTH_LOG_LEVEL="DEBUG" # optional; falls back to LOG_LEVEL when unset

3. Instrument your agent:

import { instrument } from "@atensec/thoth";

// Instrument your agent — returns the same object, mutated in-place
const governed = instrument(agent, {
  agentId: "document-summarizer",
  approvedScope: ["web_search", "read_file", "send_email"],
  tenantId: "your-tenant-id",
  apiUrl: process.env.THOTH_API_URL!, // required if THOTH_API_URL is not set
  userId: "[email protected]",
  enforcement: "block", // default is "block"; options: observe | step_up | block | progressive
  // apiKey reads from THOTH_API_KEY env var automatically
});

// Every tool call is now governed — no other changes required
const result = await governed.run(
  "Summarize the attached document and send it to the team.",
);

Legacy ThothClient

Module-level APIs are preferred for new integrations:

  • instrument()
  • wrapAnthropicTools()
  • wrapOpenAITools()

ThothClient remains supported for backward compatibility:

import { ThothClient } from "@atensec/thoth";

const client = new ThothClient({
  tenantId: "your-tenant-id",
  userId: "[email protected]",
});

client.instrument(agent, {
  agentId: "document-summarizer",
  approvedScope: ["web_search", "read_file"],
  apiUrl: process.env.THOTH_API_URL!,
});

// Legacy aliases:
client.wrap(agent, {
  agentId: "document-summarizer",
  approvedScope: ["web_search", "read_file"],
  apiUrl: process.env.THOTH_API_URL!,
});

How It Works

Agent calls tool
      │
      ▼
Thoth intercepts (instrument)
      │
      ├── Emits TOOL_CALL_PRE event → tenant API (async, non-blocking)
      │
      ├── Calls enforcer /v1/enforce
      │        │
      │        ├── ALLOW   → tool executes normally
      │        ├── STEP_UP → waits for human approval (polls /v1/enforce/hold/{token})
      │        └── BLOCK   → throws ThothPolicyViolation
      │
      ├── Tool executes (if allowed)
      │
      └── Emits TOOL_CALL_POST event → tenant API (async, non-blocking)

Events are emitted to the Aten ingest API asynchronously and never block tool execution. If the enforcer is unreachable, Thoth fails closed (BLOCK) and raises ThothPolicyViolation before tool execution.

Streaming tools (async generators) are supported: TOOL_CALL_PRE is emitted before the first yield, TOOL_CALL_POST is emitted after generator exhaustion.


LangChain.js Example

Thoth works with any object that has a .tools array where each tool has a .run method. This includes LangChain.js AgentExecutor instances:

import { AgentExecutor, createOpenAIToolsAgent } from "langchain/agents";
import { ChatOpenAI } from "@langchain/openai";
import { DynamicTool } from "@langchain/core/tools";
import { instrument } from "@atensec/thoth";

const webSearch = new DynamicTool({
  name: "web_search",
  description: "Search the web for current information.",
  func: async (query: string) => {
    // your search implementation
    return `Results for: ${query}`;
  },
});

const readFile = new DynamicTool({
  name: "read_file",
  description: "Read a file from the local filesystem.",
  func: async (path: string) => {
    // your file read implementation
    return `Contents of ${path}`;
  },
});

const llm = new ChatOpenAI({ model: "gpt-4o" });
const agent = await createOpenAIToolsAgent({
  llm,
  tools: [webSearch, readFile],
  prompt,
});

const executor = new AgentExecutor({
  agent,
  tools: [webSearch, readFile],
});

// One call instruments all tools on the executor
const governed = instrument(executor, {
  agentId: "research-agent",
  approvedScope: ["web_search", "read_file"],
  tenantId: "acme-corp",
  userId: "[email protected]",
  enforcement: "block",
});

// Every tool invocation is now policy-checked
const result = await governed.invoke({
  input: "Find recent SEC filings for AAPL",
});

Framework Integrations

For frameworks that don't use the .tools[].run shape, use the dedicated integration helpers.

Anthropic Claude

import Anthropic from "@anthropic-ai/sdk";
import { wrapAnthropicTools } from "@atensec/thoth/anthropic";
// or: import { wrapAnthropicTools } from "@atensec/thoth";

const client = new Anthropic();

const wrappedFns = wrapAnthropicTools(
  { search_docs: mySearchFn, delete_record: myDeleteFn },
  {
    agentId: "claude-research-agent",
    approvedScope: ["search_docs"],
    tenantId: "acme-corp",
    apiUrl: "https://enforce.acme-corp.atensecurity.com",
    userId: "[email protected]",
    enforcement: "step_up",
  },
);

const messages: Anthropic.MessageParam[] = [
  { role: "user", content: "Find our data retention policy." },
];

while (true) {
  const response = await client.messages.create({
    model: "claude-opus-4-6",
    max_tokens: 1024,
    tools: TOOLS,
    messages,
  });
  if (response.stop_reason === "end_turn") break;
  const toolResults: Anthropic.ToolResultBlockParam[] = [];
  for (const block of response.content) {
    if (block.type === "tool_use") {
      const fn = wrappedFns[block.name];
      if (fn) {
        const result = await fn(block.input); // governance runs here
        toolResults.push({
          type: "tool_result",
          tool_use_id: block.id,
          content: String(result),
        });
      }
    }
  }
  messages.push({ role: "assistant", content: response.content });
  messages.push({ role: "user", content: toolResults });
}

OpenAI Function Calling

import OpenAI from "openai";
import { wrapOpenAITools } from "@atensec/thoth/openai";
// or: import { wrapOpenAITools } from "@atensec/thoth";

const client = new OpenAI();

const wrappedFns = wrapOpenAITools(
  { search_docs: mySearchFn, send_email: mySendEmailFn },
  {
    agentId: "openai-agent",
    approvedScope: ["search_docs"],
    tenantId: "acme-corp",
    apiUrl: "https://enforce.acme-corp.atensecurity.com",
    userId: "[email protected]",
    enforcement: "block",
  },
);

const messages: OpenAI.ChatCompletionMessageParam[] = [
  { role: "user", content: "Find docs about access control" },
];

while (true) {
  const response = await client.chat.completions.create({
    model: "gpt-4o",
    tools: TOOLS,
    messages,
  });
  const msg = response.choices[0].message;
  if (!msg.tool_calls?.length) break;
  messages.push(msg);
  for (const call of msg.tool_calls) {
    const fn = wrappedFns[call.function.name];
    if (fn) {
      const args = JSON.parse(call.function.arguments) as Record<
        string,
        unknown
      >;
      const result = await fn(args); // governance runs here
      messages.push({
        role: "tool",
        tool_call_id: call.id,
        content: String(result),
      });
    }
  }
}

Configuration Reference

All options are passed as the second argument to instrument(), wrapAnthropicTools(), wrapOpenAITools(), or the ThothClient.instrument*() methods.

| Field | Type | Required | Default | Description | | ---------------------- | --------------------------- | -------- | ---------------- | ------------------------------------------------------------------------------ | | agentId | string | Yes | — | Stable identifier for this agent. Used in policy rules and dashboard grouping. | | tenantId | string | Yes | — | Your Maat tenant ID. | | approvedScope | string[] | Yes | — | List of tool names this agent is authorized to call. | | apiUrl | string | Yes* | $THOTH_API_URL | Tenant API base URL used for both /v1/enforce and /v1/events/batch. | | userId | string | No | "system" | Identity of the user on whose behalf the agent acts. | | enforcement | EnforcementMode \| string | No | "block" | Enforcement mode: observe, step_up, block, or progressive. | | apiKey | string | No | $THOTH_API_KEY | API key from the Aten dashboard. | | stepUpTimeoutMinutes | number | No | 15 | How long to wait for human approval before timing out a step-up hold. | | stepUpPollIntervalMs | number | No | 5000 | How often to poll the enforcer for step-up approval status (milliseconds). |

* apiUrl may be omitted only when THOTH_API_URL is set.

TypeScript types

import type {
  ThothConfig,
  BehavioralEvent,
  EnforcementDecision,
} from "@atensec/thoth";
import {
  EnforcementMode,
  DecisionType,
  EventType,
  SourceType,
} from "@atensec/thoth";

Error Handling

When enforcement mode is block or progressive and the enforcer returns a BLOCK decision, Thoth throws a ThothPolicyViolation before the tool executes:

import { instrument, ThothPolicyViolation } from "@atensec/thoth";

const governed = instrument(agent, {
  agentId: "sensitive-agent",
  approvedScope: ["read_file"],
  tenantId: "acme-corp",
  enforcement: "block",
});

try {
  const result = await governed.tools[0].run("/etc/passwd");
} catch (err) {
  if (err instanceof ThothPolicyViolation) {
    // err.toolName    — the tool that was blocked
    // err.reason      — human-readable policy reason
    // err.violationId — reference ID for the violation record in the Maat dashboard
    console.warn(`Blocked: ${err.toolName} — ${err.reason}`);
    if (err.violationId) {
      console.warn(`Violation record: ${err.violationId}`);
    }
    return {
      error: "This action is not permitted under your current access policy.",
    };
  }
  throw err;
}

ThothPolicyViolation extends Error, so existing error handling and logging pipelines pick it up automatically.


Environment Variables

| Variable | Description | Example | | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- | | THOTH_API_KEY | API key from the Aten dashboard. Used as default if apiKey config option is not set. | thoth_live_abc123... | | THOTH_API_URL | Tenant API base URL used for both enforcement and event ingestion when apiUrl is not provided. | https://enforce.<tenant>.<apex-domain> | | THOTH_LOG_LEVEL | Optional SDK decision-log level override (DEBUG, INFO, WARN, ERROR). Falls back to LOG_LEVEL when unset. | DEBUG |

When decision logging is enabled at debug level, SDK decision logs include hold_token for STEP_UP flows.


Enforcement Modes

| Mode | Value | Behavior | | ----------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Observe | observe | All tool calls pass through. Events are still emitted for audit. No blocking, no step-up. Use for initial rollout and baselining. | | Step-Up | step_up | Suspicious calls trigger a human approval request. Tool execution is held until approved or timed out. | | Block | block | Calls that violate policy throw ThothPolicyViolation immediately before the tool executes. | | Progressive | progressive | Escalating mode. The enforcer chooses the appropriate response per tool call based on policy rules. |


Policy Decisions

The enforcer returns one of three decisions for each tool call:

| Decision | Meaning | Agent behavior | | --------- | ----------------------------- | ----------------------------------------------------------------------------------------------------- | | ALLOW | Call is within policy. | Tool executes immediately. | | STEP_UP | Call requires human approval. | SDK polls /v1/enforce/hold/{token} until approved or timed out. On timeout: ThothPolicyViolation. | | BLOCK | Call violates policy. | ThothPolicyViolation is thrown before the tool executes. |

Enforcer errors (network timeout, 5xx) always result in ALLOW so that infrastructure faults never interrupt production workloads.


Dashboard

View sessions, violations, step-up requests, and policy decisions in your Thoth dashboard at https://<tenant>.<apex-domain>.

The dashboard shows:

  • Sessions — per-agent session timelines with all tool calls
  • Violations — blocked or escalated actions with full context
  • Approvals — step-up queue for human reviewers
  • Policies — view and edit the rules driving enforcement decisions
  • Behavioral Analytics — drift detection and anomaly scores over time

License

Apache-2.0 — Copyright 2026 Aten Security, Inc. See LICENSE for details.