> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kavachos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cost attribution

> Track LLM token spend, API call costs, and custom costs per agent, per tool, and per delegation chain.

## Why cost attribution matters

When agents make LLM calls, every token has a price. In multi-agent systems where one agent can spawn or delegate to others, costs accumulate across multiple providers and chains. Without proper attribution you cannot answer basic questions: which agent is responsible for a \$200 spike? Which tool is burning the most budget? Did the delegation chain for last night's job exceed its allocation?

Cost attribution gives you per-agent, per-tool, and per-delegation-chain cost records. It integrates with budget policies so you can fire alerts before spend becomes a problem, not after.

## Setup

```typescript theme={"system"}
import { createKavach } from 'kavachos';
import { createCostAttributionModule } from 'kavachos/auth';

const kavach = await createKavach({
  database: { provider: 'sqlite', url: 'kavach.db' },
});

const costs = createCostAttributionModule(kavach.db, {
  currency: 'USD',
  retentionDays: 90,
  alertThresholds: { warn: 5.00, critical: 20.00 },
  onAlert: async (alert) => {
    console.warn(`[cost] ${alert.type}: agent ${alert.agentId} spent $${alert.currentCostUsd.toFixed(4)} (threshold: $${alert.threshold})`);
  },
});
```

### Configuration options

<ParamField path="currency" type="string" default="'USD'">ISO 4217 currency code for cost records.</ParamField>
<ParamField path="alertThresholds" type="{ warn: number; critical: number }" default="undefined (no threshold alerts)">Dollar amounts for 24-hour rolling spend that trigger alerts.</ParamField>
<ParamField path="onAlert" type={`(alert: CostAlert) => void | Promise<void>`} default="undefined">Called when a threshold is crossed or a budget policy is exceeded.</ParamField>
<ParamField path="retentionDays" type="number" default="90">How many days of cost events to keep. Older rows are deleted on cleanup().</ParamField>

## Recording costs

Call `recordCost()` after each LLM response or API call. Pass the raw token counts and the exact dollar amount from the provider's response.

```typescript theme={"system"}
// After an OpenAI completion
const completion = await openai.chat.completions.create({ model: 'gpt-4o', messages });

await costs.recordCost({
  agentId: agent.id,
  tool: 'openai:gpt-4o',
  inputTokens: completion.usage?.prompt_tokens,
  outputTokens: completion.usage?.completion_tokens,
  costUsd: calculateOpenAiCost(completion.usage),
});
```

### RecordCostInput

<ParamField path="agentId" type="string">The agent that incurred this cost.</ParamField>
<ParamField path="tool" type="string">Provider and model identifier, e.g. 'openai:gpt-4o', 'anthropic:claude-3-5-sonnet', 'mcp:github'.</ParamField>
<ParamField path="inputTokens" type="number" default="undefined">Prompt tokens consumed.</ParamField>
<ParamField path="outputTokens" type="number" default="undefined">Completion tokens generated.</ParamField>
<ParamField path="costUsd" type="number">Exact cost in the configured currency.</ParamField>
<ParamField path="metadata" type={`Record<string, unknown>`} default="undefined">Any additional data to store alongside this event (request IDs, model version, etc.).</ParamField>
<ParamField path="delegationChainId" type="string" default="undefined">When set, this event is also attributed to the given delegation chain.</ParamField>

<Info>
  Costs are stored internally as integer microdollars (value × 1,000,000) to avoid floating-point drift across aggregations.
</Info>

### Provider helpers

<Tabs>
  <Tab>
    ```typescript theme={"system"}
    function openAiCostUsd(usage: OpenAI.CompletionUsage, model: string): number {
      const rates: Record<string, { input: number; output: number }> = {
        'gpt-4o':        { input: 2.50 / 1_000_000, output: 10.00 / 1_000_000 },
        'gpt-4o-mini':   { input: 0.15 / 1_000_000, output: 0.60 / 1_000_000 },
      };
      const rate = rates[model] ?? { input: 0, output: 0 };
      return rate.input * usage.prompt_tokens + rate.output * usage.completion_tokens;
    }

    await costs.recordCost({
      agentId,
      tool: `openai:${completion.model}`,
      inputTokens: completion.usage.prompt_tokens,
      outputTokens: completion.usage.completion_tokens,
      costUsd: openAiCostUsd(completion.usage, completion.model),
    });
    ```
  </Tab>

  <Tab>
    ```typescript theme={"system"}
    function anthropicCostUsd(usage: Anthropic.Usage, model: string): number {
      const rates: Record<string, { input: number; output: number }> = {
        'claude-3-5-sonnet-20241022': { input: 3.00 / 1_000_000, output: 15.00 / 1_000_000 },
        'claude-3-5-haiku-20241022':  { input: 0.80 / 1_000_000, output: 4.00 / 1_000_000 },
      };
      const rate = rates[model] ?? { input: 0, output: 0 };
      return rate.input * usage.input_tokens + rate.output * usage.output_tokens;
    }

    await costs.recordCost({
      agentId,
      tool: `anthropic:${message.model}`,
      inputTokens: message.usage.input_tokens,
      outputTokens: message.usage.output_tokens,
      costUsd: anthropicCostUsd(message.usage, message.model),
    });
    ```
  </Tab>

  <Tab>
    ```typescript theme={"system"}
    // For any provider with a flat per-call cost
    await costs.recordCost({
      agentId,
      tool: 'mcp:github',
      costUsd: 0.0001,
      metadata: { operation: 'create_issue', repo: 'acme/app' },
    });
    ```
  </Tab>
</Tabs>

## Generating cost reports

### Per-agent report

```typescript theme={"system"}
const result = await costs.getAgentCost(agent.id);

if (result.success) {
  console.log('Total:', result.data.totalCostUsd.toFixed(4));
  console.log('By tool:', result.data.byTool);
  console.log('By day:', result.data.byDay);
}
```

Pass a custom period to narrow the query:

```typescript theme={"system"}
const result = await costs.getAgentCost(agent.id, {
  start: new Date('2025-01-01'),
  end: new Date('2025-01-31'),
});
```

### CostReport

<ParamField path="agentId" type="string">Agent (or owner/chain) this report covers.</ParamField>
<ParamField path="period" type="{ start: Date; end: Date }">The time window the report covers.</ParamField>
<ParamField path="totalCostUsd" type="number">Total cost across all events in the period.</ParamField>
<ParamField path="byTool" type={`Array<{ tool: string; costUsd: number; callCount: number }>`}>Cost breakdown per tool, sorted by cost descending.</ParamField>
<ParamField path="byDay" type={`Array<{ date: string; costUsd: number }>`}>Daily spend as YYYY-MM-DD strings, sorted ascending.</ParamField>

### Owner report

Aggregate cost across all agents owned by a user:

```typescript theme={"system"}
const result = await costs.getOwnerCost(userId);
if (result.success) {
  console.log(`Total spend for ${userId}: $${result.data.totalCostUsd.toFixed(2)}`);
}
```

### Top agents by cost

Find the most expensive agents in any period:

```typescript theme={"system"}
const result = await costs.getTopAgentsByCost(10, {
  start: new Date('2025-01-01'),
  end: new Date('2025-01-31'),
});

if (result.success) {
  for (const { agentId, totalCostUsd } of result.data) {
    console.log(`${agentId}: $${totalCostUsd.toFixed(4)}`);
  }
}
```

### Delegation chain report

When agents delegate to sub-agents, you can attribute all costs back to the originating chain by passing `delegationChainId` in `recordCost()`:

```typescript theme={"system"}
// When the parent agent creates a delegation
const chain = await kavach.delegate({
  fromAgent: parentAgent.id,
  toAgent: childAgent.id,
  permissions: [{ resource: 'tool:summarize', actions: ['execute'] }],
  expiresIn: '1h',
});

// In the child agent's handler
await costs.recordCost({
  agentId: childAgent.id,
  tool: 'openai:gpt-4o-mini',
  costUsd: 0.02,
  delegationChainId: chain.id,
});

// Later: total cost across all agents in that chain
const result = await costs.getDelegationChainCost(chain.id);
```

## Setting up alerts

Alerts fire automatically when `recordCost()` is called. There are three alert types:

| Type              | When it fires                                                                |
| ----------------- | ---------------------------------------------------------------------------- |
| `warn`            | 24-hour rolling spend crosses the `warn` threshold                           |
| `critical`        | 24-hour rolling spend crosses the `critical` threshold                       |
| `budget_exceeded` | Monthly spend crosses the `maxTokensCostPerMonth` limit from a budget policy |

```typescript theme={"system"}
const costs = createCostAttributionModule(kavach.db, {
  alertThresholds: {
    warn: 5.00,      // $5 in 24 hours
    critical: 20.00, // $20 in 24 hours
  },
  onAlert: async (alert) => {
    if (alert.type === 'budget_exceeded') {
      // Revoke or suspend the agent
      await kavach.agent.revoke(alert.agentId);
    }

    // Send to Slack, PagerDuty, etc.
    await notifyOpsChannel({
      text: `[${alert.type.toUpperCase()}] Agent ${alert.agentId} spent $${alert.currentCostUsd.toFixed(2)} (limit: $${alert.threshold}) over ${alert.period}`,
    });
  },
});
```

### CostAlert

<ParamField path="type" type="'warn' | 'critical' | 'budget_exceeded'">Severity of the alert.</ParamField>
<ParamField path="agentId" type="string">The agent that triggered the alert.</ParamField>
<ParamField path="currentCostUsd" type="number">The actual spend that triggered the alert.</ParamField>
<ParamField path="threshold" type="number">The limit that was crossed.</ParamField>
<ParamField path="period" type="string">Time window for this alert, e.g. '24h' or 'monthly'.</ParamField>

## Integration with budget policies

`checkBudget()` reads budget policies created via `kavach.policies` and compares them against actual spend from the cost events table. This gives you a real-time answer before authorizing an operation.

```typescript theme={"system"}
const result = await costs.checkBudget(agent.id);

if (result.success) {
  const { withinBudget, spent, limit, remaining } = result.data;

  if (!withinBudget) {
    return new Response('Agent has exceeded its monthly cost budget', { status: 402 });
  }

  console.log(`$${spent.toFixed(4)} of $${limit?.toFixed(2) ?? '∞'} used`);
}
```

To set a budget limit, create a policy with `maxTokensCostPerMonth`:

```typescript theme={"system"}
await kavach.policies.create({
  agentId: agent.id,
  limits: {
    maxTokensCostPerMonth: 50, // $50/month
  },
  action: 'block',
});
```

The `budget_exceeded` alert fires automatically on `recordCost()` when spend crosses this limit, so you do not need to poll.

## Maintenance

Cost events accumulate. Run `cleanup()` periodically to remove events older than the retention window:

```typescript theme={"system"}
// In a cron job
const result = await costs.cleanup({ retentionDays: 90 });
if (result.success) {
  console.log(`Deleted ${result.data.deleted} cost events`);
}
```

If you configured `retentionDays` on the module, you can call `cleanup()` with no arguments and it uses that value.

## Return types

All methods return a `Result<T>` union:

```typescript theme={"system"}
type Result<T> =
  | { success: true; data: T }
  | { success: false; error: KavachError };
```

Check `result.success` before accessing `result.data`. Error codes:

| Code                    | Cause                          |
| ----------------------- | ------------------------------ |
| `RECORD_COST_FAILED`    | Database insert failed         |
| `GET_AGENT_COST_FAILED` | Query failed for agent report  |
| `GET_OWNER_COST_FAILED` | Query failed for owner report  |
| `GET_TOP_AGENTS_FAILED` | Aggregation query failed       |
| `GET_CHAIN_COST_FAILED` | Chain attribution query failed |
| `CHECK_BUDGET_FAILED`   | Budget policy lookup failed    |
| `CLEANUP_FAILED`        | Cleanup delete failed          |
