@agentwares/agentguard-sdk 0.1.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,64 @@
1
+ # @agentwares/agentguard-sdk
2
+
3
+ The [agentguard](../../apps/agentguard-cli) policy engine for tool calls that never go through MCP — OpenAI Agents SDK, LangChain, or plain functions — plus a guarded `fetch` that puts a **hard dollar limit on LLM token spend** across OpenAI, Anthropic and Gemini. Same `agentguard.yaml`, same caps, kill switch, approvals, dry-run and hash-chained audit log as the proxy; the CLI (`agentguard report`, `kill`, `approve`, `verify`) works on the same files.
4
+
5
+ ```sh
6
+ npm i @agentwares/agentguard-sdk
7
+ ```
8
+
9
+ ```ts
10
+ import { createGuard, createGuardedFetch, wrapOpenAIAgentsTools } from "@agentwares/agentguard-sdk";
11
+ import { Agent, run, tool } from "@openai/agents";
12
+ import OpenAI, { setDefaultOpenAIClient } from "@openai/agents-openai";
13
+
14
+ const ag = await createGuard({ policy: "agentguard.yaml" }); // file-backed: shares state with the CLI
15
+
16
+ // 1. tools: caps, approvals, loop breaker, dry-run — blocked calls return { code, cause, fix } as the tool output
17
+ const tools = wrapOpenAIAgentsTools(ag, [deleteContact, sendEmail, chargeCard]);
18
+
19
+ // 2. tokens: every OpenAI/Anthropic/Gemini response is priced and charged to the same spend_usd caps
20
+ setDefaultOpenAIClient(new OpenAI({ fetch: createGuardedFetch(ag) }));
21
+
22
+ await run(new Agent({ name: "ops", tools }), "clean up stale contacts");
23
+ console.log(await ag.reportMarkdown()); // what it did / would have destroyed / spent
24
+ ```
25
+
26
+ ## API
27
+
28
+ | | |
29
+ | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | `createGuard({ policy, runId?, agent?, onEvent?, memory?, env? })` | `policy` is a path to `agentguard.yaml` (file-backed state, shared with the CLI) or an inline policy object (in-memory). `agent` picks a scope from `agents:`. |
31
+ | `ag.wrap(fn, { name, annotations?, outputSchema?, onBlock? })` | wrap `(args) => result`; blocked calls throw `GuardError` (or return the body with `onBlock: "return"`) |
32
+ | `ag.wrapAll({ name: fn, … })` | wrap a map of functions by name |
33
+ | `wrapOpenAIAgentsTool(s)(ag, tool(s), opts?)` | wrap what `tool({...})` returns (duck-typed on `invoke`); blocked → error JSON as the tool output, faked → synthetic JSON |
34
+ | `wrapLangChainTool(s)(ag, tool(s), opts?)` | wrap a `StructuredTool` / `DynamicStructuredTool`; keeps the prototype, intercepts `_call` |
35
+ | `createGuardedFetch(ag, { fetch?, runId?, providers?, onSpend? })` | a `fetch` for `new OpenAI({ fetch })`, `new Anthropic({ fetch })`, Gemini REST: refuses calls once `spend_usd` is used up (402 `CAP_EXCEEDED`, 403 `KILLED`), prices every response (streamed too) with built-in list prices or `spend.models` overrides |
36
+ | `ag.spend(usd, label, { force? })` | record spend from anything else (a paid API); throws `CAP_EXCEEDED` unless `force` |
37
+ | `ag.halt(reason)` / `ag.resume()` | kill switch (writes the KILL file when file-backed) |
38
+ | `ag.pendingApprovals()` / `ag.approve(id)` / `ag.deny(id)` | approvals |
39
+ | `ag.newRun(id?)` / `ag.runId` | run identity for per-run caps and the loop window |
40
+ | `ag.status()` / `ag.audit()` / `ag.report()` / `ag.reportMarkdown()` | counters vs caps; the audit entries; the incident-shaped report |
41
+
42
+ Errors (`GuardError` or the returned body) always carry `{ code, cause, fix, retryable, details? }` with codes `KILLED`, `APPROVAL_REQUIRED`, `APPROVAL_DENIED`, `LOOP_DETECTED`, `CAP_EXCEEDED`, `TOOL_DENIED`, `UPSTREAM_ERROR`.
43
+
44
+ ## Spend on tokens
45
+
46
+ `createGuardedFetch` recognizes `api.openai.com` (chat completions and responses, streamed or not — OpenAI chat streams get `stream_options.include_usage` added), `api.anthropic.com` (`message_start` + `message_delta` usage) and `generativelanguage.googleapis.com` (`usageMetadata`). Prices: built-in list prices for current Claude / GPT / Gemini families (`DEFAULT_MODEL_PRICES` in core), overridden per pattern in the policy:
47
+
48
+ ```yaml
49
+ caps:
50
+ per_run: { spend_usd: 5 }
51
+ per_day: { spend_usd: 50 }
52
+ spend:
53
+ models:
54
+ "gpt-5*": { input_per_mtok: 1.25, output_per_mtok: 10, cached_input_per_mtok: 0.125 }
55
+ "my-finetune*": { input_per_mtok: 3, output_per_mtok: 12 }
56
+ ```
57
+
58
+ Money spent by a response that crosses the cap is still recorded (`reason: over cap after the fact`) and alerts fire; the next call is refused.
59
+
60
+ A model with no matching price — a fine-tune, or one newer than the built-in table — is recorded at $0 with `reason: no list price for "<model>"`, so `agentguard report` shows a model the budget is not covering instead of under-counting it silently. Add it under `spend.models` to bring it back under the cap.
61
+
62
+ ## Tests
63
+
64
+ `pnpm test` — plain functions, OpenAI-Agents-shaped and LangChain-shaped fakes (no SDK dependency), file-backed state shared with the CLI, guarded fetch with fake OpenAI/Anthropic responses including SSE streams.
@@ -0,0 +1,166 @@
1
+ import * as _agentwares_agentguard_core from '@agentwares/agentguard-core';
2
+ import { Guard, Policy, AgentScope, ToolAnnotationsLike, GuardResult, GuardErrorBody, AuditEntry, ApprovalRecord, Report, PolicyInput, GuardEvent, LlmUsage } from '@agentwares/agentguard-core';
3
+ export { GuardError, GuardErrorBody, GuardEvent, GuardResult, Policy, PolicyInput, Report } from '@agentwares/agentguard-core';
4
+ import { LoadedPolicy, FileKillSwitch } from '@agentwares/agentguard-core/node';
5
+
6
+ interface CreateGuardOptions {
7
+ /** path to `agentguard.yaml` (file-backed state, shared with the CLI) or an inline policy (in-memory) */
8
+ policy: string | PolicyInput | Policy;
9
+ /** run id for calls that do not pass one (default: generated) */
10
+ runId?: string;
11
+ /** agent scope by name (from `agents:` in the policy) */
12
+ agent?: string;
13
+ onEvent?: (event: GuardEvent) => void | Promise<void>;
14
+ /** in-memory even when `policy` is a path */
15
+ memory?: boolean;
16
+ env?: Record<string, string | undefined>;
17
+ }
18
+ interface ToolMeta {
19
+ name: string;
20
+ description?: string;
21
+ annotations?: ToolAnnotationsLike;
22
+ /** JSON Schema of the result — used to synthesize dry-run results */
23
+ outputSchema?: Record<string, unknown>;
24
+ /** override the guard's run id for this tool */
25
+ runId?: string;
26
+ }
27
+ interface WrapOptions extends ToolMeta {
28
+ /** `throw` (default): blocked calls throw `GuardError`; `return`: they return the error body */
29
+ onBlock?: "throw" | "return";
30
+ }
31
+ type AnyFn = (...args: never[]) => unknown;
32
+ declare function newRunId(now?: Date): string;
33
+ declare class AgentGuard {
34
+ readonly guard: Guard;
35
+ readonly policy: Policy;
36
+ readonly loaded?: LoadedPolicy;
37
+ readonly kill?: FileKillSwitch;
38
+ runId: string;
39
+ readonly agent: AgentScope | undefined;
40
+ constructor(guard: Guard, opts: {
41
+ runId: string;
42
+ agent?: AgentScope;
43
+ loaded?: LoadedPolicy;
44
+ kill?: FileKillSwitch;
45
+ });
46
+ /** Start a new run (new per-run caps and loop window). Returns the id. */
47
+ newRun(id?: string): string;
48
+ /** Run one guarded call. `execute` receives the (unchanged) arguments. */
49
+ run(meta: ToolMeta, args: unknown, execute: (args: unknown) => unknown): Promise<GuardResult>;
50
+ /** Wrap a plain function `(args) => result`. Blocked calls throw `GuardError` (or return the body). */
51
+ wrap<A, R>(fn: (args: A) => R | Promise<R>, opts: WrapOptions): (args: A) => Promise<R | GuardErrorBody>;
52
+ /** Wrap a map of named functions at once (`{ crm_get_contact: fn, ... }`). */
53
+ wrapAll<T extends Record<string, AnyFn>>(fns: T, opts?: {
54
+ annotations?: Record<string, ToolAnnotationsLike>;
55
+ onBlock?: "throw" | "return";
56
+ }): {
57
+ [K in keyof T]: (args: Parameters<T[K]>[0]) => Promise<Awaited<ReturnType<T[K]>> | GuardErrorBody>;
58
+ };
59
+ /** Dollars spent outside a tool call (an LLM response, a paid API). Throws `CAP_EXCEEDED` when it does not fit. */
60
+ spend(usd: number, label: string, opts?: {
61
+ details?: unknown;
62
+ force?: boolean;
63
+ runId?: string;
64
+ reason?: string;
65
+ }): Promise<AuditEntry>;
66
+ halt(reason?: string): Promise<void>;
67
+ resume(): Promise<void>;
68
+ approve(id: string, by?: string, note?: string): Promise<ApprovalRecord | undefined>;
69
+ deny(id: string, by?: string, note?: string): Promise<ApprovalRecord | undefined>;
70
+ pendingApprovals(): Promise<ApprovalRecord[]>;
71
+ status(runId?: string): Promise<{
72
+ usage: Awaited<ReturnType<_agentwares_agentguard_core.CapsEngine["usage"]>>;
73
+ caps: Policy["caps"];
74
+ remaining: _agentwares_agentguard_core.CapCheck["remaining"];
75
+ killed: _agentwares_agentguard_core.KillState;
76
+ }>;
77
+ audit(): Promise<AuditEntry[]>;
78
+ report(runId?: string | null): Promise<Report>;
79
+ reportMarkdown(runId?: string | null): Promise<string>;
80
+ }
81
+ declare function createGuard(opts: CreateGuardOptions): Promise<AgentGuard>;
82
+
83
+ /**
84
+ * Hard spend limits for LLM calls: a `fetch` that recognizes OpenAI, Anthropic and Gemini
85
+ * requests, refuses them once the run/day budget is gone, and charges the actual token cost of
86
+ * every response (streamed or not) to the same counters the tool caps use.
87
+ */
88
+
89
+ type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
90
+ interface GuardedFetchOptions {
91
+ /** underlying fetch (default `globalThis.fetch`) */
92
+ fetch?: FetchLike;
93
+ /** run id override (default: the guard's current run) */
94
+ runId?: () => string;
95
+ /** hosts to treat as LLM providers beyond the built-ins (e.g. a proxy) → provider */
96
+ providers?: Record<string, Provider>;
97
+ /** called with every priced call */
98
+ onSpend?: (info: {
99
+ provider: Provider;
100
+ model: string;
101
+ usage: LlmUsage;
102
+ usd: number | undefined;
103
+ }) => void;
104
+ }
105
+ type Provider = "openai" | "anthropic" | "gemini";
106
+ declare function detectProvider(url: URL, extra?: Record<string, Provider>): Provider | undefined;
107
+ /** Normalize the usage object of any provider into input/output/cached tokens. */
108
+ declare function normalizeUsage(provider: Provider, raw: unknown): LlmUsage | undefined;
109
+ /** Extract usage from a non-streamed JSON body. */
110
+ declare function usageFromJson(provider: Provider, json: Record<string, unknown>): LlmUsage | undefined;
111
+ /** Extract usage from a complete SSE transcript (all `data:` payloads). */
112
+ declare function usageFromSse(provider: Provider, text: string): LlmUsage | undefined;
113
+ /**
114
+ * Wrap `fetch` for an OpenAI / Anthropic / Gemini SDK client (`new OpenAI({ fetch })`,
115
+ * `new Anthropic({ fetch })`). Non-provider URLs pass straight through.
116
+ */
117
+ declare function createGuardedFetch(ag: AgentGuard, opts?: GuardedFetchOptions): FetchLike;
118
+
119
+ /**
120
+ * OpenAI Agents SDK (`@openai/agents`) — wrap the object `tool({...})` returns. Duck-typed on
121
+ * `{ name, description?, parameters?, invoke(runContext, input) }` so this package has no
122
+ * dependency on the SDK. Blocked calls return the structured error as the tool's string result
123
+ * (the model reads `fix`), so the run keeps going instead of crashing.
124
+ */
125
+
126
+ interface OpenAIAgentsToolLike {
127
+ name: string;
128
+ description?: string;
129
+ parameters?: unknown;
130
+ invoke: (runContext: unknown, input: string, ...rest: unknown[]) => Promise<unknown> | unknown;
131
+ }
132
+ interface OpenAIAgentsWrapOptions {
133
+ annotations?: ToolAnnotationsLike;
134
+ outputSchema?: Record<string, unknown>;
135
+ /** `return` (default): blocked calls return the error JSON as the tool output; `throw`: throw `GuardError` */
136
+ onBlock?: "return" | "throw";
137
+ }
138
+ declare function wrapOpenAIAgentsTool<T extends OpenAIAgentsToolLike>(ag: AgentGuard, tool: T, opts?: OpenAIAgentsWrapOptions): T;
139
+ declare function wrapOpenAIAgentsTools<T extends OpenAIAgentsToolLike>(ag: AgentGuard, tools: T[], opts?: OpenAIAgentsWrapOptions & {
140
+ annotations?: Record<string, ToolAnnotationsLike>;
141
+ }): T[];
142
+
143
+ /**
144
+ * LangChain.js — wrap a `StructuredTool` / `DynamicStructuredTool` / anything with
145
+ * `{ name, description?, _call(args, ...) }` or `{ name, invoke(args, ...) }`. The wrapper keeps the
146
+ * tool's prototype (so `instanceof StructuredTool` still holds) and intercepts `_call`, which every
147
+ * public entry point (`invoke`, `call`, `run`) ends up in. Blocked calls return the error JSON.
148
+ */
149
+
150
+ interface LangChainToolLike {
151
+ name: string;
152
+ description?: string;
153
+ _call?: (args: unknown, ...rest: unknown[]) => Promise<unknown> | unknown;
154
+ invoke?: (args: unknown, ...rest: unknown[]) => Promise<unknown> | unknown;
155
+ }
156
+ interface LangChainWrapOptions {
157
+ annotations?: ToolAnnotationsLike;
158
+ outputSchema?: Record<string, unknown>;
159
+ onBlock?: "return" | "throw";
160
+ }
161
+ declare function wrapLangChainTool<T extends LangChainToolLike>(ag: AgentGuard, tool: T, opts?: LangChainWrapOptions): T;
162
+ declare function wrapLangChainTools<T extends LangChainToolLike>(ag: AgentGuard, tools: T[], opts?: LangChainWrapOptions & {
163
+ annotations?: Record<string, ToolAnnotationsLike>;
164
+ }): T[];
165
+
166
+ export { AgentGuard, type AnyFn, type CreateGuardOptions, type FetchLike, type GuardedFetchOptions, type LangChainToolLike, type LangChainWrapOptions, type OpenAIAgentsToolLike, type OpenAIAgentsWrapOptions, type Provider, type ToolMeta, type WrapOptions, createGuard, createGuardedFetch, detectProvider, newRunId, normalizeUsage, usageFromJson, usageFromSse, wrapLangChainTool, wrapLangChainTools, wrapOpenAIAgentsTool, wrapOpenAIAgentsTools };
package/dist/index.js ADDED
@@ -0,0 +1,465 @@
1
+ // src/guard.ts
2
+ import {
3
+ Guard,
4
+ GuardError,
5
+ buildReport,
6
+ parsePolicy,
7
+ renderReportMarkdown
8
+ } from "@agentwares/agentguard-core";
9
+ import {
10
+ FileApprovalStore,
11
+ FileAuditSink,
12
+ FileKillSwitch,
13
+ FileStateStore,
14
+ loadPolicyFile
15
+ } from "@agentwares/agentguard-core/node";
16
+ function newRunId(now = /* @__PURE__ */ new Date()) {
17
+ return `run_${now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}_${Math.random().toString(36).slice(2, 6)}`;
18
+ }
19
+ var AgentGuard = class {
20
+ guard;
21
+ policy;
22
+ loaded;
23
+ kill;
24
+ runId;
25
+ agent;
26
+ constructor(guard, opts) {
27
+ this.guard = guard;
28
+ this.policy = guard.policy;
29
+ this.runId = opts.runId;
30
+ this.agent = opts.agent;
31
+ this.loaded = opts.loaded;
32
+ this.kill = opts.kill;
33
+ }
34
+ /** Start a new run (new per-run caps and loop window). Returns the id. */
35
+ newRun(id = newRunId()) {
36
+ this.runId = id;
37
+ return id;
38
+ }
39
+ /** Run one guarded call. `execute` receives the (unchanged) arguments. */
40
+ run(meta, args, execute) {
41
+ return this.guard.run(
42
+ {
43
+ tool: {
44
+ name: meta.name,
45
+ description: meta.description,
46
+ annotations: meta.annotations,
47
+ outputSchema: meta.outputSchema
48
+ },
49
+ args,
50
+ runId: meta.runId ?? this.runId,
51
+ agent: this.agent
52
+ },
53
+ execute
54
+ );
55
+ }
56
+ /** Wrap a plain function `(args) => result`. Blocked calls throw `GuardError` (or return the body). */
57
+ wrap(fn, opts) {
58
+ return async (args) => {
59
+ const result = await this.run(opts, args, (a) => fn(a));
60
+ if (result.error && !result.ok) {
61
+ if (opts.onBlock === "return") return result.error;
62
+ throw new GuardError({
63
+ code: result.error.code,
64
+ cause: result.error.cause,
65
+ fix: result.error.fix,
66
+ retryable: result.error.retryable,
67
+ details: result.error.details
68
+ });
69
+ }
70
+ return result.value;
71
+ };
72
+ }
73
+ /** Wrap a map of named functions at once (`{ crm_get_contact: fn, ... }`). */
74
+ wrapAll(fns, opts = {}) {
75
+ const out = {};
76
+ for (const [name, fn] of Object.entries(fns)) {
77
+ out[name] = this.wrap(fn, {
78
+ name,
79
+ annotations: opts.annotations?.[name],
80
+ onBlock: opts.onBlock
81
+ });
82
+ }
83
+ return out;
84
+ }
85
+ /** Dollars spent outside a tool call (an LLM response, a paid API). Throws `CAP_EXCEEDED` when it does not fit. */
86
+ spend(usd, label, opts = {}) {
87
+ return this.guard.spend(opts.runId ?? this.runId, usd, {
88
+ label,
89
+ agent: this.agent,
90
+ details: opts.details,
91
+ force: opts.force,
92
+ reason: opts.reason
93
+ });
94
+ }
95
+ async halt(reason = "killed via SDK") {
96
+ await this.guard.halt(reason);
97
+ this.kill?.kill(reason);
98
+ }
99
+ async resume() {
100
+ await this.guard.resume();
101
+ this.kill?.resume();
102
+ }
103
+ approve(id, by = "sdk", note) {
104
+ return this.guard.decide(id, "approved", by, note);
105
+ }
106
+ deny(id, by = "sdk", note) {
107
+ return this.guard.decide(id, "denied", by, note);
108
+ }
109
+ pendingApprovals() {
110
+ return this.guard.approvals.list("pending");
111
+ }
112
+ status(runId = this.runId) {
113
+ return this.guard.status(runId, this.agent);
114
+ }
115
+ audit() {
116
+ return this.guard.audit.read();
117
+ }
118
+ async report(runId = this.runId) {
119
+ return buildReport(await this.audit(), { runId: runId ?? void 0 });
120
+ }
121
+ async reportMarkdown(runId = this.runId) {
122
+ return renderReportMarkdown(await this.report(runId));
123
+ }
124
+ };
125
+ async function createGuard(opts) {
126
+ const env = opts.env ?? process.env;
127
+ let policy;
128
+ let loaded;
129
+ if (typeof opts.policy === "string") {
130
+ loaded = loadPolicyFile(opts.policy, { env });
131
+ policy = loaded.policy;
132
+ } else {
133
+ policy = "version" in opts.policy && typeof opts.policy.kill === "object" && opts.policy.audit ? opts.policy : parsePolicy(opts.policy, { env });
134
+ }
135
+ const agent = opts.agent ? policy.agents.find((a) => a.name === opts.agent) : void 0;
136
+ if (opts.agent && !agent)
137
+ throw new GuardError({
138
+ code: "INVALID_POLICY",
139
+ cause: `no agent "${opts.agent}" in the policy`,
140
+ fix: `add it under agents: or use one of ${policy.agents.map((a) => a.name).join(", ") || "(none)"}`
141
+ });
142
+ const useFiles = loaded !== void 0 && !opts.memory;
143
+ const kill = useFiles ? new FileKillSwitch(loaded.killPath) : void 0;
144
+ const guard = new Guard({
145
+ policy,
146
+ env,
147
+ onEvent: opts.onEvent,
148
+ ...useFiles ? {
149
+ state: new FileStateStore(loaded.stateDir),
150
+ audit: new FileAuditSink(loaded.auditPath),
151
+ approvals: new FileApprovalStore(loaded.stateDir),
152
+ kill
153
+ } : {}
154
+ });
155
+ return new AgentGuard(guard, { runId: opts.runId ?? newRunId(), agent, loaded, kill });
156
+ }
157
+
158
+ // src/fetch.ts
159
+ import { estimateLlmCost } from "@agentwares/agentguard-core";
160
+ var HOSTS = {
161
+ "api.openai.com": "openai",
162
+ "api.anthropic.com": "anthropic",
163
+ "generativelanguage.googleapis.com": "gemini"
164
+ };
165
+ function detectProvider(url, extra = {}) {
166
+ return extra[url.host] ?? HOSTS[url.host];
167
+ }
168
+ function normalizeUsage(provider, raw) {
169
+ if (!raw || typeof raw !== "object") return void 0;
170
+ const u = raw;
171
+ const n = (v) => typeof v === "number" ? v : 0;
172
+ if (provider === "openai") {
173
+ const details = u.prompt_tokens_details ?? u.input_tokens_details;
174
+ if (u.prompt_tokens !== void 0 || u.input_tokens !== void 0) {
175
+ return {
176
+ input_tokens: n(u.prompt_tokens ?? u.input_tokens),
177
+ output_tokens: n(u.completion_tokens ?? u.output_tokens),
178
+ cached_input_tokens: n(details?.cached_tokens)
179
+ };
180
+ }
181
+ return void 0;
182
+ }
183
+ if (provider === "anthropic") {
184
+ if (u.input_tokens === void 0 && u.output_tokens === void 0) return void 0;
185
+ return {
186
+ input_tokens: n(u.input_tokens) + n(u.cache_read_input_tokens) + n(u.cache_creation_input_tokens),
187
+ output_tokens: n(u.output_tokens),
188
+ cached_input_tokens: n(u.cache_read_input_tokens)
189
+ };
190
+ }
191
+ if (u.promptTokenCount === void 0 && u.candidatesTokenCount === void 0) return void 0;
192
+ return {
193
+ input_tokens: n(u.promptTokenCount),
194
+ output_tokens: n(u.candidatesTokenCount) + n(u.thoughtsTokenCount),
195
+ cached_input_tokens: n(u.cachedContentTokenCount)
196
+ };
197
+ }
198
+ function modelFrom(provider, url, body, responseJson) {
199
+ if (typeof responseJson?.model === "string") return responseJson.model;
200
+ if (typeof body?.model === "string") return body.model;
201
+ if (provider === "gemini") {
202
+ const m = /\/models\/([^:/]+)/.exec(url.pathname);
203
+ if (m) return m[1];
204
+ }
205
+ return "unknown";
206
+ }
207
+ function usageFromJson(provider, json) {
208
+ return normalizeUsage(
209
+ provider,
210
+ json.usage ?? json.response?.usage ?? json.usageMetadata
211
+ );
212
+ }
213
+ function usageFromSse(provider, text) {
214
+ let usage;
215
+ let anthropicInput;
216
+ for (const line of text.split("\n")) {
217
+ if (!line.startsWith("data:")) continue;
218
+ const payload = line.slice(5).trim();
219
+ if (!payload || payload === "[DONE]") continue;
220
+ let json;
221
+ try {
222
+ json = JSON.parse(payload);
223
+ } catch {
224
+ continue;
225
+ }
226
+ if (provider === "anthropic") {
227
+ if (json.type === "message_start")
228
+ anthropicInput = normalizeUsage(
229
+ provider,
230
+ json.message?.usage
231
+ );
232
+ if (json.type === "message_delta") {
233
+ const delta = normalizeUsage(provider, json.usage);
234
+ if (delta)
235
+ usage = {
236
+ ...anthropicInput,
237
+ ...delta,
238
+ input_tokens: (delta.input_tokens || anthropicInput?.input_tokens) ?? 0
239
+ };
240
+ }
241
+ continue;
242
+ }
243
+ const found = usageFromJson(provider, json);
244
+ if (found) usage = found;
245
+ }
246
+ return usage;
247
+ }
248
+ function blockedResponse(status, body) {
249
+ return new Response(
250
+ JSON.stringify({
251
+ error: { type: body.code, message: `${body.code}: ${body.cause} \u2014 ${body.fix}` },
252
+ ...body
253
+ }),
254
+ { status, headers: { "Content-Type": "application/json", "X-Agentguard": String(body.code) } }
255
+ );
256
+ }
257
+ function createGuardedFetch(ag, opts = {}) {
258
+ const base = opts.fetch ?? globalThis.fetch.bind(globalThis);
259
+ const runId = opts.runId ?? (() => ag.runId);
260
+ return async (input, init) => {
261
+ const request = input instanceof Request ? input : void 0;
262
+ const url = new URL(request ? request.url : input instanceof URL ? input.href : String(input));
263
+ const provider = detectProvider(url, opts.providers);
264
+ if (!provider) return base(input, init);
265
+ let bodyText;
266
+ if (typeof init?.body === "string") bodyText = init.body;
267
+ else if (request && request.method !== "GET") bodyText = await request.clone().text();
268
+ let body;
269
+ try {
270
+ body = bodyText ? JSON.parse(bodyText) : void 0;
271
+ } catch {
272
+ body = void 0;
273
+ }
274
+ const model = modelFrom(provider, url, body);
275
+ const killed = await ag.guard.killState();
276
+ if (killed.killed) {
277
+ return blockedResponse(403, {
278
+ code: "KILLED",
279
+ cause: `agentguard kill switch is on (${killed.reason ?? ""})`,
280
+ fix: "a human must run `agentguard resume`",
281
+ retryable: false
282
+ });
283
+ }
284
+ const check = await ag.guard.canSpend(runId(), 0, ag.agent);
285
+ const remaining = Math.min(
286
+ check.remaining.spend_usd?.per_run ?? Infinity,
287
+ check.remaining.spend_usd?.per_day ?? Infinity
288
+ );
289
+ if (remaining <= 0) {
290
+ const scope = (check.remaining.spend_usd?.per_run ?? Infinity) <= 0 ? "this run" : "today";
291
+ return blockedResponse(402, {
292
+ code: "CAP_EXCEEDED",
293
+ cause: `spend_usd cap for ${scope} is used up`,
294
+ fix: "stop and report to the user; a human can raise caps in agentguard.yaml",
295
+ retryable: false,
296
+ details: { remaining: check.remaining, runId: runId() }
297
+ });
298
+ }
299
+ let forwardedInit = init;
300
+ if (provider === "openai" && body?.stream === true && url.pathname.endsWith("/chat/completions") && !body.stream_options) {
301
+ body.stream_options = { include_usage: true };
302
+ forwardedInit = { ...init, body: JSON.stringify(body) };
303
+ }
304
+ const response = await base(
305
+ request && forwardedInit === init ? request : url,
306
+ request && forwardedInit === init ? void 0 : {
307
+ ...forwardedInit,
308
+ method: forwardedInit?.method ?? request?.method,
309
+ headers: forwardedInit?.headers ?? request?.headers
310
+ }
311
+ );
312
+ if (!response.ok || !response.body) return response;
313
+ const charge = async (usage, resolvedModel) => {
314
+ if (!usage) return;
315
+ const usd = estimateLlmCost(ag.policy, resolvedModel, usage);
316
+ opts.onSpend?.({ provider, model: resolvedModel, usage, usd });
317
+ await ag.spend(usd ?? 0, `llm:${resolvedModel}`, {
318
+ force: true,
319
+ runId: runId(),
320
+ details: { provider, model: resolvedModel, usage },
321
+ reason: usd === void 0 ? `no list price for "${resolvedModel}" \u2014 not counted against spend_usd; add it under spend.models in agentguard.yaml` : void 0
322
+ });
323
+ };
324
+ const contentType = response.headers.get("content-type") ?? "";
325
+ if (contentType.includes("text/event-stream")) {
326
+ let transcript = "";
327
+ const decoder = new TextDecoder();
328
+ const tapped = response.body.pipeThrough(
329
+ new TransformStream({
330
+ transform(chunk, controller) {
331
+ transcript += decoder.decode(chunk, { stream: true });
332
+ controller.enqueue(chunk);
333
+ },
334
+ async flush() {
335
+ transcript += decoder.decode();
336
+ await charge(usageFromSse(provider, transcript), model);
337
+ }
338
+ })
339
+ );
340
+ return new Response(tapped, {
341
+ status: response.status,
342
+ statusText: response.statusText,
343
+ headers: response.headers
344
+ });
345
+ }
346
+ const text = await response.text();
347
+ try {
348
+ const json = JSON.parse(text);
349
+ await charge(usageFromJson(provider, json), modelFrom(provider, url, body, json));
350
+ } catch {
351
+ }
352
+ return new Response(text, {
353
+ status: response.status,
354
+ statusText: response.statusText,
355
+ headers: response.headers
356
+ });
357
+ };
358
+ }
359
+
360
+ // src/adapters/openai-agents.ts
361
+ function parseInput(input) {
362
+ if (typeof input !== "string") return input;
363
+ try {
364
+ return JSON.parse(input);
365
+ } catch {
366
+ return { input };
367
+ }
368
+ }
369
+ function wrapOpenAIAgentsTool(ag, tool, opts = {}) {
370
+ const meta = {
371
+ name: tool.name,
372
+ description: tool.description,
373
+ annotations: opts.annotations,
374
+ outputSchema: opts.outputSchema
375
+ };
376
+ const original = tool.invoke.bind(tool);
377
+ const invoke = async (runContext, input, ...rest) => {
378
+ const result = await ag.run(
379
+ meta,
380
+ parseInput(input),
381
+ () => original(runContext, input, ...rest)
382
+ );
383
+ if (result.error && !result.ok) {
384
+ if (opts.onBlock === "throw")
385
+ throw Object.assign(new Error(`${result.error.code}: ${result.error.cause}`), result.error);
386
+ return JSON.stringify(result.error);
387
+ }
388
+ if (result.faked)
389
+ return typeof result.value === "string" ? result.value : JSON.stringify(result.value);
390
+ return result.value;
391
+ };
392
+ return { ...tool, invoke };
393
+ }
394
+ function wrapOpenAIAgentsTools(ag, tools, opts = {}) {
395
+ return tools.map(
396
+ (t) => wrapOpenAIAgentsTool(ag, t, { ...opts, annotations: opts.annotations?.[t.name] })
397
+ );
398
+ }
399
+
400
+ // src/adapters/langchain.ts
401
+ function stringify(value) {
402
+ return typeof value === "string" ? value : JSON.stringify(value);
403
+ }
404
+ function wrapLangChainTool(ag, tool, opts = {}) {
405
+ const meta = {
406
+ name: tool.name,
407
+ description: tool.description,
408
+ annotations: opts.annotations,
409
+ outputSchema: opts.outputSchema
410
+ };
411
+ const guardCall = async (original, args, rest) => {
412
+ const result = await ag.run(meta, args, () => original(args, ...rest));
413
+ if (result.error && !result.ok) {
414
+ if (opts.onBlock === "throw")
415
+ throw Object.assign(new Error(`${result.error.code}: ${result.error.cause}`), result.error);
416
+ return JSON.stringify(result.error);
417
+ }
418
+ return result.faked ? stringify(result.value) : result.value;
419
+ };
420
+ const wrapped = Object.create(tool);
421
+ if (typeof tool._call === "function") {
422
+ const original = tool._call.bind(tool);
423
+ Object.defineProperty(wrapped, "_call", {
424
+ value: (args, ...rest) => guardCall(original, args, rest),
425
+ writable: true,
426
+ configurable: true
427
+ });
428
+ } else if (typeof tool.invoke === "function") {
429
+ const original = tool.invoke.bind(tool);
430
+ Object.defineProperty(wrapped, "invoke", {
431
+ value: (args, ...rest) => guardCall(original, args, rest),
432
+ writable: true,
433
+ configurable: true
434
+ });
435
+ } else {
436
+ throw new Error(`cannot wrap "${tool.name}": no _call or invoke`);
437
+ }
438
+ return wrapped;
439
+ }
440
+ function wrapLangChainTools(ag, tools, opts = {}) {
441
+ return tools.map(
442
+ (t) => wrapLangChainTool(ag, t, { ...opts, annotations: opts.annotations?.[t.name] })
443
+ );
444
+ }
445
+
446
+ // src/index.ts
447
+ import {
448
+ GuardError as GuardError2
449
+ } from "@agentwares/agentguard-core";
450
+ export {
451
+ AgentGuard,
452
+ GuardError2 as GuardError,
453
+ createGuard,
454
+ createGuardedFetch,
455
+ detectProvider,
456
+ newRunId,
457
+ normalizeUsage,
458
+ usageFromJson,
459
+ usageFromSse,
460
+ wrapLangChainTool,
461
+ wrapLangChainTools,
462
+ wrapOpenAIAgentsTool,
463
+ wrapOpenAIAgentsTools
464
+ };
465
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/guard.ts","../src/fetch.ts","../src/adapters/openai-agents.ts","../src/adapters/langchain.ts","../src/index.ts"],"sourcesContent":["/**\n * `createGuard` — the policy engine for tool calls that bypass MCP. Same YAML, same caps, same\n * audit log as the proxy; file-backed when given a policy path, in-memory when given an object.\n */\nimport {\n Guard,\n GuardError,\n buildReport,\n parsePolicy,\n renderReportMarkdown,\n type AgentScope,\n type ApprovalRecord,\n type AuditEntry,\n type GuardErrorBody,\n type GuardEvent,\n type GuardResult,\n type Policy,\n type PolicyInput,\n type Report,\n type ToolAnnotationsLike,\n} from \"@agentwares/agentguard-core\";\nimport {\n FileApprovalStore,\n FileAuditSink,\n FileKillSwitch,\n FileStateStore,\n loadPolicyFile,\n type LoadedPolicy,\n} from \"@agentwares/agentguard-core/node\";\n\nexport interface CreateGuardOptions {\n /** path to `agentguard.yaml` (file-backed state, shared with the CLI) or an inline policy (in-memory) */\n policy: string | PolicyInput | Policy;\n /** run id for calls that do not pass one (default: generated) */\n runId?: string;\n /** agent scope by name (from `agents:` in the policy) */\n agent?: string;\n onEvent?: (event: GuardEvent) => void | Promise<void>;\n /** in-memory even when `policy` is a path */\n memory?: boolean;\n env?: Record<string, string | undefined>;\n}\n\nexport interface ToolMeta {\n name: string;\n description?: string;\n annotations?: ToolAnnotationsLike;\n /** JSON Schema of the result — used to synthesize dry-run results */\n outputSchema?: Record<string, unknown>;\n /** override the guard's run id for this tool */\n runId?: string;\n}\n\nexport interface WrapOptions extends ToolMeta {\n /** `throw` (default): blocked calls throw `GuardError`; `return`: they return the error body */\n onBlock?: \"throw\" | \"return\";\n}\n\nexport type AnyFn = (...args: never[]) => unknown;\n\nexport function newRunId(now: Date = new Date()): string {\n return `run_${now\n .toISOString()\n .replace(/[-:.TZ]/g, \"\")\n .slice(0, 14)}_${Math.random().toString(36).slice(2, 6)}`;\n}\n\nexport class AgentGuard {\n readonly guard: Guard;\n readonly policy: Policy;\n readonly loaded?: LoadedPolicy;\n readonly kill?: FileKillSwitch;\n runId: string;\n readonly agent: AgentScope | undefined;\n\n constructor(\n guard: Guard,\n opts: { runId: string; agent?: AgentScope; loaded?: LoadedPolicy; kill?: FileKillSwitch },\n ) {\n this.guard = guard;\n this.policy = guard.policy;\n this.runId = opts.runId;\n this.agent = opts.agent;\n this.loaded = opts.loaded;\n this.kill = opts.kill;\n }\n\n /** Start a new run (new per-run caps and loop window). Returns the id. */\n newRun(id: string = newRunId()): string {\n this.runId = id;\n return id;\n }\n\n /** Run one guarded call. `execute` receives the (unchanged) arguments. */\n run(meta: ToolMeta, args: unknown, execute: (args: unknown) => unknown): Promise<GuardResult> {\n return this.guard.run(\n {\n tool: {\n name: meta.name,\n description: meta.description,\n annotations: meta.annotations,\n outputSchema: meta.outputSchema,\n },\n args,\n runId: meta.runId ?? this.runId,\n agent: this.agent,\n },\n execute,\n );\n }\n\n /** Wrap a plain function `(args) => result`. Blocked calls throw `GuardError` (or return the body). */\n wrap<A, R>(\n fn: (args: A) => R | Promise<R>,\n opts: WrapOptions,\n ): (args: A) => Promise<R | GuardErrorBody> {\n return async (args: A) => {\n const result = await this.run(opts, args, (a) => fn(a as A));\n if (result.error && !result.ok) {\n if (opts.onBlock === \"return\") return result.error;\n throw new GuardError({\n code: result.error.code,\n cause: result.error.cause,\n fix: result.error.fix,\n retryable: result.error.retryable,\n details: result.error.details,\n });\n }\n return result.value as R;\n };\n }\n\n /** Wrap a map of named functions at once (`{ crm_get_contact: fn, ... }`). */\n wrapAll<T extends Record<string, AnyFn>>(\n fns: T,\n opts: { annotations?: Record<string, ToolAnnotationsLike>; onBlock?: \"throw\" | \"return\" } = {},\n ): {\n [K in keyof T]: (\n args: Parameters<T[K]>[0],\n ) => Promise<Awaited<ReturnType<T[K]>> | GuardErrorBody>;\n } {\n const out: Record<string, unknown> = {};\n for (const [name, fn] of Object.entries(fns)) {\n out[name] = this.wrap(fn as (args: unknown) => unknown, {\n name,\n annotations: opts.annotations?.[name],\n onBlock: opts.onBlock,\n });\n }\n return out as {\n [K in keyof T]: (\n args: Parameters<T[K]>[0],\n ) => Promise<Awaited<ReturnType<T[K]>> | GuardErrorBody>;\n };\n }\n\n /** Dollars spent outside a tool call (an LLM response, a paid API). Throws `CAP_EXCEEDED` when it does not fit. */\n spend(\n usd: number,\n label: string,\n opts: { details?: unknown; force?: boolean; runId?: string; reason?: string } = {},\n ): Promise<AuditEntry> {\n return this.guard.spend(opts.runId ?? this.runId, usd, {\n label,\n agent: this.agent,\n details: opts.details,\n force: opts.force,\n reason: opts.reason,\n });\n }\n\n async halt(reason = \"killed via SDK\"): Promise<void> {\n await this.guard.halt(reason);\n this.kill?.kill(reason);\n }\n async resume(): Promise<void> {\n await this.guard.resume();\n this.kill?.resume();\n }\n approve(id: string, by = \"sdk\", note?: string): Promise<ApprovalRecord | undefined> {\n return this.guard.decide(id, \"approved\", by, note);\n }\n deny(id: string, by = \"sdk\", note?: string): Promise<ApprovalRecord | undefined> {\n return this.guard.decide(id, \"denied\", by, note);\n }\n pendingApprovals(): Promise<ApprovalRecord[]> {\n return this.guard.approvals.list(\"pending\");\n }\n status(runId: string = this.runId) {\n return this.guard.status(runId, this.agent);\n }\n audit(): Promise<AuditEntry[]> {\n return this.guard.audit.read();\n }\n async report(runId: string | null = this.runId): Promise<Report> {\n return buildReport(await this.audit(), { runId: runId ?? undefined });\n }\n async reportMarkdown(runId: string | null = this.runId): Promise<string> {\n return renderReportMarkdown(await this.report(runId));\n }\n}\n\nexport async function createGuard(opts: CreateGuardOptions): Promise<AgentGuard> {\n const env = opts.env ?? process.env;\n let policy: Policy;\n let loaded: LoadedPolicy | undefined;\n if (typeof opts.policy === \"string\") {\n loaded = loadPolicyFile(opts.policy, { env });\n policy = loaded.policy;\n } else {\n policy =\n \"version\" in opts.policy &&\n typeof (opts.policy as Policy).kill === \"object\" &&\n (opts.policy as Policy).audit\n ? (opts.policy as Policy)\n : parsePolicy(opts.policy, { env });\n }\n const agent = opts.agent ? policy.agents.find((a) => a.name === opts.agent) : undefined;\n if (opts.agent && !agent)\n throw new GuardError({\n code: \"INVALID_POLICY\",\n cause: `no agent \"${opts.agent}\" in the policy`,\n fix: `add it under agents: or use one of ${policy.agents.map((a) => a.name).join(\", \") || \"(none)\"}`,\n });\n const useFiles = loaded !== undefined && !opts.memory;\n const kill = useFiles ? new FileKillSwitch(loaded!.killPath) : undefined;\n const guard = new Guard({\n policy,\n env,\n onEvent: opts.onEvent,\n ...(useFiles\n ? {\n state: new FileStateStore(loaded!.stateDir),\n audit: new FileAuditSink(loaded!.auditPath),\n approvals: new FileApprovalStore(loaded!.stateDir),\n kill,\n }\n : {}),\n });\n return new AgentGuard(guard, { runId: opts.runId ?? newRunId(), agent, loaded, kill });\n}\n","/**\n * Hard spend limits for LLM calls: a `fetch` that recognizes OpenAI, Anthropic and Gemini\n * requests, refuses them once the run/day budget is gone, and charges the actual token cost of\n * every response (streamed or not) to the same counters the tool caps use.\n */\nimport { estimateLlmCost, type LlmUsage } from \"@agentwares/agentguard-core\";\nimport type { AgentGuard } from \"./guard.js\";\n\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nexport interface GuardedFetchOptions {\n /** underlying fetch (default `globalThis.fetch`) */\n fetch?: FetchLike;\n /** run id override (default: the guard's current run) */\n runId?: () => string;\n /** hosts to treat as LLM providers beyond the built-ins (e.g. a proxy) → provider */\n providers?: Record<string, Provider>;\n /** called with every priced call */\n onSpend?: (info: {\n provider: Provider;\n model: string;\n usage: LlmUsage;\n usd: number | undefined;\n }) => void;\n}\n\nexport type Provider = \"openai\" | \"anthropic\" | \"gemini\";\n\nconst HOSTS: Record<string, Provider> = {\n \"api.openai.com\": \"openai\",\n \"api.anthropic.com\": \"anthropic\",\n \"generativelanguage.googleapis.com\": \"gemini\",\n};\n\nexport function detectProvider(\n url: URL,\n extra: Record<string, Provider> = {},\n): Provider | undefined {\n return extra[url.host] ?? HOSTS[url.host];\n}\n\n/** Normalize the usage object of any provider into input/output/cached tokens. */\nexport function normalizeUsage(provider: Provider, raw: unknown): LlmUsage | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const u = raw as Record<string, unknown>;\n const n = (v: unknown): number => (typeof v === \"number\" ? v : 0);\n if (provider === \"openai\") {\n const details = (u.prompt_tokens_details ?? u.input_tokens_details) as\n Record<string, unknown> | undefined;\n if (u.prompt_tokens !== undefined || u.input_tokens !== undefined) {\n return {\n input_tokens: n(u.prompt_tokens ?? u.input_tokens),\n output_tokens: n(u.completion_tokens ?? u.output_tokens),\n cached_input_tokens: n(details?.cached_tokens),\n };\n }\n return undefined;\n }\n if (provider === \"anthropic\") {\n if (u.input_tokens === undefined && u.output_tokens === undefined) return undefined;\n return {\n input_tokens:\n n(u.input_tokens) + n(u.cache_read_input_tokens) + n(u.cache_creation_input_tokens),\n output_tokens: n(u.output_tokens),\n cached_input_tokens: n(u.cache_read_input_tokens),\n };\n }\n if (u.promptTokenCount === undefined && u.candidatesTokenCount === undefined) return undefined;\n return {\n input_tokens: n(u.promptTokenCount),\n output_tokens: n(u.candidatesTokenCount) + n(u.thoughtsTokenCount),\n cached_input_tokens: n(u.cachedContentTokenCount),\n };\n}\n\nfunction modelFrom(\n provider: Provider,\n url: URL,\n body: Record<string, unknown> | undefined,\n responseJson?: Record<string, unknown>,\n): string {\n if (typeof responseJson?.model === \"string\") return responseJson.model;\n if (typeof body?.model === \"string\") return body.model;\n if (provider === \"gemini\") {\n const m = /\\/models\\/([^:/]+)/.exec(url.pathname);\n if (m) return m[1]!;\n }\n return \"unknown\";\n}\n\n/** Extract usage from a non-streamed JSON body. */\nexport function usageFromJson(\n provider: Provider,\n json: Record<string, unknown>,\n): LlmUsage | undefined {\n return normalizeUsage(\n provider,\n json.usage ??\n (json.response as Record<string, unknown> | undefined)?.usage ??\n json.usageMetadata,\n );\n}\n\n/** Extract usage from a complete SSE transcript (all `data:` payloads). */\nexport function usageFromSse(provider: Provider, text: string): LlmUsage | undefined {\n let usage: LlmUsage | undefined;\n let anthropicInput: LlmUsage | undefined;\n for (const line of text.split(\"\\n\")) {\n if (!line.startsWith(\"data:\")) continue;\n const payload = line.slice(5).trim();\n if (!payload || payload === \"[DONE]\") continue;\n let json: Record<string, unknown>;\n try {\n json = JSON.parse(payload) as Record<string, unknown>;\n } catch {\n continue;\n }\n if (provider === \"anthropic\") {\n if (json.type === \"message_start\")\n anthropicInput = normalizeUsage(\n provider,\n (json.message as Record<string, unknown> | undefined)?.usage,\n );\n if (json.type === \"message_delta\") {\n const delta = normalizeUsage(provider, json.usage);\n if (delta)\n usage = {\n ...anthropicInput,\n ...delta,\n input_tokens: (delta.input_tokens || anthropicInput?.input_tokens) ?? 0,\n };\n }\n continue;\n }\n const found = usageFromJson(provider, json);\n if (found) usage = found;\n }\n return usage;\n}\n\nfunction blockedResponse(status: number, body: Record<string, unknown>): Response {\n return new Response(\n JSON.stringify({\n error: { type: body.code, message: `${body.code}: ${body.cause} — ${body.fix}` },\n ...body,\n }),\n { status, headers: { \"Content-Type\": \"application/json\", \"X-Agentguard\": String(body.code) } },\n );\n}\n\n/**\n * Wrap `fetch` for an OpenAI / Anthropic / Gemini SDK client (`new OpenAI({ fetch })`,\n * `new Anthropic({ fetch })`). Non-provider URLs pass straight through.\n */\nexport function createGuardedFetch(ag: AgentGuard, opts: GuardedFetchOptions = {}): FetchLike {\n const base = opts.fetch ?? globalThis.fetch.bind(globalThis);\n const runId = opts.runId ?? (() => ag.runId);\n return async (input, init) => {\n const request = input instanceof Request ? input : undefined;\n const url = new URL(request ? request.url : input instanceof URL ? input.href : String(input));\n const provider = detectProvider(url, opts.providers);\n if (!provider) return base(input, init);\n\n let bodyText: string | undefined;\n if (typeof init?.body === \"string\") bodyText = init.body;\n else if (request && request.method !== \"GET\") bodyText = await request.clone().text();\n let body: Record<string, unknown> | undefined;\n try {\n body = bodyText ? (JSON.parse(bodyText) as Record<string, unknown>) : undefined;\n } catch {\n body = undefined;\n }\n const model = modelFrom(provider, url, body);\n\n const killed = await ag.guard.killState();\n if (killed.killed) {\n return blockedResponse(403, {\n code: \"KILLED\",\n cause: `agentguard kill switch is on (${killed.reason ?? \"\"})`,\n fix: \"a human must run `agentguard resume`\",\n retryable: false,\n });\n }\n const check = await ag.guard.canSpend(runId(), 0, ag.agent);\n const remaining = Math.min(\n check.remaining.spend_usd?.per_run ?? Infinity,\n check.remaining.spend_usd?.per_day ?? Infinity,\n );\n if (remaining <= 0) {\n const scope = (check.remaining.spend_usd?.per_run ?? Infinity) <= 0 ? \"this run\" : \"today\";\n return blockedResponse(402, {\n code: \"CAP_EXCEEDED\",\n cause: `spend_usd cap for ${scope} is used up`,\n fix: \"stop and report to the user; a human can raise caps in agentguard.yaml\",\n retryable: false,\n details: { remaining: check.remaining, runId: runId() },\n });\n }\n\n // OpenAI chat streams only report usage when asked to.\n let forwardedInit = init;\n if (\n provider === \"openai\" &&\n body?.stream === true &&\n url.pathname.endsWith(\"/chat/completions\") &&\n !body.stream_options\n ) {\n body.stream_options = { include_usage: true };\n forwardedInit = { ...init, body: JSON.stringify(body) };\n }\n const response = await base(\n request && forwardedInit === init ? request : url,\n request && forwardedInit === init\n ? undefined\n : {\n ...forwardedInit,\n method: forwardedInit?.method ?? request?.method,\n headers: forwardedInit?.headers ?? request?.headers,\n },\n );\n if (!response.ok || !response.body) return response;\n\n const charge = async (usage: LlmUsage | undefined, resolvedModel: string): Promise<void> => {\n if (!usage) return;\n const usd = estimateLlmCost(ag.policy, resolvedModel, usage);\n opts.onSpend?.({ provider, model: resolvedModel, usage, usd });\n // No list price for this model: record the call at $0 rather than dropping it, so the run\n // shows a model the budget is not covering instead of silently under-counting.\n await ag.spend(usd ?? 0, `llm:${resolvedModel}`, {\n force: true,\n runId: runId(),\n details: { provider, model: resolvedModel, usage },\n reason:\n usd === undefined\n ? `no list price for \"${resolvedModel}\" — not counted against spend_usd; add it under spend.models in agentguard.yaml`\n : undefined,\n });\n };\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"text/event-stream\")) {\n let transcript = \"\";\n const decoder = new TextDecoder();\n const tapped = response.body.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n transcript += decoder.decode(chunk, { stream: true });\n controller.enqueue(chunk);\n },\n async flush() {\n transcript += decoder.decode();\n await charge(usageFromSse(provider, transcript), model);\n },\n }),\n );\n return new Response(tapped, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n }\n const text = await response.text();\n try {\n const json = JSON.parse(text) as Record<string, unknown>;\n await charge(usageFromJson(provider, json), modelFrom(provider, url, body, json));\n } catch {\n // not JSON\n }\n return new Response(text, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n };\n}\n","/**\n * OpenAI Agents SDK (`@openai/agents`) — wrap the object `tool({...})` returns. Duck-typed on\n * `{ name, description?, parameters?, invoke(runContext, input) }` so this package has no\n * dependency on the SDK. Blocked calls return the structured error as the tool's string result\n * (the model reads `fix`), so the run keeps going instead of crashing.\n */\nimport type { AgentGuard, ToolMeta } from \"../guard.js\";\nimport type { ToolAnnotationsLike } from \"@agentwares/agentguard-core\";\n\nexport interface OpenAIAgentsToolLike {\n name: string;\n description?: string;\n parameters?: unknown;\n invoke: (runContext: unknown, input: string, ...rest: unknown[]) => Promise<unknown> | unknown;\n}\n\nexport interface OpenAIAgentsWrapOptions {\n annotations?: ToolAnnotationsLike;\n outputSchema?: Record<string, unknown>;\n /** `return` (default): blocked calls return the error JSON as the tool output; `throw`: throw `GuardError` */\n onBlock?: \"return\" | \"throw\";\n}\n\nfunction parseInput(input: unknown): unknown {\n if (typeof input !== \"string\") return input;\n try {\n return JSON.parse(input);\n } catch {\n return { input };\n }\n}\n\nexport function wrapOpenAIAgentsTool<T extends OpenAIAgentsToolLike>(\n ag: AgentGuard,\n tool: T,\n opts: OpenAIAgentsWrapOptions = {},\n): T {\n const meta: ToolMeta = {\n name: tool.name,\n description: tool.description,\n annotations: opts.annotations,\n outputSchema: opts.outputSchema,\n };\n const original = tool.invoke.bind(tool);\n const invoke = async (\n runContext: unknown,\n input: string,\n ...rest: unknown[]\n ): Promise<unknown> => {\n const result = await ag.run(meta, parseInput(input), () =>\n original(runContext, input, ...rest),\n );\n if (result.error && !result.ok) {\n if (opts.onBlock === \"throw\")\n throw Object.assign(new Error(`${result.error.code}: ${result.error.cause}`), result.error);\n return JSON.stringify(result.error);\n }\n if (result.faked)\n return typeof result.value === \"string\" ? result.value : JSON.stringify(result.value);\n return result.value;\n };\n return { ...tool, invoke } as T;\n}\n\nexport function wrapOpenAIAgentsTools<T extends OpenAIAgentsToolLike>(\n ag: AgentGuard,\n tools: T[],\n opts: OpenAIAgentsWrapOptions & { annotations?: Record<string, ToolAnnotationsLike> } = {},\n): T[] {\n return tools.map((t) =>\n wrapOpenAIAgentsTool(ag, t, { ...opts, annotations: opts.annotations?.[t.name] }),\n );\n}\n","/**\n * LangChain.js — wrap a `StructuredTool` / `DynamicStructuredTool` / anything with\n * `{ name, description?, _call(args, ...) }` or `{ name, invoke(args, ...) }`. The wrapper keeps the\n * tool's prototype (so `instanceof StructuredTool` still holds) and intercepts `_call`, which every\n * public entry point (`invoke`, `call`, `run`) ends up in. Blocked calls return the error JSON.\n */\nimport type { AgentGuard, ToolMeta } from \"../guard.js\";\nimport type { ToolAnnotationsLike } from \"@agentwares/agentguard-core\";\n\nexport interface LangChainToolLike {\n name: string;\n description?: string;\n _call?: (args: unknown, ...rest: unknown[]) => Promise<unknown> | unknown;\n invoke?: (args: unknown, ...rest: unknown[]) => Promise<unknown> | unknown;\n}\n\nexport interface LangChainWrapOptions {\n annotations?: ToolAnnotationsLike;\n outputSchema?: Record<string, unknown>;\n onBlock?: \"return\" | \"throw\";\n}\n\nfunction stringify(value: unknown): unknown {\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nexport function wrapLangChainTool<T extends LangChainToolLike>(\n ag: AgentGuard,\n tool: T,\n opts: LangChainWrapOptions = {},\n): T {\n const meta: ToolMeta = {\n name: tool.name,\n description: tool.description,\n annotations: opts.annotations,\n outputSchema: opts.outputSchema,\n };\n const guardCall = async (\n original: (...a: unknown[]) => unknown,\n args: unknown,\n rest: unknown[],\n ): Promise<unknown> => {\n const result = await ag.run(meta, args, () => original(args, ...rest));\n if (result.error && !result.ok) {\n if (opts.onBlock === \"throw\")\n throw Object.assign(new Error(`${result.error.code}: ${result.error.cause}`), result.error);\n return JSON.stringify(result.error);\n }\n return result.faked ? stringify(result.value) : result.value;\n };\n const wrapped = Object.create(tool) as T;\n if (typeof tool._call === \"function\") {\n const original = tool._call.bind(tool) as (...a: unknown[]) => unknown;\n Object.defineProperty(wrapped, \"_call\", {\n value: (args: unknown, ...rest: unknown[]) => guardCall(original, args, rest),\n writable: true,\n configurable: true,\n });\n } else if (typeof tool.invoke === \"function\") {\n const original = tool.invoke.bind(tool) as (...a: unknown[]) => unknown;\n Object.defineProperty(wrapped, \"invoke\", {\n value: (args: unknown, ...rest: unknown[]) => guardCall(original, args, rest),\n writable: true,\n configurable: true,\n });\n } else {\n throw new Error(`cannot wrap \"${tool.name}\": no _call or invoke`);\n }\n return wrapped;\n}\n\nexport function wrapLangChainTools<T extends LangChainToolLike>(\n ag: AgentGuard,\n tools: T[],\n opts: LangChainWrapOptions & { annotations?: Record<string, ToolAnnotationsLike> } = {},\n): T[] {\n return tools.map((t) =>\n wrapLangChainTool(ag, t, { ...opts, annotations: opts.annotations?.[t.name] }),\n );\n}\n","/**\n * @agentwares/agentguard-sdk — agentguard for tool calls that bypass MCP.\n *\n * const ag = await createGuard({ policy: \"agentguard.yaml\" });\n * const tools = wrapOpenAIAgentsTools(ag, [deleteContact, sendEmail]);\n * const openai = new OpenAI({ fetch: createGuardedFetch(ag) }); // hard spend limit on tokens\n */\nexport {\n AgentGuard,\n createGuard,\n newRunId,\n type AnyFn,\n type CreateGuardOptions,\n type ToolMeta,\n type WrapOptions,\n} from \"./guard.js\";\nexport {\n createGuardedFetch,\n detectProvider,\n normalizeUsage,\n usageFromJson,\n usageFromSse,\n type FetchLike,\n type GuardedFetchOptions,\n type Provider,\n} from \"./fetch.js\";\nexport {\n wrapOpenAIAgentsTool,\n wrapOpenAIAgentsTools,\n type OpenAIAgentsToolLike,\n type OpenAIAgentsWrapOptions,\n} from \"./adapters/openai-agents.js\";\nexport {\n wrapLangChainTool,\n wrapLangChainTools,\n type LangChainToolLike,\n type LangChainWrapOptions,\n} from \"./adapters/langchain.js\";\nexport {\n GuardError,\n type GuardErrorBody,\n type GuardEvent,\n type GuardResult,\n type Policy,\n type PolicyInput,\n type Report,\n} from \"@agentwares/agentguard-core\";\n"],"mappings":"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","names":["GuardError"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@agentwares/agentguard-sdk",
3
+ "version": "0.1.0",
4
+ "description": "agentguard for tool calls that bypass MCP: wrap OpenAI Agents SDK, LangChain or plain function tools with the same policy engine (spend caps, approvals, kill switch, dry-run, loop breaker, audit log) and cap LLM spend with a guarded fetch.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/agentwares/agentwares",
25
+ "directory": "packages/agentguard-sdk"
26
+ },
27
+ "keywords": [
28
+ "agentguard",
29
+ "openai-agents",
30
+ "langchain",
31
+ "ai-agents",
32
+ "spend-limit",
33
+ "kill-switch",
34
+ "guardrails",
35
+ "middleware"
36
+ ],
37
+ "sideEffects": false,
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "dependencies": {
42
+ "@agentwares/agentguard-core": "^0.1.0"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "typecheck": "tsc --noEmit",
47
+ "lint": "eslint src",
48
+ "test": "vitest run"
49
+ }
50
+ }