agentledger 0.3.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/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # AgentLedger
2
+
3
+ **See everything your AI agents do.** Track actions, monitor costs, and kill agents when things go wrong.
4
+
5
+ Your agents send emails, create tickets, charge credit cards, and call APIs. AgentLedger logs every action, tracks every cost, and lets you kill agents instantly when things go sideways.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install agentledger
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { AgentLedger } from 'agentledger';
17
+
18
+ const ledger = new AgentLedger({
19
+ apiKey: process.env.AGENTLEDGER_KEY, // Get this from agentledger.co
20
+ });
21
+
22
+ // Wrap any async function — it's logged, timed, and budget-checked
23
+ const result = await ledger.track({
24
+ agent: 'support-bot',
25
+ service: 'slack',
26
+ action: 'send_message',
27
+ }, async () => {
28
+ return await slack.chat.postMessage({ channel: '#support', text: 'Hello!' });
29
+ });
30
+ ```
31
+
32
+ Open your dashboard at [agentledger.co](https://agentledger.co) and watch your agents in real-time.
33
+
34
+ ## Features
35
+
36
+ - **Zero dependencies** — just this package, nothing else
37
+ - **Fail-open by default** — if AgentLedger is down, your agents keep running
38
+ - **Pre-flight checks** — block actions before they happen if agent is over budget
39
+ - **Kill switches** — pause or kill any agent instantly
40
+ - **Cost tracking** — know exactly what each agent costs
41
+ - **5ms overhead** — async logging, doesn't slow your agents down
42
+
43
+ ## API
44
+
45
+ ### `new AgentLedger(config)`
46
+
47
+ ```typescript
48
+ const ledger = new AgentLedger({
49
+ apiKey: 'al_...', // Required. Get from agentledger.co
50
+ baseUrl: 'https://...', // Optional. Default: https://agentledger.co
51
+ failOpen: true, // Optional. If true, agents run even if AgentLedger is unreachable
52
+ timeout: 5000, // Optional. API timeout in ms
53
+ onError: (err) => {}, // Optional. Called on communication errors
54
+ });
55
+ ```
56
+
57
+ ### `ledger.track(options, fn)`
58
+
59
+ Wrap an async function. Runs a pre-flight budget check, executes the function, logs the result.
60
+
61
+ ```typescript
62
+ const { result, allowed, durationMs } = await ledger.track({
63
+ agent: 'my-bot',
64
+ service: 'stripe',
65
+ action: 'charge',
66
+ costCents: 50,
67
+ metadata: { customerId: '123' },
68
+ }, async () => {
69
+ return await stripe.charges.create({ amount: 5000 });
70
+ });
71
+ ```
72
+
73
+ ### `ledger.check(options)`
74
+
75
+ Pre-flight check without executing. Use before expensive operations.
76
+
77
+ ```typescript
78
+ const { allowed, blockReason } = await ledger.check({
79
+ agent: 'my-bot',
80
+ service: 'openai',
81
+ action: 'completion',
82
+ });
83
+
84
+ if (!allowed) {
85
+ console.log(`Blocked: ${blockReason}`);
86
+ }
87
+ ```
88
+
89
+ ### `ledger.log(options)`
90
+
91
+ Manual logging without wrapping a function.
92
+
93
+ ```typescript
94
+ await ledger.log({
95
+ agent: 'my-bot',
96
+ service: 'sendgrid',
97
+ action: 'send_email',
98
+ costCents: 1,
99
+ durationMs: 340,
100
+ });
101
+ ```
102
+
103
+ ### `ledger.pauseAgent(name)` / `resumeAgent(name)` / `killAgent(name)`
104
+
105
+ Control agents programmatically.
106
+
107
+ ```typescript
108
+ await ledger.pauseAgent('runaway-bot'); // All future actions blocked
109
+ await ledger.resumeAgent('runaway-bot'); // Back in action
110
+ await ledger.killAgent('runaway-bot'); // Permanently stopped
111
+ ```
112
+
113
+ ## Framework Integrations
114
+
115
+ ### LangChain
116
+
117
+ ```typescript
118
+ import { AgentLedger } from 'agentledger';
119
+ import { AgentLedgerCallbackHandler } from 'agentledger/integrations/langchain';
120
+
121
+ const handler = new AgentLedgerCallbackHandler(ledger, {
122
+ agent: 'research-bot',
123
+ serviceMap: { 'tavily_search': { service: 'tavily', action: 'search' } },
124
+ });
125
+
126
+ const agent = createReactAgent({ llm, tools, callbacks: [handler] });
127
+ ```
128
+
129
+ ### OpenAI
130
+
131
+ ```typescript
132
+ import { createToolExecutor } from 'agentledger/integrations/openai';
133
+
134
+ const execute = createToolExecutor(ledger, 'my-agent', tools, serviceMap);
135
+ ```
136
+
137
+ ### MCP Servers
138
+
139
+ ```typescript
140
+ import { wrapMCPServer } from 'agentledger/integrations/mcp';
141
+
142
+ wrapMCPServer(ledger, server, { agent: 'my-mcp-server' });
143
+ ```
144
+
145
+ ### Express
146
+
147
+ ```typescript
148
+ import { agentLedgerMiddleware } from 'agentledger/integrations/express';
149
+
150
+ app.post('/api/action', agentLedgerMiddleware(ledger, {
151
+ agent: 'api-bot', service: 'internal', action: 'process',
152
+ }), handler);
153
+ ```
154
+
155
+ ## Self-Hosting
156
+
157
+ AgentLedger is open source. You can self-host the dashboard:
158
+
159
+ ```bash
160
+ git clone https://github.com/mnoonan/agentledger.git
161
+ cd agentledger
162
+ npm install && npm run dev
163
+ ```
164
+
165
+ Point your SDK to your own instance:
166
+
167
+ ```typescript
168
+ const ledger = new AgentLedger({
169
+ apiKey: 'al_...',
170
+ baseUrl: 'https://your-instance.com',
171
+ });
172
+ ```
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,94 @@
1
+ export interface AgentLedgerConfig {
2
+ /** Your AgentLedger API key (starts with al_) */
3
+ apiKey: string;
4
+ /** Base URL for the AgentLedger API. Default: https://agentledger.co */
5
+ baseUrl?: string;
6
+ /** If true, actions proceed even if AgentLedger is unreachable. Default: true */
7
+ failOpen?: boolean;
8
+ /** Timeout in ms for API calls. Default: 5000 */
9
+ timeout?: number;
10
+ /** Called when an error occurs communicating with AgentLedger */
11
+ onError?: (error: Error) => void;
12
+ }
13
+ export interface TrackOptions {
14
+ /** Name of the agent performing the action */
15
+ agent: string;
16
+ /** Service being called (e.g. 'slack', 'stripe', 'openai') */
17
+ service: string;
18
+ /** Action being performed (e.g. 'send_message', 'charge', 'completion') */
19
+ action: string;
20
+ /** Estimated cost in cents. Auto-calculated from duration if not provided. */
21
+ costCents?: number;
22
+ /** Additional metadata to log with the action */
23
+ metadata?: Record<string, unknown>;
24
+ }
25
+ export interface TrackResult<T> {
26
+ /** The return value of the wrapped function */
27
+ result: T;
28
+ /** Whether AgentLedger allowed the action */
29
+ allowed: boolean;
30
+ /** Duration of the action in milliseconds */
31
+ durationMs: number;
32
+ /** The logged action ID */
33
+ actionId?: string;
34
+ }
35
+ export interface CheckResult {
36
+ allowed: boolean;
37
+ blockReason?: string;
38
+ remainingBudget?: {
39
+ actions?: number;
40
+ costCents?: number;
41
+ };
42
+ }
43
+ export declare class AgentLedger {
44
+ private apiKey;
45
+ private baseUrl;
46
+ private failOpen;
47
+ private timeout;
48
+ private onError?;
49
+ constructor(config: AgentLedgerConfig);
50
+ /**
51
+ * Track an agent action. Wraps an async function with logging and budget checks.
52
+ *
53
+ * @example
54
+ * const result = await ledger.track({
55
+ * agent: 'support-bot',
56
+ * service: 'slack',
57
+ * action: 'send_message',
58
+ * }, async () => {
59
+ * return await slack.chat.postMessage({ channel: '#support', text: 'Hello!' });
60
+ * });
61
+ */
62
+ track<T>(options: TrackOptions, fn: () => Promise<T>): Promise<TrackResult<T>>;
63
+ /**
64
+ * Check if an action is allowed without executing it.
65
+ * Useful for pre-flight checks before expensive operations.
66
+ */
67
+ check(options: Pick<TrackOptions, 'agent' | 'service' | 'action'>): Promise<CheckResult>;
68
+ /**
69
+ * Log an action directly without wrapping a function.
70
+ * Useful when you want manual control over timing.
71
+ */
72
+ log(options: TrackOptions & {
73
+ status?: string;
74
+ durationMs?: number;
75
+ }): Promise<{
76
+ id?: string;
77
+ }>;
78
+ /**
79
+ * Pause an agent. All future actions will be blocked until resumed.
80
+ */
81
+ pauseAgent(name: string): Promise<void>;
82
+ /**
83
+ * Resume a paused agent.
84
+ */
85
+ resumeAgent(name: string): Promise<void>;
86
+ /**
87
+ * Kill an agent permanently. All future actions will be blocked.
88
+ */
89
+ killAgent(name: string): Promise<void>;
90
+ private logAction;
91
+ private fetch;
92
+ private handleError;
93
+ }
94
+ export default AgentLedger;
package/dist/index.js ADDED
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentLedger = void 0;
4
+ class AgentLedger {
5
+ constructor(config) {
6
+ if (!config.apiKey || !config.apiKey.startsWith('al_')) {
7
+ throw new Error('AgentLedger: Invalid API key. Keys start with "al_".');
8
+ }
9
+ this.apiKey = config.apiKey;
10
+ this.baseUrl = (config.baseUrl || 'https://agentledger.co').replace(/\/$/, '');
11
+ this.failOpen = config.failOpen !== false; // default true
12
+ this.timeout = config.timeout || 5000;
13
+ this.onError = config.onError;
14
+ }
15
+ /**
16
+ * Track an agent action. Wraps an async function with logging and budget checks.
17
+ *
18
+ * @example
19
+ * const result = await ledger.track({
20
+ * agent: 'support-bot',
21
+ * service: 'slack',
22
+ * action: 'send_message',
23
+ * }, async () => {
24
+ * return await slack.chat.postMessage({ channel: '#support', text: 'Hello!' });
25
+ * });
26
+ */
27
+ async track(options, fn) {
28
+ // Pre-flight check
29
+ let allowed = true;
30
+ try {
31
+ const check = await this.check(options);
32
+ allowed = check.allowed;
33
+ if (!allowed) {
34
+ throw new Error(`AgentLedger: Action blocked - ${check.blockReason || 'budget exceeded'}`);
35
+ }
36
+ }
37
+ catch (err) {
38
+ if (err instanceof Error && err.message.startsWith('AgentLedger: Action blocked')) {
39
+ throw err;
40
+ }
41
+ // Communication error — fail open or closed
42
+ if (!this.failOpen) {
43
+ throw new Error('AgentLedger: Cannot verify action (fail-closed mode)');
44
+ }
45
+ this.handleError(err);
46
+ }
47
+ // Execute the action
48
+ const start = Date.now();
49
+ let status = 'success';
50
+ let result;
51
+ try {
52
+ result = await fn();
53
+ }
54
+ catch (err) {
55
+ status = 'error';
56
+ const durationMs = Date.now() - start;
57
+ // Log the error, then re-throw
58
+ this.logAction(options, status, durationMs).catch(this.handleError.bind(this));
59
+ throw err;
60
+ }
61
+ const durationMs = Date.now() - start;
62
+ // Log the action (fire and forget for speed, unless we need the ID)
63
+ let actionId;
64
+ try {
65
+ const logResult = await this.logAction(options, status, durationMs);
66
+ actionId = logResult?.id;
67
+ }
68
+ catch (err) {
69
+ this.handleError(err);
70
+ }
71
+ return { result, allowed, durationMs, actionId };
72
+ }
73
+ /**
74
+ * Check if an action is allowed without executing it.
75
+ * Useful for pre-flight checks before expensive operations.
76
+ */
77
+ async check(options) {
78
+ const res = await this.fetch('/api/v1/check', {
79
+ method: 'POST',
80
+ body: JSON.stringify({
81
+ agent: options.agent,
82
+ service: options.service,
83
+ action: options.action,
84
+ }),
85
+ });
86
+ if (!res.ok) {
87
+ throw new Error(`AgentLedger: Check failed (${res.status})`);
88
+ }
89
+ return res.json();
90
+ }
91
+ /**
92
+ * Log an action directly without wrapping a function.
93
+ * Useful when you want manual control over timing.
94
+ */
95
+ async log(options) {
96
+ return this.logAction(options, options.status || 'success', options.durationMs || 0);
97
+ }
98
+ /**
99
+ * Pause an agent. All future actions will be blocked until resumed.
100
+ */
101
+ async pauseAgent(name) {
102
+ const res = await this.fetch(`/api/v1/agents/${encodeURIComponent(name)}/pause`, {
103
+ method: 'POST',
104
+ });
105
+ if (!res.ok)
106
+ throw new Error(`AgentLedger: Failed to pause agent (${res.status})`);
107
+ }
108
+ /**
109
+ * Resume a paused agent.
110
+ */
111
+ async resumeAgent(name) {
112
+ const res = await this.fetch(`/api/v1/agents/${encodeURIComponent(name)}/resume`, {
113
+ method: 'POST',
114
+ });
115
+ if (!res.ok)
116
+ throw new Error(`AgentLedger: Failed to resume agent (${res.status})`);
117
+ }
118
+ /**
119
+ * Kill an agent permanently. All future actions will be blocked.
120
+ */
121
+ async killAgent(name) {
122
+ const res = await this.fetch(`/api/v1/agents/${encodeURIComponent(name)}/kill`, {
123
+ method: 'POST',
124
+ });
125
+ if (!res.ok)
126
+ throw new Error(`AgentLedger: Failed to kill agent (${res.status})`);
127
+ }
128
+ // Internal: log action to API
129
+ async logAction(options, status, durationMs) {
130
+ const res = await this.fetch('/api/v1/actions', {
131
+ method: 'POST',
132
+ body: JSON.stringify({
133
+ agent: options.agent,
134
+ service: options.service,
135
+ action: options.action,
136
+ status,
137
+ cost_cents: options.costCents || 0,
138
+ duration_ms: durationMs,
139
+ metadata: options.metadata || {},
140
+ }),
141
+ });
142
+ if (!res.ok) {
143
+ throw new Error(`AgentLedger: Failed to log action (${res.status})`);
144
+ }
145
+ const data = await res.json();
146
+ return { id: data.id };
147
+ }
148
+ // Internal: fetch with timeout and auth
149
+ async fetch(path, init = {}) {
150
+ const controller = new AbortController();
151
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
152
+ try {
153
+ return await fetch(`${this.baseUrl}${path}`, {
154
+ ...init,
155
+ signal: controller.signal,
156
+ headers: {
157
+ 'Content-Type': 'application/json',
158
+ Authorization: `Bearer ${this.apiKey}`,
159
+ ...(init.headers || {}),
160
+ },
161
+ });
162
+ }
163
+ finally {
164
+ clearTimeout(timeoutId);
165
+ }
166
+ }
167
+ handleError(err) {
168
+ const error = err instanceof Error ? err : new Error(String(err));
169
+ if (this.onError) {
170
+ this.onError(error);
171
+ }
172
+ }
173
+ }
174
+ exports.AgentLedger = AgentLedger;
175
+ exports.default = AgentLedger;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * AgentLedger Express middleware and generic HTTP integration.
3
+ *
4
+ * Usage with Express:
5
+ * import { AgentLedger } from '@agentledger/sdk';
6
+ * import { agentLedgerMiddleware } from '@agentledger/sdk/integrations/express';
7
+ *
8
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
9
+ *
10
+ * // Track all requests to a specific route
11
+ * app.post('/api/agent/send-email', agentLedgerMiddleware(ledger, {
12
+ * agent: 'email-bot',
13
+ * service: 'sendgrid',
14
+ * action: 'send_email',
15
+ * }), (req, res) => {
16
+ * // your handler
17
+ * });
18
+ *
19
+ * // Or track all routes with auto-detection
20
+ * app.use('/api/agent', agentLedgerMiddleware(ledger, {
21
+ * agent: 'my-agent',
22
+ * autoDetect: true, // uses req.path as action, req.method as metadata
23
+ * }));
24
+ */
25
+ import type { AgentLedger, TrackOptions } from '../index';
26
+ export interface MiddlewareConfig {
27
+ /** Agent name */
28
+ agent: string;
29
+ /** Service name. If autoDetect is true, can be omitted */
30
+ service?: string;
31
+ /** Action name. If autoDetect is true, uses req.path */
32
+ action?: string;
33
+ /** Auto-detect service/action from request path */
34
+ autoDetect?: boolean;
35
+ /** Custom cost extractor from request/response */
36
+ costExtractor?: (req: unknown, res: unknown) => number;
37
+ }
38
+ /**
39
+ * Express-compatible middleware that tracks requests as agent actions.
40
+ */
41
+ export declare function agentLedgerMiddleware(ledger: AgentLedger, config: MiddlewareConfig): (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => Promise<void>;
42
+ /**
43
+ * Simple wrapper for any async function — no Express dependency needed.
44
+ * Wraps a function with AgentLedger tracking and returns a new function
45
+ * with the same signature.
46
+ *
47
+ * Usage:
48
+ * const trackedFn = trackFunction(ledger, {
49
+ * agent: 'my-bot',
50
+ * service: 'slack',
51
+ * action: 'send_message',
52
+ * }, slackSendMessage);
53
+ *
54
+ * await trackedFn('#general', 'Hello!');
55
+ */
56
+ export declare function trackFunction<TArgs extends unknown[], TResult>(ledger: AgentLedger, options: Pick<TrackOptions, 'agent' | 'service' | 'action'>, fn: (...args: TArgs) => Promise<TResult>): (...args: TArgs) => Promise<TResult>;
57
+ interface ExpressRequest {
58
+ method: string;
59
+ path?: string;
60
+ url?: string;
61
+ [key: string]: unknown;
62
+ }
63
+ interface ExpressResponse {
64
+ statusCode: number;
65
+ end: (...args: unknown[]) => ExpressResponse;
66
+ [key: string]: unknown;
67
+ }
68
+ type NextFunction = (err?: unknown) => void;
69
+ export {};
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * AgentLedger Express middleware and generic HTTP integration.
4
+ *
5
+ * Usage with Express:
6
+ * import { AgentLedger } from '@agentledger/sdk';
7
+ * import { agentLedgerMiddleware } from '@agentledger/sdk/integrations/express';
8
+ *
9
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
10
+ *
11
+ * // Track all requests to a specific route
12
+ * app.post('/api/agent/send-email', agentLedgerMiddleware(ledger, {
13
+ * agent: 'email-bot',
14
+ * service: 'sendgrid',
15
+ * action: 'send_email',
16
+ * }), (req, res) => {
17
+ * // your handler
18
+ * });
19
+ *
20
+ * // Or track all routes with auto-detection
21
+ * app.use('/api/agent', agentLedgerMiddleware(ledger, {
22
+ * agent: 'my-agent',
23
+ * autoDetect: true, // uses req.path as action, req.method as metadata
24
+ * }));
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.agentLedgerMiddleware = agentLedgerMiddleware;
28
+ exports.trackFunction = trackFunction;
29
+ /**
30
+ * Express-compatible middleware that tracks requests as agent actions.
31
+ */
32
+ function agentLedgerMiddleware(ledger, config) {
33
+ return async (req, res, next) => {
34
+ const start = Date.now();
35
+ // Determine service/action
36
+ let service = config.service || 'http';
37
+ let action = config.action || 'request';
38
+ if (config.autoDetect) {
39
+ // /api/agent/send-email → service: 'agent', action: 'send-email'
40
+ const pathParts = (req.path || req.url || '').split('/').filter(Boolean);
41
+ if (pathParts.length >= 2) {
42
+ service = pathParts[pathParts.length - 2] || service;
43
+ action = pathParts[pathParts.length - 1] || action;
44
+ }
45
+ else if (pathParts.length === 1) {
46
+ action = pathParts[0] || action;
47
+ }
48
+ }
49
+ // Hook into response finish to log with timing
50
+ const originalEnd = res.end.bind(res);
51
+ let logged = false;
52
+ res.end = function (...args) {
53
+ if (!logged) {
54
+ logged = true;
55
+ const durationMs = Date.now() - start;
56
+ const status = res.statusCode >= 400 ? 'error' : 'success';
57
+ const costCents = config.costExtractor ? config.costExtractor(req, res) : 0;
58
+ // Fire and forget — don't block the response
59
+ ledger.log({
60
+ agent: config.agent,
61
+ service,
62
+ action,
63
+ status,
64
+ durationMs,
65
+ costCents,
66
+ metadata: {
67
+ source: 'express',
68
+ method: req.method,
69
+ path: req.path || req.url,
70
+ statusCode: res.statusCode,
71
+ },
72
+ }).catch(() => { }); // fail-open
73
+ }
74
+ return originalEnd(...args);
75
+ };
76
+ next();
77
+ };
78
+ }
79
+ /**
80
+ * Simple wrapper for any async function — no Express dependency needed.
81
+ * Wraps a function with AgentLedger tracking and returns a new function
82
+ * with the same signature.
83
+ *
84
+ * Usage:
85
+ * const trackedFn = trackFunction(ledger, {
86
+ * agent: 'my-bot',
87
+ * service: 'slack',
88
+ * action: 'send_message',
89
+ * }, slackSendMessage);
90
+ *
91
+ * await trackedFn('#general', 'Hello!');
92
+ */
93
+ function trackFunction(ledger, options, fn) {
94
+ return async (...args) => {
95
+ const { result } = await ledger.track({
96
+ agent: options.agent,
97
+ service: options.service,
98
+ action: options.action,
99
+ metadata: { source: 'tracked-function' },
100
+ }, () => fn(...args));
101
+ return result;
102
+ };
103
+ }
@@ -0,0 +1,7 @@
1
+ export { AgentLedgerCallbackHandler } from './langchain';
2
+ export type { AgentLedgerCallbackConfig } from './langchain';
3
+ export { withAgentLedger, createToolExecutor, wrapOpenAICompletion } from './openai';
4
+ export { wrapMCPServer, wrapMCPTool } from './mcp';
5
+ export type { MCPWrapConfig } from './mcp';
6
+ export { agentLedgerMiddleware, trackFunction } from './express';
7
+ export type { MiddlewareConfig } from './express';
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackFunction = exports.agentLedgerMiddleware = exports.wrapMCPTool = exports.wrapMCPServer = exports.wrapOpenAICompletion = exports.createToolExecutor = exports.withAgentLedger = exports.AgentLedgerCallbackHandler = void 0;
4
+ var langchain_1 = require("./langchain");
5
+ Object.defineProperty(exports, "AgentLedgerCallbackHandler", { enumerable: true, get: function () { return langchain_1.AgentLedgerCallbackHandler; } });
6
+ var openai_1 = require("./openai");
7
+ Object.defineProperty(exports, "withAgentLedger", { enumerable: true, get: function () { return openai_1.withAgentLedger; } });
8
+ Object.defineProperty(exports, "createToolExecutor", { enumerable: true, get: function () { return openai_1.createToolExecutor; } });
9
+ Object.defineProperty(exports, "wrapOpenAICompletion", { enumerable: true, get: function () { return openai_1.wrapOpenAICompletion; } });
10
+ var mcp_1 = require("./mcp");
11
+ Object.defineProperty(exports, "wrapMCPServer", { enumerable: true, get: function () { return mcp_1.wrapMCPServer; } });
12
+ Object.defineProperty(exports, "wrapMCPTool", { enumerable: true, get: function () { return mcp_1.wrapMCPTool; } });
13
+ var express_1 = require("./express");
14
+ Object.defineProperty(exports, "agentLedgerMiddleware", { enumerable: true, get: function () { return express_1.agentLedgerMiddleware; } });
15
+ Object.defineProperty(exports, "trackFunction", { enumerable: true, get: function () { return express_1.trackFunction; } });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * AgentLedger integration for LangChain.
3
+ *
4
+ * Usage:
5
+ * import { AgentLedger } from '@agentledger/sdk';
6
+ * import { AgentLedgerCallbackHandler } from '@agentledger/sdk/integrations/langchain';
7
+ *
8
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
9
+ * const handler = new AgentLedgerCallbackHandler(ledger, { agent: 'my-agent' });
10
+ *
11
+ * const chain = new ChatOpenAI({ callbacks: [handler] });
12
+ * // or
13
+ * await agent.invoke({ input: '...' }, { callbacks: [handler] });
14
+ */
15
+ import type { AgentLedger } from '../index';
16
+ interface Serialized {
17
+ id?: string[];
18
+ name?: string;
19
+ [key: string]: unknown;
20
+ }
21
+ export interface AgentLedgerCallbackConfig {
22
+ /** Name of the agent in AgentLedger */
23
+ agent: string;
24
+ /** Map LangChain tool names to AgentLedger services. Default: tool name becomes both service and action */
25
+ serviceMap?: Record<string, {
26
+ service: string;
27
+ action?: string;
28
+ }>;
29
+ /** Whether to track LLM calls (tokens/cost). Default: true */
30
+ trackLLM?: boolean;
31
+ /** Whether to track tool calls. Default: true */
32
+ trackTools?: boolean;
33
+ /** Whether to track chain/agent runs. Default: false */
34
+ trackChains?: boolean;
35
+ }
36
+ export declare class AgentLedgerCallbackHandler {
37
+ name: string;
38
+ private ledger;
39
+ private config;
40
+ private runTimers;
41
+ constructor(ledger: AgentLedger, config: AgentLedgerCallbackConfig);
42
+ handleToolStart(tool: Serialized, input: string, runId: string): Promise<void>;
43
+ handleToolEnd(output: string, runId: string): Promise<void>;
44
+ handleToolError(err: Error, runId: string): Promise<void>;
45
+ handleLLMStart(llm: Serialized, prompts: string[], runId: string): Promise<void>;
46
+ handleLLMEnd(output: unknown, runId: string): Promise<void>;
47
+ handleLLMError(err: Error, runId: string): Promise<void>;
48
+ handleChainStart(chain: Serialized, inputs: Record<string, unknown>, runId: string): Promise<void>;
49
+ handleChainEnd(outputs: Record<string, unknown>, runId: string): Promise<void>;
50
+ handleChainError(err: Error, runId: string): Promise<void>;
51
+ private toolNameStore;
52
+ private extractToolName;
53
+ }
54
+ export {};
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * AgentLedger integration for LangChain.
4
+ *
5
+ * Usage:
6
+ * import { AgentLedger } from '@agentledger/sdk';
7
+ * import { AgentLedgerCallbackHandler } from '@agentledger/sdk/integrations/langchain';
8
+ *
9
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
10
+ * const handler = new AgentLedgerCallbackHandler(ledger, { agent: 'my-agent' });
11
+ *
12
+ * const chain = new ChatOpenAI({ callbacks: [handler] });
13
+ * // or
14
+ * await agent.invoke({ input: '...' }, { callbacks: [handler] });
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.AgentLedgerCallbackHandler = void 0;
18
+ class AgentLedgerCallbackHandler {
19
+ constructor(ledger, config) {
20
+ this.name = 'AgentLedgerCallbackHandler';
21
+ this.runTimers = new Map();
22
+ // ==================== HELPERS ====================
23
+ this.toolNameStore = new Map();
24
+ this.ledger = ledger;
25
+ this.config = {
26
+ trackLLM: true,
27
+ trackTools: true,
28
+ trackChains: false,
29
+ ...config,
30
+ };
31
+ }
32
+ // ==================== TOOL CALLS ====================
33
+ async handleToolStart(tool, input, runId) {
34
+ if (!this.config.trackTools)
35
+ return;
36
+ this.runTimers.set(runId, Date.now());
37
+ }
38
+ async handleToolEnd(output, runId) {
39
+ if (!this.config.trackTools)
40
+ return;
41
+ const startTime = this.runTimers.get(runId);
42
+ const durationMs = startTime ? Date.now() - startTime : 0;
43
+ this.runTimers.delete(runId);
44
+ // Extract tool name from runId context (stored during handleToolStart)
45
+ const toolName = this.extractToolName(runId) || 'unknown_tool';
46
+ const mapped = this.config.serviceMap?.[toolName];
47
+ await this.ledger.log({
48
+ agent: this.config.agent,
49
+ service: mapped?.service || toolName,
50
+ action: mapped?.action || 'invoke',
51
+ durationMs,
52
+ metadata: { source: 'langchain', runId, outputLength: output?.length },
53
+ }).catch(() => { }); // fail-open
54
+ }
55
+ async handleToolError(err, runId) {
56
+ if (!this.config.trackTools)
57
+ return;
58
+ const startTime = this.runTimers.get(runId);
59
+ const durationMs = startTime ? Date.now() - startTime : 0;
60
+ this.runTimers.delete(runId);
61
+ const toolName = this.extractToolName(runId) || 'unknown_tool';
62
+ const mapped = this.config.serviceMap?.[toolName];
63
+ await this.ledger.log({
64
+ agent: this.config.agent,
65
+ service: mapped?.service || toolName,
66
+ action: mapped?.action || 'invoke',
67
+ status: 'error',
68
+ durationMs,
69
+ metadata: { source: 'langchain', runId, error: err.message },
70
+ }).catch(() => { });
71
+ }
72
+ // ==================== LLM CALLS ====================
73
+ async handleLLMStart(llm, prompts, runId) {
74
+ if (!this.config.trackLLM)
75
+ return;
76
+ this.runTimers.set(runId, Date.now());
77
+ }
78
+ async handleLLMEnd(output, runId) {
79
+ if (!this.config.trackLLM)
80
+ return;
81
+ const startTime = this.runTimers.get(runId);
82
+ const durationMs = startTime ? Date.now() - startTime : 0;
83
+ this.runTimers.delete(runId);
84
+ // Try to extract token usage and model info
85
+ const llmOutput = output;
86
+ const tokenUsage = llmOutput?.llmOutput?.tokenUsage;
87
+ const model = llmOutput?.llmOutput?.modelName;
88
+ // Estimate cost from tokens (rough: $0.01 per 1K tokens for GPT-4 class)
89
+ const totalTokens = tokenUsage?.totalTokens || 0;
90
+ const estimatedCostCents = Math.ceil(totalTokens * 0.001);
91
+ await this.ledger.log({
92
+ agent: this.config.agent,
93
+ service: model?.includes('claude') ? 'anthropic' : model?.includes('gpt') ? 'openai' : 'llm',
94
+ action: 'completion',
95
+ costCents: estimatedCostCents,
96
+ durationMs,
97
+ metadata: { source: 'langchain', runId, model, tokenUsage },
98
+ }).catch(() => { });
99
+ }
100
+ async handleLLMError(err, runId) {
101
+ if (!this.config.trackLLM)
102
+ return;
103
+ const startTime = this.runTimers.get(runId);
104
+ const durationMs = startTime ? Date.now() - startTime : 0;
105
+ this.runTimers.delete(runId);
106
+ await this.ledger.log({
107
+ agent: this.config.agent,
108
+ service: 'llm',
109
+ action: 'completion',
110
+ status: 'error',
111
+ durationMs,
112
+ metadata: { source: 'langchain', runId, error: err.message },
113
+ }).catch(() => { });
114
+ }
115
+ // ==================== CHAIN/AGENT RUNS ====================
116
+ async handleChainStart(chain, inputs, runId) {
117
+ if (!this.config.trackChains)
118
+ return;
119
+ this.runTimers.set(runId, Date.now());
120
+ }
121
+ async handleChainEnd(outputs, runId) {
122
+ if (!this.config.trackChains)
123
+ return;
124
+ const startTime = this.runTimers.get(runId);
125
+ const durationMs = startTime ? Date.now() - startTime : 0;
126
+ this.runTimers.delete(runId);
127
+ await this.ledger.log({
128
+ agent: this.config.agent,
129
+ service: 'langchain',
130
+ action: 'chain_run',
131
+ durationMs,
132
+ metadata: { source: 'langchain', runId },
133
+ }).catch(() => { });
134
+ }
135
+ async handleChainError(err, runId) {
136
+ if (!this.config.trackChains)
137
+ return;
138
+ const startTime = this.runTimers.get(runId);
139
+ const durationMs = startTime ? Date.now() - startTime : 0;
140
+ this.runTimers.delete(runId);
141
+ await this.ledger.log({
142
+ agent: this.config.agent,
143
+ service: 'langchain',
144
+ action: 'chain_run',
145
+ status: 'error',
146
+ durationMs,
147
+ metadata: { source: 'langchain', runId, error: err.message },
148
+ }).catch(() => { });
149
+ }
150
+ extractToolName(runId) {
151
+ return this.toolNameStore.get(runId);
152
+ }
153
+ }
154
+ exports.AgentLedgerCallbackHandler = AgentLedgerCallbackHandler;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * AgentLedger integration for MCP (Model Context Protocol) servers.
3
+ *
4
+ * Wraps MCP tool handlers so every tool invocation is logged.
5
+ *
6
+ * Usage with @modelcontextprotocol/sdk:
7
+ *
8
+ * import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
+ * import { AgentLedger } from '@agentledger/sdk';
10
+ * import { wrapMCPServer } from '@agentledger/sdk/integrations/mcp';
11
+ *
12
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
13
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
14
+ *
15
+ * // Register tools normally
16
+ * server.tool('send_email', { to: z.string(), body: z.string() }, async (args) => {
17
+ * return await sendEmail(args.to, args.body);
18
+ * });
19
+ *
20
+ * // Wrap the server — all tool calls are now logged
21
+ * wrapMCPServer(ledger, server, { agent: 'my-mcp-server' });
22
+ */
23
+ import type { AgentLedger } from '../index';
24
+ export interface MCPWrapConfig {
25
+ /** Agent name in AgentLedger */
26
+ agent: string;
27
+ /** Map tool names to AgentLedger service/action. Default: tool name as service, 'invoke' as action */
28
+ serviceMap?: Record<string, {
29
+ service: string;
30
+ action?: string;
31
+ }>;
32
+ }
33
+ /**
34
+ * Wraps an MCP server instance to log all tool invocations.
35
+ * Works by monkey-patching the tool registration method.
36
+ *
37
+ * Compatible with @modelcontextprotocol/sdk McpServer.
38
+ */
39
+ export declare function wrapMCPServer(ledger: AgentLedger, server: MCPServerLike, config: MCPWrapConfig): void;
40
+ /**
41
+ * Alternative: wrap a single MCP tool handler function directly.
42
+ *
43
+ * Usage:
44
+ * server.tool('send_email', schema, wrapMCPTool(ledger, {
45
+ * agent: 'my-server',
46
+ * service: 'sendgrid',
47
+ * action: 'send_email',
48
+ * }, async (args) => {
49
+ * return await sendEmail(args.to, args.body);
50
+ * }));
51
+ */
52
+ export declare function wrapMCPTool<TArgs, TResult>(ledger: AgentLedger, options: {
53
+ agent: string;
54
+ service: string;
55
+ action: string;
56
+ }, handler: (args: TArgs) => Promise<TResult>): (args: TArgs) => Promise<TResult>;
57
+ /**
58
+ * Minimal interface for MCP server compatibility.
59
+ * We don't import @modelcontextprotocol/sdk to keep zero dependencies.
60
+ */
61
+ interface MCPServerLike {
62
+ tool: (name: string, ...args: unknown[]) => unknown;
63
+ }
64
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * AgentLedger integration for MCP (Model Context Protocol) servers.
4
+ *
5
+ * Wraps MCP tool handlers so every tool invocation is logged.
6
+ *
7
+ * Usage with @modelcontextprotocol/sdk:
8
+ *
9
+ * import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ * import { AgentLedger } from '@agentledger/sdk';
11
+ * import { wrapMCPServer } from '@agentledger/sdk/integrations/mcp';
12
+ *
13
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
14
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
15
+ *
16
+ * // Register tools normally
17
+ * server.tool('send_email', { to: z.string(), body: z.string() }, async (args) => {
18
+ * return await sendEmail(args.to, args.body);
19
+ * });
20
+ *
21
+ * // Wrap the server — all tool calls are now logged
22
+ * wrapMCPServer(ledger, server, { agent: 'my-mcp-server' });
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.wrapMCPServer = wrapMCPServer;
26
+ exports.wrapMCPTool = wrapMCPTool;
27
+ /**
28
+ * Wraps an MCP server instance to log all tool invocations.
29
+ * Works by monkey-patching the tool registration method.
30
+ *
31
+ * Compatible with @modelcontextprotocol/sdk McpServer.
32
+ */
33
+ function wrapMCPServer(ledger, server, config) {
34
+ const originalTool = server.tool.bind(server);
35
+ server.tool = function wrappedTool(name, ...rest) {
36
+ // tool(name, schema, handler) or tool(name, description, schema, handler)
37
+ // The handler is always the last argument
38
+ const handler = rest[rest.length - 1];
39
+ const otherArgs = rest.slice(0, -1);
40
+ const wrappedHandler = async (...handlerArgs) => {
41
+ const mapped = config.serviceMap?.[name];
42
+ const { result } = await ledger.track({
43
+ agent: config.agent,
44
+ service: mapped?.service || name,
45
+ action: mapped?.action || 'invoke',
46
+ metadata: {
47
+ source: 'mcp',
48
+ toolName: name,
49
+ argsPreview: JSON.stringify(handlerArgs[0]).slice(0, 500),
50
+ },
51
+ }, () => handler(...handlerArgs));
52
+ return result;
53
+ };
54
+ return originalTool(name, ...otherArgs, wrappedHandler);
55
+ };
56
+ }
57
+ /**
58
+ * Alternative: wrap a single MCP tool handler function directly.
59
+ *
60
+ * Usage:
61
+ * server.tool('send_email', schema, wrapMCPTool(ledger, {
62
+ * agent: 'my-server',
63
+ * service: 'sendgrid',
64
+ * action: 'send_email',
65
+ * }, async (args) => {
66
+ * return await sendEmail(args.to, args.body);
67
+ * }));
68
+ */
69
+ function wrapMCPTool(ledger, options, handler) {
70
+ return async (args) => {
71
+ const { result } = await ledger.track({
72
+ agent: options.agent,
73
+ service: options.service,
74
+ action: options.action,
75
+ metadata: {
76
+ source: 'mcp',
77
+ argsPreview: JSON.stringify(args).slice(0, 500),
78
+ },
79
+ }, () => handler(args));
80
+ return result;
81
+ };
82
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * AgentLedger integration for the OpenAI Agents SDK.
3
+ *
4
+ * Wraps tool functions so every tool invocation is automatically logged.
5
+ *
6
+ * Usage:
7
+ * import { AgentLedger } from '@agentledger/sdk';
8
+ * import { withAgentLedger } from '@agentledger/sdk/integrations/openai';
9
+ *
10
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
11
+ *
12
+ * // Wrap individual tool functions
13
+ * const trackedSendEmail = withAgentLedger(ledger, {
14
+ * agent: 'support-bot',
15
+ * service: 'sendgrid',
16
+ * action: 'send_email',
17
+ * }, sendEmail);
18
+ *
19
+ * // Or wrap an entire tools array
20
+ * const tools = wrapOpenAITools(ledger, 'my-agent', [
21
+ * { type: 'function', function: { name: 'send_email', ... } }
22
+ * ]);
23
+ */
24
+ import type { AgentLedger, TrackOptions } from '../index';
25
+ /**
26
+ * Wraps a single async function with AgentLedger tracking.
27
+ * The function signature is preserved — drop-in replacement.
28
+ */
29
+ export declare function withAgentLedger<TArgs extends unknown[], TResult>(ledger: AgentLedger, options: Pick<TrackOptions, 'agent' | 'service' | 'action'>, fn: (...args: TArgs) => Promise<TResult>): (...args: TArgs) => Promise<TResult>;
30
+ /**
31
+ * Map of tool names to their handler functions.
32
+ */
33
+ type ToolHandlers = Record<string, (args: Record<string, unknown>) => Promise<unknown>>;
34
+ /**
35
+ * Service mapping: how to categorize each tool for AgentLedger tracking.
36
+ * If a tool isn't in the map, its name is used as both service and action.
37
+ */
38
+ type ServiceMap = Record<string, {
39
+ service: string;
40
+ action?: string;
41
+ }>;
42
+ /**
43
+ * Creates a tool execution wrapper that logs all tool calls to AgentLedger.
44
+ *
45
+ * Usage:
46
+ * const executeTools = createToolExecutor(ledger, 'my-agent', {
47
+ * send_email: sendEmailFn,
48
+ * create_ticket: createTicketFn,
49
+ * }, {
50
+ * send_email: { service: 'sendgrid', action: 'send' },
51
+ * create_ticket: { service: 'jira', action: 'create_issue' },
52
+ * });
53
+ *
54
+ * // In your OpenAI agent loop:
55
+ * for (const toolCall of message.tool_calls) {
56
+ * const result = await executeTools(toolCall.function.name, JSON.parse(toolCall.function.arguments));
57
+ * }
58
+ */
59
+ export declare function createToolExecutor(ledger: AgentLedger, agent: string, handlers: ToolHandlers, serviceMap?: ServiceMap): (toolName: string, args: Record<string, unknown>) => Promise<unknown>;
60
+ /**
61
+ * Convenience: wraps the OpenAI chat.completions.create call itself to track LLM usage.
62
+ */
63
+ export declare function wrapOpenAICompletion<TArgs extends unknown[], TResult>(ledger: AgentLedger, agent: string, createFn: (...args: TArgs) => Promise<TResult>): (...args: TArgs) => Promise<TResult>;
64
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * AgentLedger integration for the OpenAI Agents SDK.
4
+ *
5
+ * Wraps tool functions so every tool invocation is automatically logged.
6
+ *
7
+ * Usage:
8
+ * import { AgentLedger } from '@agentledger/sdk';
9
+ * import { withAgentLedger } from '@agentledger/sdk/integrations/openai';
10
+ *
11
+ * const ledger = new AgentLedger({ apiKey: 'al_...' });
12
+ *
13
+ * // Wrap individual tool functions
14
+ * const trackedSendEmail = withAgentLedger(ledger, {
15
+ * agent: 'support-bot',
16
+ * service: 'sendgrid',
17
+ * action: 'send_email',
18
+ * }, sendEmail);
19
+ *
20
+ * // Or wrap an entire tools array
21
+ * const tools = wrapOpenAITools(ledger, 'my-agent', [
22
+ * { type: 'function', function: { name: 'send_email', ... } }
23
+ * ]);
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.withAgentLedger = withAgentLedger;
27
+ exports.createToolExecutor = createToolExecutor;
28
+ exports.wrapOpenAICompletion = wrapOpenAICompletion;
29
+ /**
30
+ * Wraps a single async function with AgentLedger tracking.
31
+ * The function signature is preserved — drop-in replacement.
32
+ */
33
+ function withAgentLedger(ledger, options, fn) {
34
+ return async (...args) => {
35
+ const { result } = await ledger.track({
36
+ agent: options.agent,
37
+ service: options.service,
38
+ action: options.action,
39
+ metadata: {
40
+ source: 'openai-agents',
41
+ argsPreview: JSON.stringify(args).slice(0, 500),
42
+ },
43
+ }, () => fn(...args));
44
+ return result;
45
+ };
46
+ }
47
+ /**
48
+ * Creates a tool execution wrapper that logs all tool calls to AgentLedger.
49
+ *
50
+ * Usage:
51
+ * const executeTools = createToolExecutor(ledger, 'my-agent', {
52
+ * send_email: sendEmailFn,
53
+ * create_ticket: createTicketFn,
54
+ * }, {
55
+ * send_email: { service: 'sendgrid', action: 'send' },
56
+ * create_ticket: { service: 'jira', action: 'create_issue' },
57
+ * });
58
+ *
59
+ * // In your OpenAI agent loop:
60
+ * for (const toolCall of message.tool_calls) {
61
+ * const result = await executeTools(toolCall.function.name, JSON.parse(toolCall.function.arguments));
62
+ * }
63
+ */
64
+ function createToolExecutor(ledger, agent, handlers, serviceMap) {
65
+ return async (toolName, args) => {
66
+ const handler = handlers[toolName];
67
+ if (!handler) {
68
+ throw new Error(`Unknown tool: ${toolName}`);
69
+ }
70
+ const mapped = serviceMap?.[toolName];
71
+ const { result } = await ledger.track({
72
+ agent,
73
+ service: mapped?.service || toolName,
74
+ action: mapped?.action || 'invoke',
75
+ metadata: {
76
+ source: 'openai-agents',
77
+ toolName,
78
+ argsPreview: JSON.stringify(args).slice(0, 500),
79
+ },
80
+ }, () => handler(args));
81
+ return result;
82
+ };
83
+ }
84
+ /**
85
+ * Convenience: wraps the OpenAI chat.completions.create call itself to track LLM usage.
86
+ */
87
+ function wrapOpenAICompletion(ledger, agent, createFn) {
88
+ return withAgentLedger(ledger, { agent, service: 'openai', action: 'completion' }, createFn);
89
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "agentledger",
3
+ "version": "0.3.0",
4
+ "description": "Track, monitor, and control AI agent actions. The missing observability layer for AI agents. Zero dependencies.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./integrations/langchain": {
14
+ "import": "./dist/integrations/langchain.js",
15
+ "types": "./dist/integrations/langchain.d.ts"
16
+ },
17
+ "./integrations/openai": {
18
+ "import": "./dist/integrations/openai.js",
19
+ "types": "./dist/integrations/openai.d.ts"
20
+ },
21
+ "./integrations/mcp": {
22
+ "import": "./dist/integrations/mcp.js",
23
+ "types": "./dist/integrations/mcp.d.ts"
24
+ },
25
+ "./integrations/express": {
26
+ "import": "./dist/integrations/express.js",
27
+ "types": "./dist/integrations/express.d.ts"
28
+ }
29
+ },
30
+ "files": ["dist", "README.md"],
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "keywords": [
36
+ "ai", "agents", "monitoring", "observability", "langchain", "openai",
37
+ "mcp", "safety", "llm", "ai-agents", "kill-switch", "budget",
38
+ "cost-tracking", "anthropic", "claude"
39
+ ],
40
+ "author": "AgentLedger",
41
+ "license": "MIT",
42
+ "homepage": "https://agentledger.co",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/mnoonan/agentledger"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/mnoonan/agentledger/issues"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }