@theokit/sdk 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.8.0] - 2026-06-12
6
+
7
+ ### Added
8
+
9
+ - `.claude/` consumer template with 15 domain-specific passive skills, convention rules, AGENTS.md (cross-agent), and CLAUDE.md for AI coding tool integration. Scaffold via `npx theokit-init-claude`. Skills auto-inject TheoKit API knowledge when editing files matching each domain (Agent Core, Tools, Memory, DI, DI-Agent, Gateways, RAG, Workflows, Eval, Cron, Subscriptions, Errors, Config, Streaming, Budget). 33 tests.
10
+
5
11
  ## [1.7.0] - 2026-06-11
6
12
 
7
13
  ### Changed
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { cpSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ // EC-1: Node version guard (matches SDK engines.node)
6
+ const [major, minor] = process.versions.node.split(".").map(Number);
7
+ if (major < 22 || (major === 22 && minor < 12)) {
8
+ console.error(`@theokit/sdk requires Node >= 22.12.0. Current: ${process.version}`);
9
+ process.exit(1);
10
+ }
11
+
12
+ const templateDir = join(import.meta.dirname, "../claude-template");
13
+ const cwd = process.cwd();
14
+ const targetDir = join(cwd, ".claude");
15
+ const force = process.argv.includes("--force");
16
+
17
+ // EC-4: Check .claude/, AGENTS.md, CLAUDE.md independently
18
+ const conflicts = [];
19
+ if (existsSync(targetDir)) conflicts.push(".claude/");
20
+ if (existsSync(join(cwd, "AGENTS.md"))) conflicts.push("AGENTS.md");
21
+ if (existsSync(join(cwd, "CLAUDE.md"))) conflicts.push("CLAUDE.md");
22
+
23
+ if (conflicts.length > 0 && !force) {
24
+ console.error(`Already exists: ${conflicts.join(", ")}. Use --force to overwrite.`);
25
+ process.exit(1);
26
+ }
27
+
28
+ cpSync(join(templateDir, "dot-claude"), targetDir, { recursive: true });
29
+ cpSync(join(templateDir, "AGENTS.md"), join(cwd, "AGENTS.md"));
30
+ cpSync(join(templateDir, "CLAUDE.md"), join(cwd, "CLAUDE.md"));
31
+
32
+ console.log("Created .claude/ with TheoKit SDK configuration (15 domain skills).");
33
+ console.log("Created AGENTS.md (cross-agent) and CLAUDE.md (Claude Code).");
34
+ console.log("\nNext: open Claude Code and start building with TheoKit.");
@@ -0,0 +1,139 @@
1
+ # @theokit/sdk — TypeScript SDK for AI Agents
2
+
3
+ Build AI agents that run locally or in the cloud. Same code, same API, pick your runtime.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install @theokit/sdk
9
+ ```
10
+
11
+ Set your API key:
12
+ ```bash
13
+ export THEOKIT_API_KEY="your-key"
14
+ ```
15
+
16
+ ## Import Map
17
+
18
+ ```typescript
19
+ import { Agent } from "@theokit/sdk"; // Core: Agent, Run, SDKMessage
20
+ import { defineTool } from "@theokit/sdk"; // Tool definitions
21
+ import { TheokitAgentError } from "@theokit/sdk/errors"; // Error hierarchy
22
+ import { Cron } from "@theokit/sdk/cron"; // Scheduled jobs
23
+ import { Eval } from "@theokit/sdk/eval"; // Evaluation suite
24
+ import { Workflow } from "@theokit/sdk/workflow"; // Multi-step workflows
25
+ import { defineSubscription } from "@theokit/sdk/subscription"; // SSE/WebSocket subscriptions
26
+ import { VectorRetriever } from "@theokit/sdk/rag"; // RAG: retrievers, rerankers, splitters
27
+ import { defineSubAgent } from "@theokit/sdk/a2a"; // Agent-to-agent delegation
28
+ import { SandboxBackend } from "@theokit/sdk/sandbox"; // Sandbox backends
29
+ import { defineAuth } from "@theokit/sdk/server/auth"; // Authentication
30
+ import { TaskStore } from "@theokit/sdk/task-store"; // Task persistence
31
+ import { createClient } from "@theokit/sdk/client"; // HTTP client
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```typescript
37
+ const agent = await Agent.create({
38
+ apiKey: process.env.THEOKIT_API_KEY!,
39
+ model: { id: "google/gemini-2.0-flash-001" },
40
+ local: { cwd: process.cwd() },
41
+ });
42
+
43
+ const run = await agent.send("Summarize this repository");
44
+ for await (const event of run.stream()) {
45
+ if (event.type === "assistant") console.log(event.content);
46
+ }
47
+
48
+ agent.dispose(); // Always clean up
49
+ ```
50
+
51
+ ## Core Patterns
52
+
53
+ ### Agent lifecycle
54
+ - `Agent.create(options)` — create an agent (local or cloud)
55
+ - `agent.send(prompt)` — send a message, get a Run
56
+ - `run.stream()` — AsyncGenerator of SDKMessage events
57
+ - `agent.dispose()` — clean up resources (or use `await using`)
58
+ - `Agent.prompt(options, prompt)` — one-shot: create, send, dispose
59
+
60
+ ### Tool definition
61
+ ```typescript
62
+ const searchTool = defineTool({
63
+ name: "search",
64
+ description: "Search the web",
65
+ inputSchema: z.object({ query: z.string() }),
66
+ execute: async ({ query }) => ({ results: await search(query) }),
67
+ });
68
+ ```
69
+
70
+ ### Streaming events (SDKMessage)
71
+ - `{ type: "assistant", content }` — text from the model
72
+ - `{ type: "tool_use", name, input }` — tool call
73
+ - `{ type: "tool_result", name, output }` — tool response
74
+ - `{ type: "status", status }` — run status change
75
+ - `{ type: "error", error }` — error event
76
+ - `{ type: "usage", tokens }` — token usage update
77
+
78
+ ### Error handling
79
+ ```typescript
80
+ try {
81
+ await agent.send("...");
82
+ } catch (e) {
83
+ if (e instanceof TheokitAgentError) {
84
+ console.error(e.code, e.message); // typed error with code
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### DI decorators (`@theokit/di` + `@theokit/di-agent`)
90
+ ```typescript
91
+ import { Injectable, Container } from "@theokit/di";
92
+ import { Tool, Workflow, Cron, InjectAgent } from "@theokit/di-agent";
93
+
94
+ @Injectable()
95
+ class MyService {
96
+ @Tool({ name: "search", description: "Search" })
97
+ searchTool!: ToolOptions;
98
+
99
+ @Cron({ schedule: "*/5 * * * *" })
100
+ cleanup() { /* runs every 5 min */ }
101
+ }
102
+ ```
103
+
104
+ ### Gateways
105
+ ```typescript
106
+ import { defineGateway } from "@theokit/gateway-telegram"; // or -slack, -discord, etc.
107
+ const gateway = defineGateway({ token: process.env.BOT_TOKEN });
108
+ ```
109
+
110
+ Available: telegram, slack, discord, whatsapp, teams, email, sms, mattermost, line, matrix.
111
+
112
+ ## Anti-patterns
113
+
114
+ - NEVER import from `@theokit/sdk/internal/...` — internal paths are not public API
115
+ - NEVER import from `@theokit/sdk/dist/...` — use the exports map above
116
+ - NEVER forget `agent.dispose()` — causes resource leaks
117
+ - NEVER use `new Agent()` — always use `Agent.create()`
118
+ - NEVER use `any` for tool input schemas — use Zod schemas
119
+
120
+ ## Packages
121
+
122
+ | Package | Purpose |
123
+ |---------|---------|
124
+ | `@theokit/sdk` | Core SDK (Agent, Run, Tools, Memory, Streaming) |
125
+ | `@theokit/di` | Dependency injection container |
126
+ | `@theokit/di-agent` | 15 agentic decorators for DI |
127
+ | `@theokit/gateway-*` | Platform gateways (Telegram, Slack, etc.) |
128
+ | `@theokit/react` | React hooks for agent UIs |
129
+
130
+ ## Configuration
131
+
132
+ Project config lives in `.theokit/`:
133
+ - `.theokit/mcp.json` — MCP server configuration
134
+ - `.theokit/hooks.json` — lifecycle hooks
135
+ - `.theokit/agents/*.md` — agent instruction files
136
+
137
+ Environment variables:
138
+ - `THEOKIT_API_KEY` — API key (required)
139
+ - `THEOKIT_MODEL_ID` — default model override
@@ -0,0 +1,51 @@
1
+ @AGENTS.md
2
+
3
+ ## Claude Code — TheoKit SDK Extensions
4
+
5
+ This project uses `@theokit/sdk`. The AGENTS.md above contains the full API reference. Below are Claude Code-specific extensions.
6
+
7
+ ### Available Skills (auto-loaded by domain)
8
+
9
+ These skills inject TheoKit knowledge automatically when you edit files matching their domain:
10
+
11
+ | Skill | Triggers on files matching |
12
+ |-------|---------------------------|
13
+ | `theokit-agent-core` | `*agent*`, `*Agent*`, `sdk.*` |
14
+ | `theokit-tools` | `*tool*`, `*Tool*` |
15
+ | `theokit-memory` | `*memory*`, `*Memory*`, `*embed*` |
16
+ | `theokit-di` | `*container*`, `*inject*`, `*provider*`, `*module*` |
17
+ | `theokit-di-agent` | `*decorator*`, `*Decorator*`, `di-agent*` |
18
+ | `theokit-gateways` | `*gateway*`, `*telegram*`, `*slack*`, `*discord*` |
19
+ | `theokit-rag` | `*retriev*`, `*rerank*`, `*splitter*`, `*rag*` |
20
+ | `theokit-workflows` | `*workflow*`, `*Workflow*`, `*step*` |
21
+ | `theokit-eval` | `*eval*`, `*Eval*`, `*scorer*` |
22
+ | `theokit-cron` | `*cron*`, `*Cron*`, `*job*`, `*schedule*` |
23
+ | `theokit-subscriptions` | `*subscri*`, `*sse*`, `*websocket*`, `*ws.*` |
24
+ | `theokit-errors` | `*error*`, `*Error*`, `*exception*` |
25
+ | `theokit-config` | `.theokit/**`, `config.*`, `theo.config.*` |
26
+ | `theokit-streaming` | `*stream*`, `*Stream*`, `*SDKMessage*` |
27
+ | `theokit-budget` | `*budget*`, `*Budget*`, `*cost*`, `*token*` |
28
+
29
+ ### Settings
30
+
31
+ `.claude/settings.json` is pre-configured with safe defaults:
32
+ - Allows: `npm run *`, `pnpm *`, `git status`, `git diff`, reading `src/` and `docs/`
33
+ - Denies: `.env*` file reads, `sudo`, `rm -rf`
34
+ - Override locally: create `.claude/settings.local.json` (add to `.gitignore`)
35
+
36
+ ### Customization
37
+
38
+ Add your project-specific instructions below this line. The SDK knowledge above stays current with your installed `@theokit/sdk` version.
39
+
40
+ ```markdown
41
+ ## My Project
42
+
43
+ Build: `npm run build`
44
+ Test: `npm test`
45
+ Lint: `npm run lint`
46
+
47
+ ## Architecture
48
+ - `src/agents/` — agent definitions
49
+ - `src/tools/` — tool implementations
50
+ - `src/services/` — business logic
51
+ ```
@@ -0,0 +1,33 @@
1
+ # TheoKit SDK Conventions
2
+
3
+ ## Agent lifecycle
4
+ - Always use `Agent.create()` to create agents — NEVER `new Agent()`
5
+ - Always call `agent.dispose()` or use `await using` when done
6
+ - Use `Agent.prompt()` for one-shot operations (auto-disposes)
7
+
8
+ ## Imports
9
+ - Use `@theokit/sdk` for core (Agent, defineTool, Memory)
10
+ - Use `@theokit/sdk/errors` for error types
11
+ - Use `@theokit/sdk/subscription` for SSE/WebSocket
12
+ - Use `@theokit/sdk/rag` for retrievers, rerankers, splitters
13
+ - Use `@theokit/sdk/cron` for scheduled jobs
14
+ - Use `@theokit/sdk/eval` for evaluation
15
+ - Use `@theokit/sdk/workflow` for workflows
16
+ - NEVER import from `@theokit/sdk/internal/...`
17
+ - NEVER import from `@theokit/sdk/dist/...`
18
+
19
+ ## Tools
20
+ - Tool `inputSchema` MUST use Zod schemas — NEVER `any` or untyped objects
21
+ - Tool `execute` MUST return a serializable value
22
+
23
+ ## DI
24
+ - Use `@Injectable()` + `@Inject()` from `@theokit/di`
25
+ - NEVER manually `new` a service — let the container resolve it
26
+
27
+ ## Error handling
28
+ - Catch `TheokitAgentError` (base class) and check `error.code`
29
+ - NEVER silently swallow errors — log with context or rethrow
30
+
31
+ ## Gateways
32
+ - One gateway per agent instance
33
+ - Configure via `defineGateway()` from the specific gateway package
@@ -0,0 +1,16 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run *)",
5
+ "Bash(pnpm *)",
6
+ "Bash(npx *)",
7
+ "Read(./src/**)",
8
+ "Read(./docs/**)",
9
+ "Read(./tests/**)",
10
+ "Bash(git status)",
11
+ "Bash(git diff)",
12
+ "Bash(git log *)"
13
+ ],
14
+ "deny": ["Read(.env*)", "Read(**/.env*)", "Bash(sudo *)", "Bash(rm -rf *)"]
15
+ }
16
+ }
@@ -0,0 +1,209 @@
1
+ ---
2
+ user-invocable: false
3
+ description: Agent lifecycle, sending messages, streaming, and disposal patterns for @theokit/sdk.
4
+ paths:
5
+ - "**/*agent*"
6
+ - "**/*Agent*"
7
+ - "**/sdk.*"
8
+ ---
9
+
10
+ # TheoKit SDK -- Agent Core
11
+
12
+ Quick reference for Agent lifecycle, Run streaming, and disposal.
13
+
14
+ ## Agent.create
15
+
16
+ ```typescript
17
+ import { Agent } from "@theokit/sdk";
18
+
19
+ const agent = await Agent.create({
20
+ apiKey: process.env.THEOKIT_API_KEY!,
21
+ model: { id: "google/gemini-2.0-flash-001" },
22
+ local: { cwd: process.cwd() },
23
+ });
24
+ ```
25
+
26
+ Returns an `SDKAgent`. Local agents get `agent-<uuid>` IDs; cloud agents get `bc-<uuid>`.
27
+
28
+ ### AgentOptions (key fields)
29
+
30
+ | Property | Type | Notes |
31
+ |---|---|---|
32
+ | `model` | `ModelSelection` | Required for local; `{ id, params? }`. |
33
+ | `apiKey` | `string` | Falls back to `THEOKIT_API_KEY` env. |
34
+ | `local` | `{ cwd, settingSources?, sandboxOptions? }` | Local runtime config. |
35
+ | `cloud` | `CloudOptions` | Cloud runtime config (repos, autoCreatePR, envVars). |
36
+ | `systemPrompt` | `string \| (ctx: SystemPromptContext) => string` | Static string or async resolver. |
37
+ | `mcpServers` | `Record<string, McpServerConfig>` | Inline MCP server definitions. |
38
+ | `agents` | `Record<string, AgentDefinition>` | Subagent definitions. |
39
+ | `tools` | `CustomTool[]` | Inline custom tools (local only). |
40
+ | `memory` | `MemoryOptions` | Durable memory config. |
41
+ | `handoffs` | `Array<SDKAgent \| Handoff>` | Peer-to-peer agent handoffs. |
42
+ | `conversationStorage` | `ConversationStorageAdapter` | Pluggable persistence. |
43
+
44
+ ## Agent.prompt (one-shot)
45
+
46
+ ```typescript
47
+ const result = await Agent.prompt("What does the auth middleware do?", {
48
+ apiKey: process.env.THEOKIT_API_KEY!,
49
+ model: { id: "google/gemini-2.0-flash-001" },
50
+ local: { cwd: process.cwd() },
51
+ });
52
+ // result: { id, status, result?, model?, durationMs?, git? }
53
+ ```
54
+
55
+ Pass `throwOnError: true` to reject with `AgentRunError` instead of resolving with `status: 'error'`.
56
+
57
+ ## agent.send and Run
58
+
59
+ ```typescript
60
+ const run = await agent.send("Find the bug in src/auth.ts");
61
+ ```
62
+
63
+ ### Run interface
64
+
65
+ | Member | Type | Description |
66
+ |---|---|---|
67
+ | `id` | `string` | Run identifier. |
68
+ | `status` | `RunStatus` | `"running" \| "finished" \| "error" \| "cancelled"` |
69
+ | `stream()` | `AsyncGenerator<SDKMessage>` | Normalized event stream. |
70
+ | `wait()` | `Promise<RunResult>` | Block until finished. |
71
+ | `cancel()` | `Promise<void>` | Cancel the run. |
72
+ | `conversation()` | `Promise<ConversationTurn[]>` | Structured turn history. |
73
+ | `onDidChangeStatus(fn)` | `() => void` | Status change listener; returns unsubscribe. |
74
+
75
+ ### Streaming SDKMessage types
76
+
77
+ ```typescript
78
+ for await (const event of run.stream()) {
79
+ switch (event.type) {
80
+ case "assistant": /* event.message.content: (TextBlock | ToolUseBlock)[] */ break;
81
+ case "thinking": /* event.text, event.thinking_duration_ms? */ break;
82
+ case "tool_call": /* event.name, event.status, event.args?, event.result? */ break;
83
+ case "status": /* event.status: cloud lifecycle transitions */ break;
84
+ case "task": /* event.text: task milestones */ break;
85
+ case "request": /* event.request_id: awaiting user input */ break;
86
+ }
87
+ }
88
+ ```
89
+
90
+ ### Per-send options
91
+
92
+ ```typescript
93
+ await agent.send("Plan the refactor", {
94
+ model: { id: "claude-sonnet-4-6", params: [{ id: "thinking", value: "high" }] },
95
+ systemPrompt: "Focus on performance.",
96
+ signal: abortController.signal,
97
+ onDelta: ({ update }) => { /* InteractionUpdate */ },
98
+ onStep: ({ step }) => { /* ConversationStep */ },
99
+ });
100
+ ```
101
+
102
+ ## Agent.resume
103
+
104
+ ```typescript
105
+ const agent = await Agent.resume("bc-abc123", {
106
+ apiKey: process.env.THEOKIT_API_KEY!,
107
+ });
108
+ ```
109
+
110
+ Runtime auto-detected from ID prefix. Inline `mcpServers` and `tools` are NOT persisted -- re-pass on resume.
111
+
112
+ ## Agent.getOrCreate
113
+
114
+ ```typescript
115
+ const agent = await Agent.getOrCreate(`tg-user-${userId}`, {
116
+ apiKey: process.env.THEOKIT_API_KEY!,
117
+ model: { id: "claude-sonnet-4-6" },
118
+ local: { cwd: process.cwd() },
119
+ });
120
+ ```
121
+
122
+ Tries resume first; on `UnknownAgentError` falls through to create.
123
+
124
+ ## Agent.get / Agent.list / Agent.listRuns
125
+
126
+ ```typescript
127
+ const info = await Agent.get(agentId);
128
+ const { items, nextCursor } = await Agent.list({ runtime: "local", cwd: process.cwd() });
129
+ const { items: runs } = await Agent.listRuns(agentId);
130
+ ```
131
+
132
+ ## createAgentFactory
133
+
134
+ ```typescript
135
+ import { createAgentFactory } from "@theokit/sdk";
136
+
137
+ const factory = createAgentFactory({
138
+ apiKey: process.env.THEOKIT_API_KEY!,
139
+ model: { id: "claude-sonnet-4-6" },
140
+ local: { cwd: process.cwd() },
141
+ systemPrompt: "You are a helpful assistant.",
142
+ });
143
+ const agent = await factory.getOrCreate(`user-${userId}`);
144
+ ```
145
+
146
+ ## Agent.builder (fluent API)
147
+
148
+ ```typescript
149
+ const agent = await Agent.builder()
150
+ .apiKey(process.env.THEOKIT_API_KEY!)
151
+ .model({ id: "claude-sonnet-4-6" })
152
+ .local({ cwd: process.cwd() })
153
+ .tools([myTool])
154
+ .getOrCreate(`user-${userId}`);
155
+ ```
156
+
157
+ ## Agent.generateObject / Agent.streamObject
158
+
159
+ ```typescript
160
+ import { z } from "zod";
161
+
162
+ const { object } = await Agent.generateObject({
163
+ schema: z.object({ title: z.string(), year: z.number().nullable() }),
164
+ prompt: "Fact card about jazz.",
165
+ model: { id: "google/gemini-2.0-flash-001" },
166
+ local: { cwd: process.cwd() },
167
+ });
168
+
169
+ for await (const evt of Agent.streamObject({ schema, prompt, model, local })) {
170
+ if (evt.type === "partial") render(evt.partial);
171
+ if (evt.type === "complete") finalize(evt.object);
172
+ }
173
+ ```
174
+
175
+ ## Disposal patterns
176
+
177
+ ```typescript
178
+ // Preferred: await using (auto-dispose on block exit)
179
+ await using agent = await Agent.create({ /* ... */ });
180
+
181
+ // Explicit dispose
182
+ await agent[Symbol.asyncDispose]();
183
+
184
+ // Fire-and-forget close
185
+ agent.close();
186
+
187
+ // Reload config without disposing
188
+ await agent.reload();
189
+ ```
190
+
191
+ ## Agent.registry (production)
192
+
193
+ ```typescript
194
+ Agent.registry.configure({ maxAgents: 1000, idleTimeoutMs: 15 * 60 * 1000 });
195
+ Agent.registry.size();
196
+ Agent.registry.evict("agent-42");
197
+ await Agent.registry.evictAll(); // graceful shutdown
198
+ ```
199
+
200
+ ## Cancellation
201
+
202
+ ```typescript
203
+ const run = await agent.send(message, { signal: request.signal });
204
+ // On abort: AgentRunError with code "aborted"
205
+
206
+ // Compose with timeout:
207
+ const composed = AbortSignal.any([request.signal, AbortSignal.timeout(30_000)]);
208
+ await agent.send(message, { signal: composed });
209
+ ```
@@ -0,0 +1,176 @@
1
+ ---
2
+ user-invocable: false
3
+ paths:
4
+ - "**/*budget*"
5
+ - "**/*Budget*"
6
+ - "**/*cost*"
7
+ - "**/*token*"
8
+ description: TheoKit SDK Budget API reference — cost tracking, token budget enforcement, usage reporting
9
+ ---
10
+
11
+ # TheoKit Budget
12
+
13
+ Token cost enforcement primitive. Track, warn, and block LLM spend at the
14
+ process level with stacked USD limits and configurable enforcement modes.
15
+
16
+ ## Quick start
17
+
18
+ ```typescript
19
+ import { Budget } from "@theokit/sdk";
20
+
21
+ const handle = Budget.create({
22
+ name: "my-bot",
23
+ scope: "process",
24
+ mode: "warn",
25
+ limits: [
26
+ { window: "1h", limitUsd: 5.0 },
27
+ { window: "1d", limitUsd: 50.0 },
28
+ ],
29
+ onThreshold: (event) => {
30
+ console.warn(`Budget ${event.budgetName}: ${event.threshold * 100}% of ${event.window} limit`);
31
+ },
32
+ onExceed: (event) => {
33
+ console.error(`Budget ${event.budgetName}: exceeded ${event.window} limit`);
34
+ },
35
+ });
36
+ ```
37
+
38
+ ## Enforcement modes
39
+
40
+ | Mode | Behavior |
41
+ |---|---|
42
+ | `"audit"` | Log only. Never throws, never blocks. |
43
+ | `"warn"` | Callbacks fire at 80%, 95%, and 100% thresholds. No throw. Default. |
44
+ | `"block"` | Preflight throw (`BudgetExceededError`) BEFORE the LLM call when would-exceed. |
45
+
46
+ ## Stacked limits
47
+
48
+ Pass multiple limits. ANY exceeded limit triggers enforcement:
49
+
50
+ ```typescript
51
+ Budget.create({
52
+ name: "emergency-stop",
53
+ scope: "process",
54
+ mode: "block",
55
+ limits: [
56
+ { window: "1h", limitUsd: 2.0 },
57
+ { window: "1d", limitUsd: 10.0 },
58
+ { window: "30d", limitUsd: 100.0 },
59
+ ],
60
+ });
61
+ ```
62
+
63
+ Empty `limits[]` is valid: pure tracking with no threshold/exceed callbacks.
64
+
65
+ ## Time windows
66
+
67
+ | Window | Alignment |
68
+ |---|---|
69
+ | `"1h"` | Relative (last 60 minutes) |
70
+ | `"1d"` | UTC midnight boundary |
71
+ | `"1w"` | Monday 00:00 UTC |
72
+ | `"30d"` | 1st of month 00:00 UTC |
73
+ | `"365d"` | Jan 1 00:00 UTC |
74
+
75
+ ## Managing budgets
76
+
77
+ ```typescript
78
+ // Retrieve a budget handle
79
+ const handle = Budget.get("my-bot");
80
+
81
+ // Check spend and remaining
82
+ handle?.spentIn("1d"); // USD spent in current day window
83
+ handle?.remainingIn("1d"); // USD remaining before limit
84
+
85
+ // List all active budgets
86
+ const budgets = Budget.list();
87
+
88
+ // Snapshot all windows for all budgets
89
+ const snapshots = Budget.snapshot();
90
+ // [{ name, window, spentUsd, limitUsd, ratio }, ...]
91
+
92
+ // Delete a budget
93
+ Budget.delete("my-bot");
94
+ ```
95
+
96
+ ## Type reference
97
+
98
+ ```typescript
99
+ type BudgetScope = "agent" | "call" | "process";
100
+ type BudgetWindow = "1h" | "1d" | "1w" | "30d" | "365d";
101
+ type BudgetMode = "audit" | "warn" | "block";
102
+
103
+ interface BudgetLimit {
104
+ readonly window: BudgetWindow;
105
+ readonly limitUsd: number;
106
+ }
107
+
108
+ interface BudgetOptions {
109
+ readonly name: string; // must match ^[a-z0-9][a-z0-9_-]*$
110
+ readonly scope: BudgetScope;
111
+ readonly limits: ReadonlyArray<BudgetLimit>;
112
+ readonly mode?: BudgetMode; // default "warn"
113
+ readonly onThreshold?: (event: BudgetThresholdEvent) => void | Promise<void>;
114
+ readonly onExceed?: (event: BudgetExceedEvent) => void | Promise<void>;
115
+ }
116
+
117
+ interface BudgetHandle {
118
+ readonly name: string;
119
+ readonly mode: BudgetMode;
120
+ readonly scope: BudgetScope;
121
+ readonly limits: ReadonlyArray<BudgetLimit>;
122
+ spentIn(window: BudgetWindow): number;
123
+ remainingIn(window: BudgetWindow): number;
124
+ }
125
+
126
+ interface BudgetSnapshot {
127
+ readonly name: string;
128
+ readonly window: BudgetWindow;
129
+ readonly spentUsd: number;
130
+ readonly limitUsd: number;
131
+ readonly ratio: number;
132
+ }
133
+
134
+ interface BudgetThresholdEvent {
135
+ readonly budgetName: string;
136
+ readonly window: BudgetWindow;
137
+ readonly threshold: 0.8 | 0.95;
138
+ readonly spentUsd: number;
139
+ readonly limitUsd: number;
140
+ }
141
+
142
+ interface BudgetExceedEvent {
143
+ readonly budgetName: string;
144
+ readonly window: BudgetWindow;
145
+ readonly spentUsd: number;
146
+ readonly limitUsd: number;
147
+ readonly mode: BudgetMode;
148
+ }
149
+ ```
150
+
151
+ ## Wiring with agents
152
+
153
+ Budget enforcement integrates with `agent.send()`. When a budget is active
154
+ and mode is `"block"`, a preflight check runs before the LLM call. If the
155
+ estimated cost would exceed a limit, `BudgetExceededError` is thrown.
156
+
157
+ The `turn-ended` InteractionUpdate carries token usage that feeds the budget
158
+ ledger automatically:
159
+
160
+ ```typescript
161
+ { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }
162
+ ```
163
+
164
+ ## Errors
165
+
166
+ | Error | When |
167
+ |---|---|
168
+ | `ConfigurationError` | Invalid `name` (grammar violation) or duplicate `name` |
169
+ | `BudgetExceededError` | `mode: "block"` and spend would exceed a limit |
170
+
171
+ ## Known limitations
172
+
173
+ - v1 supports `scope: "process"` only. `"agent"` and `"call"` scopes are
174
+ reserved for future multi-tenant scenarios.
175
+ - In-flight `agent.send` calls referencing a deleted budget treat subsequent
176
+ charges as a silent no-op with a stderr warning.