🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@moneolabs/guard

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@moneolabs/guard - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+1
-1
dist/index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/guard.ts","../src/ledger.ts","../src/policy.ts","../src/rules.ts","../src/match.ts","../src/approvers.ts","../src/simulate.ts"],"sourcesContent":["export {\n createGuard,\n spentSince,\n PolicyDeniedError,\n type Guard,\n type GuardOptions,\n type Usage,\n type BudgetUsage,\n type WrapMapping,\n} from \"./guard.js\";\n\nexport {\n compilePolicy,\n versionOf,\n type Policy,\n type CompiledPolicy,\n type BudgetRule,\n type VelocityRule,\n} from \"./policy.js\";\n\nexport {\n memoryLedger,\n committedTotal,\n countsTowardLimits,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n type EntryStatus,\n} from \"./ledger.js\";\n\nexport { manualApprover, autoApprover, loggingApprover, webhookApprover } from \"./approvers.js\";\n\nexport {\n simulate,\n eventsFromLedger,\n type SimulationEvent,\n type SimulationResult,\n type SimulationReport,\n} from \"./simulate.js\";\n\nexport { matchesPattern, matchesAny } from \"./match.js\";\n\nexport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n","import {\n convertPrecision,\n fromUnits,\n id,\n MoneoError,\n parseDuration,\n parseMoney,\n peggedPrices,\n subMoney,\n systemClock,\n toDecimalString,\n valueInUsd,\n type Clock,\n type DurationInput,\n type Money,\n type MoneyInput,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport {\n committedTotal,\n memoryLedger,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n} from \"./ledger.js\";\nimport { compilePolicy, type CompiledPolicy, type Policy } from \"./policy.js\";\nimport { evaluate } from \"./rules.js\";\nimport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n\nexport interface GuardOptions {\n /** Where verdicts are written. Defaults to an in-memory ledger. */\n ledger?: DecisionLedger;\n /** Injected so tests do not have to wait out a rolling window. */\n clock?: Clock;\n /** How non-dollar assets are valued. Defaults to pegged assets only. */\n prices?: PriceSource;\n /** Where holds go for a human answer. */\n approver?: Approver;\n /** Recorded on every decision that does not name its own agent. */\n agent?: string;\n}\n\nexport interface BudgetUsage {\n window: string;\n windowMs: number;\n max: Money;\n used: Money;\n remaining: Money;\n actions?: readonly ActionKind[];\n}\n\nexport interface Usage {\n policyVersion: string;\n budgets: BudgetUsage[];\n velocity?: { max: number; window: string; used: number; remaining: number };\n}\n\n/** Maps the arguments of a wrapped function onto an intent. */\nexport interface WrapMapping<A extends unknown[]> {\n action: ActionKind;\n amount: (...args: A) => MoneyInput;\n to?: (...args: A) => string | undefined;\n agent?: (...args: A) => string | undefined;\n memo?: (...args: A) => string | undefined;\n metadata?: (...args: A) => Record<string, unknown> | undefined;\n}\n\n/** Thrown by a wrapped function when policy refuses the call. */\nexport class PolicyDeniedError extends MoneoError {\n readonly decision: Decision;\n constructor(decision: Decision) {\n super(\"policy_denied\", decision.reason, {\n rule: decision.rule,\n policyVersion: decision.policyVersion,\n });\n this.name = \"PolicyDeniedError\";\n this.decision = decision;\n }\n}\n\nexport interface Guard {\n readonly policy: CompiledPolicy;\n readonly ledger: DecisionLedger;\n\n /** Evaluate an intent. Nothing is signed and nothing is charged. */\n check(intent: Intent): Promise<Decision>;\n /** Put an existing spending function behind this policy. */\n wrap<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n mapping: WrapMapping<A>,\n ): (...args: A) => Promise<R>;\n /** Answer a held decision. */\n resolve(approvalId: string, outcome: Omit<ApprovalOutcome, \"at\"> & { at?: number }): void;\n /** What is left of each budget right now. */\n usage(options?: { agent?: string }): Promise<Usage>;\n /** Read verdicts back, blocks included. */\n history(query?: LedgerQuery): Promise<LedgerEntry[]>;\n /** Swap the policy. Later decisions record the new version. */\n update(policy: Policy): CompiledPolicy;\n}\n\nexport function createGuard(policy: Policy, options: GuardOptions = {}): Guard {\n let compiled = compilePolicy(policy);\n const ledger = options.ledger ?? memoryLedger();\n const clock = options.clock ?? systemClock;\n const prices = options.prices ?? peggedPrices;\n const approver = options.approver;\n const defaultAgent = options.agent;\n\n /**\n * Held decisions, keyed by approval id. An entry survives being resolved so\n * that `wait()` still returns the answer when the approver replied before\n * anyone started waiting. Entries are dropped once the decision settles or is\n * released, which is the point at which nothing can ask again.\n */\n const pending = new Map<\n string,\n { resolve: (outcome: ApprovalOutcome) => void; promise: Promise<ApprovalOutcome> }\n >();\n\n async function resolveIntent(intent: Intent): Promise<ResolvedIntent> {\n const amount = parseMoney(intent.amount, \"USD\");\n const usd = await valueInUsd(amount, prices);\n return {\n ...intent,\n ...((intent.agent ?? defaultAgent) ? { agent: intent.agent ?? defaultAgent } : {}),\n amount,\n usd: convertPrecision(usd, \"USD\"),\n };\n }\n\n function longestWindowMs(): number {\n const windows = [...compiled.budgets.map((b) => b.windowMs), compiled.velocity?.windowMs ?? 0];\n return windows.length > 0 ? Math.max(...windows) : 0;\n }\n\n function buildDecision(\n entry: LedgerEntry,\n intent: ResolvedIntent,\n approvalId: string | undefined,\n ): Decision {\n const decision: Decision = {\n id: entry.id,\n verdict: entry.verdict,\n reason: entry.reason,\n rule: entry.rule,\n policyVersion: entry.policyVersion,\n intent,\n at: entry.at,\n ...(approvalId ? { approvalId } : {}),\n\n async settle(actual?: MoneyInput) {\n if (entry.verdict === \"block\") {\n throw new MoneoError(\n \"settle_blocked\",\n \"a blocked decision cannot settle: nothing was signed\",\n { id: entry.id },\n );\n }\n const patch: Parameters<DecisionLedger[\"update\"]>[1] = { status: \"settled\" };\n if (actual !== undefined) {\n const settledAmount = parseMoney(actual, intent.amount.asset);\n const settledUsd = convertPrecision(await valueInUsd(settledAmount, prices), \"USD\");\n patch.amount = toDecimalString(settledAmount);\n patch.usdCents = settledUsd.units;\n }\n await ledger.update(entry.id, patch);\n if (approvalId) pending.delete(approvalId);\n },\n\n async release() {\n await ledger.update(entry.id, { status: \"released\" });\n if (approvalId) pending.delete(approvalId);\n },\n\n async wait(waitOptions = {}) {\n if (entry.verdict !== \"hold\") {\n return { granted: entry.verdict === \"allow\", at: clock.now() };\n }\n const slot = approvalId ? pending.get(approvalId) : undefined;\n if (!slot) return { granted: false, at: clock.now() };\n if (waitOptions.timeout === undefined) return slot.promise;\n\n const ms = parseDuration(waitOptions.timeout as DurationInput);\n const timeout = clock.sleep(ms).then(() => {\n throw new MoneoError(\"approval_timeout\", `no answer within ${waitOptions.timeout}`, {\n approvalId,\n });\n });\n return Promise.race([slot.promise, timeout]);\n },\n };\n return decision;\n }\n\n const guard: Guard = {\n get policy() {\n return compiled;\n },\n ledger,\n\n async check(intent) {\n const resolved = await resolveIntent(intent);\n const now = clock.now();\n const window = longestWindowMs();\n const history = window > 0 ? await ledger.query({ since: now - window }) : [];\n\n const outcome = evaluate({ intent: resolved, policy: compiled, now, history });\n const verdict: Verdict = outcome?.verdict ?? \"allow\";\n\n const entry: LedgerEntry = {\n id: id(\"dec\"),\n at: now,\n ...(resolved.agent ? { agent: resolved.agent } : {}),\n action: resolved.action,\n ...(resolved.to ? { counterparty: resolved.to } : {}),\n asset: resolved.amount.asset,\n amount: toDecimalString(resolved.amount),\n usdCents: resolved.usd.units,\n verdict,\n reason: outcome?.reason ?? \"within policy\",\n rule: outcome?.rule ?? \"default\",\n policyVersion: compiled.version,\n status: verdict === \"block\" ? \"released\" : \"reserved\",\n ...(resolved.memo ? { memo: resolved.memo } : {}),\n ...(resolved.metadata ? { metadata: resolved.metadata } : {}),\n };\n await ledger.append(entry);\n\n let approvalId: string | undefined;\n if (verdict === \"hold\") {\n approvalId = id(\"apr\");\n let settle!: (outcome: ApprovalOutcome) => void;\n const promise = new Promise<ApprovalOutcome>((res) => {\n settle = res;\n });\n pending.set(approvalId, { resolve: settle, promise });\n }\n\n const decision = buildDecision(entry, resolved, approvalId);\n\n if (verdict === \"hold\" && approver && approvalId) {\n const answered = await approver.request(decision);\n if (answered) guard.resolve(approvalId, answered);\n }\n\n return decision;\n },\n\n wrap(fn, mapping) {\n return async (...args) => {\n const intent: Intent = {\n action: mapping.action,\n amount: mapping.amount(...args),\n };\n const to = mapping.to?.(...args);\n if (to !== undefined) intent.to = to;\n const agent = mapping.agent?.(...args);\n if (agent !== undefined) intent.agent = agent;\n const memo = mapping.memo?.(...args);\n if (memo !== undefined) intent.memo = memo;\n const metadata = mapping.metadata?.(...args);\n if (metadata !== undefined) intent.metadata = metadata;\n\n const decision = await guard.check(intent);\n if (decision.verdict === \"block\") throw new PolicyDeniedError(decision);\n if (decision.verdict === \"hold\") {\n const outcome = await decision.wait();\n if (!outcome.granted) throw new PolicyDeniedError(decision);\n }\n\n try {\n const result = await fn(...args);\n await decision.settle();\n return result;\n } catch (error) {\n // The call failed, so the money did not move. Give the budget back.\n await decision.release();\n throw error;\n }\n };\n },\n\n resolve(approvalId, outcome) {\n const slot = pending.get(approvalId);\n if (!slot) return;\n // Deliberately kept in the map: settle() and release() clean it up, so an\n // answer that arrives before anyone waits is not lost.\n slot.resolve({ ...outcome, at: outcome.at ?? clock.now() });\n },\n\n async usage(usageOptions = {}) {\n const now = clock.now();\n const budgets: BudgetUsage[] = [];\n for (const budget of compiled.budgets) {\n const query: LedgerQuery = { since: now - budget.windowMs };\n if (usageOptions.agent) query.agent = usageOptions.agent;\n const entries = await ledger.query(query);\n const scoped = budget.actions\n ? entries.filter((e) => budget.actions!.includes(e.action))\n : entries;\n const used = committedTotal(scoped);\n budgets.push({\n window: budget.label,\n windowMs: budget.windowMs,\n max: budget.max,\n used,\n remaining: clampToZero(subMoney(budget.max, used)),\n ...(budget.actions ? { actions: budget.actions } : {}),\n });\n }\n\n const usage: Usage = { policyVersion: compiled.version, budgets };\n\n if (compiled.velocity) {\n const entries = await ledger.query({ since: now - compiled.velocity.windowMs });\n const used = entries.filter((e) => e.status !== \"released\" && e.verdict !== \"block\").length;\n usage.velocity = {\n max: compiled.velocity.max,\n window: formatWindow(compiled.velocity.windowMs),\n used,\n remaining: Math.max(0, compiled.velocity.max - used),\n };\n }\n\n return usage;\n },\n\n history(query) {\n return ledger.query(query);\n },\n\n update(next) {\n compiled = compilePolicy(next);\n return compiled;\n },\n };\n\n return guard;\n}\n\nfunction clampToZero(m: Money): Money {\n return m.units < 0n ? fromUnits(0n, m.asset) : m;\n}\n\nfunction formatWindow(ms: number): string {\n const hours = ms / 3_600_000;\n if (Number.isInteger(hours) && hours >= 1) return `${hours}h`;\n const minutes = ms / 60_000;\n if (Number.isInteger(minutes) && minutes >= 1) return `${minutes}m`;\n return `${Math.round(ms / 1000)}s`;\n}\n\n/** Convenience for reading a running total straight out of a ledger. */\nexport async function spentSince(\n ledger: DecisionLedger,\n since: number,\n query: Omit<LedgerQuery, \"since\"> = {},\n): Promise<Money> {\n const entries = await ledger.query({ ...query, since });\n return committedTotal(entries);\n}\n","import { fromUnits, type Money } from \"@moneolabs/core\";\nimport type { ActionKind, Verdict } from \"./types.js\";\n\n/**\n * `reserved` means the guard said yes and the money has not been confirmed\n * moved yet. It still counts against budgets, because a payment in flight is\n * money you no longer have. `released` means it never happened.\n */\nexport type EntryStatus = \"reserved\" | \"settled\" | \"released\";\n\nexport interface LedgerEntry {\n id: string;\n at: number;\n agent?: string;\n action: ActionKind;\n counterparty?: string;\n asset: string;\n /** Amount in the asset's own minor units, as a decimal string. */\n amount: string;\n /** USD value in cents at the time of the decision. */\n usdCents: bigint;\n verdict: Verdict;\n reason: string;\n rule: string;\n policyVersion: string;\n status: EntryStatus;\n memo?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LedgerQuery {\n since?: number;\n until?: number;\n agent?: string;\n action?: ActionKind;\n counterparty?: string;\n verdict?: Verdict;\n status?: EntryStatus;\n limit?: number;\n}\n\n/**\n * Where verdicts are written and where budgets are read back from. The default\n * implementation keeps everything in memory. Swap it for Postgres, SQLite, or\n * whatever already holds your financial records.\n */\nexport interface DecisionLedger {\n append(entry: LedgerEntry): Promise<void>;\n update(\n id: string,\n patch: Partial<Pick<LedgerEntry, \"status\" | \"amount\" | \"usdCents\">>,\n ): Promise<void>;\n query(query?: LedgerQuery): Promise<LedgerEntry[]>;\n}\n\n/**\n * Whether an entry consumes budget and velocity.\n *\n * An allowed movement counts from the moment it is reserved, because money in\n * flight is money you no longer have. A held movement counts only once it has\n * settled, since a pending approval may never be granted. Blocks and releases\n * never count, which is what makes a refusal free.\n */\nexport function countsTowardLimits(entry: LedgerEntry): boolean {\n if (entry.status === \"released\") return false;\n if (entry.verdict === \"block\") return false;\n if (entry.verdict === \"hold\") return entry.status === \"settled\";\n return true;\n}\n\n/** Total USD counted against budgets. */\nexport function committedTotal(entries: readonly LedgerEntry[]): Money {\n let cents = 0n;\n for (const entry of entries) {\n if (countsTowardLimits(entry)) cents += entry.usdCents;\n }\n return fromUnits(cents, \"USD\");\n}\n\nexport function memoryLedger(seed: readonly LedgerEntry[] = []): DecisionLedger {\n const entries: LedgerEntry[] = [...seed];\n const index = new Map(entries.map((e) => [e.id, e]));\n\n return {\n async append(entry) {\n entries.push(entry);\n index.set(entry.id, entry);\n },\n async update(id, patch) {\n const entry = index.get(id);\n if (!entry) return;\n Object.assign(entry, patch);\n },\n async query(query = {}) {\n let out = entries;\n if (query.since !== undefined) out = out.filter((e) => e.at >= query.since!);\n if (query.until !== undefined) out = out.filter((e) => e.at <= query.until!);\n if (query.agent) out = out.filter((e) => e.agent === query.agent);\n if (query.action) out = out.filter((e) => e.action === query.action);\n if (query.counterparty) out = out.filter((e) => e.counterparty === query.counterparty);\n if (query.verdict) out = out.filter((e) => e.verdict === query.verdict);\n if (query.status) out = out.filter((e) => e.status === query.status);\n out = [...out].sort((a, b) => a.at - b.at);\n if (query.limit !== undefined) out = out.slice(-query.limit);\n return out;\n },\n };\n}\n","import {\n fingerprint,\n formatDuration,\n formatMoney,\n isPositiveMoney,\n parseDuration,\n parseMoney,\n toDecimalString,\n ValidationError,\n type DurationInput,\n type Money,\n type MoneyInput,\n} from \"@moneolabs/core\";\nimport type { ActionKind } from \"./types.js\";\n\n/** A rolling window and the most that may move through it. */\nexport interface BudgetRule {\n window: DurationInput;\n max: MoneyInput;\n /** Restrict this budget to certain actions. Omit to cover everything. */\n actions?: ActionKind[];\n}\n\nexport interface VelocityRule {\n /** Most decisions allowed inside the window. */\n max: number;\n per: DurationInput;\n /** Count only movements to the same counterparty. */\n perCounterparty?: boolean;\n}\n\nexport interface Policy {\n /** Ceiling on any single movement, in USD. */\n perTransaction?: { max: MoneyInput };\n /** Shorthand for a single 24 hour rolling budget. */\n rolling24h?: { max: MoneyInput };\n /** Any number of rolling windows, evaluated together. */\n budgets?: BudgetRule[];\n /** Rate limit on money, not on requests. */\n velocity?: VelocityRule;\n /**\n * \"allowlist-only\" blocks anything not matched by `allow`, and blocks\n * movements that name no counterparty at all. \"open\" runs `deny` only.\n */\n counterparties?: \"allowlist-only\" | \"open\";\n /** Patterns that may receive funds. `*` matches any run of characters. */\n allow?: string[];\n /** Patterns that may never receive funds. Checked before everything else. */\n deny?: string[];\n /** Assets this agent may move. Omit to allow all. */\n assets?: { allow?: string[]; deny?: string[] };\n /** Action kinds this agent may perform. Omit to allow all. */\n actions?: { allow?: ActionKind[]; deny?: ActionKind[] };\n /** Above this USD figure, a human has to say yes. */\n escalate?: { above: MoneyInput };\n /** Carried onto every decision. Useful for naming a policy in the ledger. */\n label?: string;\n}\n\nexport interface CompiledBudget {\n /**\n * How the window was written, for example \"24h\". Rule names and refusal\n * messages quote this back, so an author reading a blocked decision sees the\n * same words they put in the policy.\n */\n readonly label: string;\n readonly windowMs: number;\n readonly max: Money;\n readonly actions?: readonly ActionKind[];\n}\n\n/** A policy with every amount and duration resolved, plus a stable version. */\nexport interface CompiledPolicy {\n readonly version: string;\n readonly label?: string;\n readonly perTransactionMax?: Money;\n readonly budgets: readonly CompiledBudget[];\n readonly velocity?: { max: number; windowMs: number; perCounterparty: boolean };\n readonly counterparties: \"allowlist-only\" | \"open\";\n readonly allow: readonly string[];\n readonly deny: readonly string[];\n readonly assetsAllow?: readonly string[];\n readonly assetsDeny: readonly string[];\n readonly actionsAllow?: readonly ActionKind[];\n readonly actionsDeny: readonly ActionKind[];\n readonly escalateAbove?: Money;\n readonly source: Policy;\n}\n\n/**\n * Resolve a policy once, up front. Every amount is parsed, every window is\n * converted to milliseconds, and the result is fingerprinted so that each\n * verdict can name the exact policy that produced it.\n */\nexport function compilePolicy(policy: Policy): CompiledPolicy {\n const perTransactionMax = policy.perTransaction\n ? requirePositiveUsd(policy.perTransaction.max, \"perTransaction.max\")\n : undefined;\n\n const budgets: CompiledBudget[] = [];\n if (policy.rolling24h) {\n budgets.push({\n label: \"24h\",\n windowMs: parseDuration(\"24h\"),\n max: requirePositiveUsd(policy.rolling24h.max, \"rolling24h.max\"),\n });\n }\n for (const [index, budget] of (policy.budgets ?? []).entries()) {\n const windowMs = parseDuration(budget.window);\n if (windowMs <= 0) {\n throw new ValidationError(`budgets[${index}].window must be longer than zero`, { budget });\n }\n budgets.push({\n label: typeof budget.window === \"string\" ? budget.window.trim() : formatDuration(windowMs),\n windowMs,\n max: requirePositiveUsd(budget.max, `budgets[${index}].max`),\n ...(budget.actions ? { actions: [...budget.actions] } : {}),\n });\n }\n\n let velocity: CompiledPolicy[\"velocity\"];\n if (policy.velocity) {\n const windowMs = parseDuration(policy.velocity.per);\n if (!Number.isInteger(policy.velocity.max) || policy.velocity.max < 1) {\n throw new ValidationError(\"velocity.max must be a whole number of one or more\", {\n velocity: policy.velocity,\n });\n }\n if (windowMs <= 0) {\n throw new ValidationError(\"velocity.per must be longer than zero\", {\n velocity: policy.velocity,\n });\n }\n velocity = {\n max: policy.velocity.max,\n windowMs,\n perCounterparty: policy.velocity.perCounterparty ?? false,\n };\n }\n\n const counterparties = policy.counterparties ?? \"open\";\n if (counterparties === \"allowlist-only\" && (policy.allow ?? []).length === 0) {\n throw new ValidationError(\n \"counterparties is allowlist-only but allow is empty, which blocks every movement\",\n );\n }\n\n const escalateAbove = policy.escalate\n ? requirePositiveUsd(policy.escalate.above, \"escalate.above\")\n : undefined;\n\n const compiled: Omit<CompiledPolicy, \"version\"> = {\n ...(policy.label ? { label: policy.label } : {}),\n ...(perTransactionMax ? { perTransactionMax } : {}),\n budgets,\n ...(velocity ? { velocity } : {}),\n counterparties,\n allow: [...(policy.allow ?? [])],\n deny: [...(policy.deny ?? [])],\n ...(policy.assets?.allow ? { assetsAllow: policy.assets.allow.map(upper) } : {}),\n assetsDeny: (policy.assets?.deny ?? []).map(upper),\n ...(policy.actions?.allow ? { actionsAllow: [...policy.actions.allow] } : {}),\n actionsDeny: [...(policy.actions?.deny ?? [])],\n ...(escalateAbove ? { escalateAbove } : {}),\n source: policy,\n };\n\n return { ...compiled, version: versionOf(compiled) };\n}\n\n/**\n * A deterministic id for a policy. Two policies that differ only in key order\n * or in how an amount was written produce the same version, which is what makes\n * \"which policy blocked this\" answerable months later.\n */\nexport function versionOf(compiled: Omit<CompiledPolicy, \"version\">): string {\n const shape = {\n label: compiled.label,\n perTransactionMax: compiled.perTransactionMax && describe(compiled.perTransactionMax),\n // The budget's own label is left out: \"24h\" and 86400000 are the same rule.\n budgets: compiled.budgets.map((b) => ({\n windowMs: b.windowMs,\n max: describe(b.max),\n actions: b.actions ? [...b.actions].sort() : undefined,\n })),\n velocity: compiled.velocity,\n counterparties: compiled.counterparties,\n allow: [...compiled.allow].sort(),\n deny: [...compiled.deny].sort(),\n assetsAllow: compiled.assetsAllow ? [...compiled.assetsAllow].sort() : undefined,\n assetsDeny: [...compiled.assetsDeny].sort(),\n actionsAllow: compiled.actionsAllow ? [...compiled.actionsAllow].sort() : undefined,\n actionsDeny: [...compiled.actionsDeny].sort(),\n escalateAbove: compiled.escalateAbove && describe(compiled.escalateAbove),\n };\n return `pol_${fingerprint(shape)}`;\n}\n\nfunction describe(m: Money): string {\n return `${toDecimalString(m)} ${m.asset}`;\n}\n\nfunction upper(value: string): string {\n return value.toUpperCase();\n}\n\nfunction requirePositiveUsd(input: MoneyInput, field: string): Money {\n const amount = parseMoney(input, \"USD\");\n if (amount.asset !== \"USD\") {\n throw new ValidationError(`${field} must be in USD, got ${formatMoney(amount)}`, { field });\n }\n if (!isPositiveMoney(amount)) {\n throw new ValidationError(`${field} must be greater than zero`, { field });\n }\n return amount;\n}\n","import {\n addMoney,\n formatDuration,\n formatMoney,\n fromUnits,\n gtMoney,\n subMoney,\n type Money,\n} from \"@moneolabs/core\";\nimport { matchesAny } from \"./match.js\";\nimport type { CompiledPolicy } from \"./policy.js\";\nimport { committedTotal, countsTowardLimits, type LedgerEntry } from \"./ledger.js\";\nimport type { ResolvedIntent } from \"./types.js\";\n\nexport interface RuleContext {\n readonly intent: ResolvedIntent;\n readonly policy: CompiledPolicy;\n readonly now: number;\n /** Everything on the ledger inside the longest window the policy cares about. */\n readonly history: readonly LedgerEntry[];\n}\n\nexport interface RuleOutcome {\n verdict: \"hold\" | \"block\";\n rule: string;\n reason: string;\n}\n\ntype Rule = (context: RuleContext) => RuleOutcome | undefined;\n\n/**\n * Order is deliberate. Denylist first so a banned counterparty is never\n * described as merely over budget, and escalation last so a held decision is\n * one that passed every hard limit.\n */\nconst RULES: Rule[] = [\n denyList,\n assetRules,\n actionRules,\n allowList,\n perTransactionCap,\n velocityLimit,\n rollingBudgets,\n escalation,\n];\n\nexport function evaluate(context: RuleContext): RuleOutcome | undefined {\n let held: RuleOutcome | undefined;\n for (const rule of RULES) {\n const outcome = rule(context);\n if (!outcome) continue;\n if (outcome.verdict === \"block\") return outcome;\n held ??= outcome;\n }\n return held;\n}\n\n/* -------------------------------------------------------------------------- */\n\nfunction denyList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (!intent.to || policy.deny.length === 0) return undefined;\n const pattern = matchesAny(intent.to, policy.deny);\n if (!pattern) return undefined;\n return {\n verdict: \"block\",\n rule: \"denylist\",\n reason: `counterparty ${intent.to} is on the denylist (matched \"${pattern}\")`,\n };\n}\n\nfunction assetRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const asset = intent.amount.asset;\n if (policy.assetsDeny.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.deny\",\n reason: `this agent may not move ${asset}`,\n };\n }\n if (policy.assetsAllow && !policy.assetsAllow.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.allow\",\n reason: `this agent may only move ${policy.assetsAllow.join(\", \")}, not ${asset}`,\n };\n }\n return undefined;\n}\n\nfunction actionRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.actionsDeny.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.deny\",\n reason: `this agent may not perform \"${intent.action}\"`,\n };\n }\n if (policy.actionsAllow && !policy.actionsAllow.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.allow\",\n reason: `this agent may only perform ${policy.actionsAllow.join(\", \")}, not \"${intent.action}\"`,\n };\n }\n return undefined;\n}\n\nfunction allowList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.counterparties !== \"allowlist-only\") return undefined;\n\n // An intent with no counterparty must not be a way around the allowlist.\n if (!intent.to) {\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: \"policy is allowlist-only and this movement names no counterparty\",\n };\n }\n if (matchesAny(intent.to, policy.allow)) return undefined;\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: `counterparty ${intent.to} is not on the allowlist`,\n };\n}\n\nfunction perTransactionCap({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const max = policy.perTransactionMax;\n if (!max || !gtMoney(intent.usd, max)) return undefined;\n return {\n verdict: \"block\",\n rule: \"perTransaction\",\n reason: `${formatMoney(intent.usd)} exceeds the ${formatMoney(max)} per-transaction limit`,\n };\n}\n\nfunction velocityLimit({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n const velocity = policy.velocity;\n if (!velocity) return undefined;\n\n const since = now - velocity.windowMs;\n const recent = history.filter(\n (entry) =>\n entry.at > since &&\n countsTowardLimits(entry) &&\n (!velocity.perCounterparty || entry.counterparty === intent.to),\n );\n\n if (recent.length < velocity.max) return undefined;\n\n const scope = velocity.perCounterparty ? ` to ${intent.to}` : \"\";\n return {\n verdict: \"block\",\n rule: \"velocity\",\n reason:\n `${recent.length} movements${scope} in the last ${formatDuration(velocity.windowMs)} ` +\n `already meets the limit of ${velocity.max}`,\n };\n}\n\nfunction rollingBudgets({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n for (const budget of policy.budgets) {\n if (budget.actions && !budget.actions.includes(intent.action)) continue;\n\n const since = now - budget.windowMs;\n const window = history.filter(\n (entry) => entry.at > since && (!budget.actions || budget.actions.includes(entry.action)),\n );\n const used = committedTotal(window);\n const projected = addMoney(used, intent.usd);\n if (!gtMoney(projected, budget.max)) continue;\n\n const remaining = remainingOrZero(budget.max, used);\n return {\n verdict: \"block\",\n rule: `budget.${budget.label}`,\n reason:\n `${formatMoney(intent.usd)} exceeds the rolling ${budget.label} budget: ` +\n `${formatMoney(budget.max)} cap, ${formatMoney(used)} used, ` +\n `${formatMoney(remaining)} left`,\n };\n }\n return undefined;\n}\n\nfunction escalation({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const above = policy.escalateAbove;\n if (!above || !gtMoney(intent.usd, above)) return undefined;\n return {\n verdict: \"hold\",\n rule: \"escalate\",\n reason: `${formatMoney(intent.usd)} is above the ${formatMoney(above)} approval threshold`,\n };\n}\n\nfunction remainingOrZero(max: Money, used: Money): Money {\n const left = subMoney(max, used);\n return left.units < 0n ? fromUnits(0n, left.asset) : left;\n}\n","/**\n * Counterparty patterns. `*` matches any run of characters, everything else is\n * literal, and matching is case insensitive because hex addresses arrive in\n * whatever case the caller happened to have.\n *\n * Examples that all match \"x402:api.pricefeed.dev/quote\":\n * \"x402:*\" \"x402:api.pricefeed.dev/*\" \"*pricefeed*\"\n */\nconst cache = new Map<string, RegExp>();\n\nexport function matchesPattern(value: string, pattern: string): boolean {\n return toRegExp(pattern).test(value);\n}\n\nexport function matchesAny(value: string, patterns: readonly string[]): string | undefined {\n for (const pattern of patterns) {\n if (matchesPattern(value, pattern)) return pattern;\n }\n return undefined;\n}\n\nfunction toRegExp(pattern: string): RegExp {\n const hit = cache.get(pattern);\n if (hit) return hit;\n\n const source = `^${pattern.split(\"*\").map(escapeRegExp).join(\".*\")}$`;\n const compiled = new RegExp(source, \"iu\");\n\n // Patterns come from config, not user input, but the cache is still bounded\n // so a generated allowlist cannot grow it without limit.\n if (cache.size > 1000) cache.clear();\n cache.set(pattern, compiled);\n return compiled;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { formatMoney } from \"@moneolabs/core\";\nimport type { ApprovalOutcome, Approver, Decision } from \"./types.js\";\n\n/**\n * Leaves every hold pending until someone calls `guard.resolve()`. This is the\n * behaviour you get when no approver is configured, expressed explicitly.\n */\nexport const manualApprover: Approver = {\n async request() {\n return undefined;\n },\n};\n\n/** Answers every hold the same way. For tests and for local development. */\nexport function autoApprover(granted: boolean, by = \"auto\"): Approver {\n return {\n async request(decision) {\n return { granted, by, at: decision.at };\n },\n };\n}\n\n/** Prints the request and leaves it pending. Handy in a terminal session. */\nexport function loggingApprover(log: (message: string) => void = console.log): Approver {\n return {\n async request(decision) {\n log(\n `[moneo] approval needed: ${decision.intent.action} ${formatMoney(decision.intent.usd)}` +\n `${decision.intent.to ? ` to ${decision.intent.to}` : \"\"}` +\n ` (${decision.reason}). Resolve with id ${decision.approvalId}`,\n );\n return undefined;\n },\n };\n}\n\n/**\n * Posts the request to an HTTP endpoint. Return a JSON body of\n * `{ \"granted\": true }` to answer immediately, or anything else to leave the\n * decision pending for `guard.resolve()`.\n */\nexport function webhookApprover(\n url: string,\n options: { headers?: Record<string, string>; fetch?: typeof fetch } = {},\n): Approver {\n const doFetch = options.fetch ?? globalThis.fetch;\n return {\n async request(decision): Promise<ApprovalOutcome | undefined> {\n const response = await doFetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(summarize(decision)),\n });\n if (!response.ok) return undefined;\n\n const body = (await response.json().catch(() => null)) as {\n granted?: boolean;\n by?: string;\n note?: string;\n } | null;\n if (!body || typeof body.granted !== \"boolean\") return undefined;\n\n return {\n granted: body.granted,\n ...(body.by ? { by: body.by } : {}),\n ...(body.note ? { note: body.note } : {}),\n at: decision.at,\n };\n },\n };\n}\n\nfunction summarize(decision: Decision) {\n return {\n approvalId: decision.approvalId,\n decisionId: decision.id,\n policyVersion: decision.policyVersion,\n reason: decision.reason,\n rule: decision.rule,\n agent: decision.intent.agent,\n action: decision.intent.action,\n to: decision.intent.to,\n memo: decision.intent.memo,\n usd: formatMoney(decision.intent.usd),\n amount: formatMoney(decision.intent.amount),\n at: decision.at,\n };\n}\n","import { fromUnits, manualClock, peggedPrices, type Money, type PriceSource } from \"@moneolabs/core\";\nimport { createGuard } from \"./guard.js\";\nimport { memoryLedger, type LedgerEntry } from \"./ledger.js\";\nimport type { Policy } from \"./policy.js\";\nimport type { Intent, Verdict } from \"./types.js\";\n\nexport interface SimulationEvent extends Intent {\n /** When it happened. Defaults to the previous event's time. */\n at?: number;\n /**\n * Whether the movement actually completed. Reserved-but-never-settled spend\n * still counts against budgets, so replays should say.\n */\n settled?: boolean;\n}\n\nexport interface SimulationResult {\n event: SimulationEvent;\n verdict: Verdict;\n reason: string;\n rule: string;\n usd: Money;\n}\n\nexport interface SimulationReport {\n policyVersion: string;\n results: SimulationResult[];\n counts: Record<Verdict, number>;\n allowedUsd: Money;\n blockedUsd: Money;\n heldUsd: Money;\n entries: LedgerEntry[];\n}\n\n/**\n * Replay a list of movements against a policy without touching anything real.\n *\n * This is how you find out that the budget you are about to ship would have\n * blocked a third of last month before you ship it, rather than after.\n */\nexport async function simulate(\n policy: Policy,\n events: readonly SimulationEvent[],\n options: { prices?: PriceSource; startAt?: number } = {},\n): Promise<SimulationReport> {\n const clock = manualClock(options.startAt ?? events[0]?.at ?? 0);\n const guard = createGuard(policy, {\n ledger: memoryLedger(),\n clock,\n prices: options.prices ?? peggedPrices,\n });\n\n const results: SimulationResult[] = [];\n const counts: Record<Verdict, number> = { allow: 0, hold: 0, block: 0 };\n let allowedCents = 0n;\n let blockedCents = 0n;\n let heldCents = 0n;\n\n for (const event of events) {\n if (event.at !== undefined && event.at > clock.now()) {\n await clock.advance(event.at - clock.now());\n }\n\n const decision = await guard.check(event);\n counts[decision.verdict] += 1;\n\n const cents = decision.intent.usd.units;\n if (decision.verdict === \"allow\") {\n allowedCents += cents;\n if (event.settled !== false) await decision.settle();\n } else if (decision.verdict === \"hold\") {\n heldCents += cents;\n } else {\n blockedCents += cents;\n }\n\n results.push({\n event,\n verdict: decision.verdict,\n reason: decision.reason,\n rule: decision.rule,\n usd: decision.intent.usd,\n });\n }\n\n return {\n policyVersion: guard.policy.version,\n results,\n counts,\n allowedUsd: fromUnits(allowedCents, \"USD\"),\n blockedUsd: fromUnits(blockedCents, \"USD\"),\n heldUsd: fromUnits(heldCents, \"USD\"),\n entries: await guard.history(),\n };\n}\n\n/** Turn past ledger entries back into events, so you can replay real history. */\nexport function eventsFromLedger(entries: readonly LedgerEntry[]): SimulationEvent[] {\n return entries.map((entry) => ({\n action: entry.action,\n amount: `${entry.amount} ${entry.asset}`,\n ...(entry.counterparty ? { to: entry.counterparty } : {}),\n ...(entry.agent ? { agent: entry.agent } : {}),\n ...(entry.memo ? { memo: entry.memo } : {}),\n at: entry.at,\n settled: entry.status === \"settled\",\n }));\n}\n"],"mappings":"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","names":["import_core","id","import_core","import_core","import_core","import_core"]}
{"version":3,"sources":["../src/index.ts","../src/guard.ts","../src/ledger.ts","../src/policy.ts","../src/rules.ts","../src/match.ts","../src/approvers.ts","../src/simulate.ts"],"sourcesContent":["export {\n createGuard,\n spentSince,\n PolicyDeniedError,\n type Guard,\n type GuardOptions,\n type Usage,\n type BudgetUsage,\n type WrapMapping,\n} from \"./guard.js\";\n\nexport {\n compilePolicy,\n versionOf,\n type Policy,\n type CompiledPolicy,\n type BudgetRule,\n type VelocityRule,\n} from \"./policy.js\";\n\nexport {\n memoryLedger,\n committedTotal,\n countsTowardLimits,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n type EntryStatus,\n} from \"./ledger.js\";\n\nexport { manualApprover, autoApprover, loggingApprover, webhookApprover } from \"./approvers.js\";\n\nexport {\n simulate,\n eventsFromLedger,\n type SimulationEvent,\n type SimulationResult,\n type SimulationReport,\n} from \"./simulate.js\";\n\nexport { matchesPattern, matchesAny } from \"./match.js\";\n\nexport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n","import {\n convertPrecision,\n fromUnits,\n id,\n MoneoError,\n parseDuration,\n parseMoney,\n peggedPrices,\n subMoney,\n systemClock,\n toDecimalString,\n valueInUsd,\n type Clock,\n type DurationInput,\n type Money,\n type MoneyInput,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport {\n committedTotal,\n memoryLedger,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n} from \"./ledger.js\";\nimport { compilePolicy, type CompiledPolicy, type Policy } from \"./policy.js\";\nimport { evaluate } from \"./rules.js\";\nimport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n\nexport interface GuardOptions {\n /** Where verdicts are written. Defaults to an in-memory ledger. */\n ledger?: DecisionLedger;\n /** Injected so tests do not have to wait out a rolling window. */\n clock?: Clock;\n /** How non-dollar assets are valued. Defaults to pegged assets only. */\n prices?: PriceSource;\n /** Where holds go for a human answer. */\n approver?: Approver;\n /** Recorded on every decision that does not name its own agent. */\n agent?: string;\n}\n\nexport interface BudgetUsage {\n window: string;\n windowMs: number;\n max: Money;\n used: Money;\n remaining: Money;\n actions?: readonly ActionKind[];\n}\n\nexport interface Usage {\n policyVersion: string;\n budgets: BudgetUsage[];\n velocity?: { max: number; window: string; used: number; remaining: number };\n}\n\n/** Maps the arguments of a wrapped function onto an intent. */\nexport interface WrapMapping<A extends unknown[]> {\n action: ActionKind;\n amount: (...args: A) => MoneyInput;\n to?: (...args: A) => string | undefined;\n agent?: (...args: A) => string | undefined;\n memo?: (...args: A) => string | undefined;\n metadata?: (...args: A) => Record<string, unknown> | undefined;\n}\n\n/** Thrown by a wrapped function when policy refuses the call. */\nexport class PolicyDeniedError extends MoneoError {\n readonly decision: Decision;\n constructor(decision: Decision) {\n super(\"policy_denied\", decision.reason, {\n rule: decision.rule,\n policyVersion: decision.policyVersion,\n });\n this.name = \"PolicyDeniedError\";\n this.decision = decision;\n }\n}\n\nexport interface Guard {\n readonly policy: CompiledPolicy;\n readonly ledger: DecisionLedger;\n\n /** Evaluate an intent. Nothing is signed and nothing is charged. */\n check(intent: Intent): Promise<Decision>;\n /** Put an existing spending function behind this policy. */\n wrap<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n mapping: WrapMapping<A>,\n ): (...args: A) => Promise<R>;\n /** Answer a held decision. */\n resolve(approvalId: string, outcome: Omit<ApprovalOutcome, \"at\"> & { at?: number }): void;\n /** What is left of each budget right now. */\n usage(options?: { agent?: string }): Promise<Usage>;\n /** Read verdicts back, blocks included. */\n history(query?: LedgerQuery): Promise<LedgerEntry[]>;\n /** Swap the policy. Later decisions record the new version. */\n update(policy: Policy): CompiledPolicy;\n}\n\nexport function createGuard(policy: Policy, options: GuardOptions = {}): Guard {\n let compiled = compilePolicy(policy);\n const ledger = options.ledger ?? memoryLedger();\n const clock = options.clock ?? systemClock;\n const prices = options.prices ?? peggedPrices;\n const approver = options.approver;\n const defaultAgent = options.agent;\n\n /**\n * Held decisions, keyed by approval id. An entry survives being resolved so\n * that `wait()` still returns the answer when the approver replied before\n * anyone started waiting. Entries are dropped once the decision settles or is\n * released, which is the point at which nothing can ask again.\n */\n const pending = new Map<\n string,\n { resolve: (outcome: ApprovalOutcome) => void; promise: Promise<ApprovalOutcome> }\n >();\n\n async function resolveIntent(intent: Intent): Promise<ResolvedIntent> {\n const amount = parseMoney(intent.amount, \"USD\");\n const usd = await valueInUsd(amount, prices);\n return {\n ...intent,\n ...((intent.agent ?? defaultAgent) ? { agent: intent.agent ?? defaultAgent } : {}),\n amount,\n usd: convertPrecision(usd, \"USD\"),\n };\n }\n\n function longestWindowMs(): number {\n const windows = [...compiled.budgets.map((b) => b.windowMs), compiled.velocity?.windowMs ?? 0];\n return windows.length > 0 ? Math.max(...windows) : 0;\n }\n\n function buildDecision(\n entry: LedgerEntry,\n intent: ResolvedIntent,\n approvalId: string | undefined,\n ): Decision {\n const decision: Decision = {\n id: entry.id,\n verdict: entry.verdict,\n reason: entry.reason,\n rule: entry.rule,\n policyVersion: entry.policyVersion,\n intent,\n at: entry.at,\n ...(approvalId ? { approvalId } : {}),\n\n async settle(actual?: MoneyInput) {\n if (entry.verdict === \"block\") {\n throw new MoneoError(\n \"settle_blocked\",\n \"a blocked decision cannot settle: nothing was signed\",\n { id: entry.id },\n );\n }\n const patch: Parameters<DecisionLedger[\"update\"]>[1] = { status: \"settled\" };\n if (actual !== undefined) {\n const settledAmount = parseMoney(actual, intent.amount.asset);\n const settledUsd = convertPrecision(await valueInUsd(settledAmount, prices), \"USD\");\n patch.amount = toDecimalString(settledAmount);\n patch.usdCents = settledUsd.units;\n }\n await ledger.update(entry.id, patch);\n if (approvalId) pending.delete(approvalId);\n },\n\n async release() {\n await ledger.update(entry.id, { status: \"released\" });\n if (approvalId) pending.delete(approvalId);\n },\n\n async wait(waitOptions = {}) {\n if (entry.verdict !== \"hold\") {\n return { granted: entry.verdict === \"allow\", at: clock.now() };\n }\n const slot = approvalId ? pending.get(approvalId) : undefined;\n if (!slot) return { granted: false, at: clock.now() };\n if (waitOptions.timeout === undefined) return slot.promise;\n\n const ms = parseDuration(waitOptions.timeout as DurationInput);\n const timeout = clock.sleep(ms).then(() => {\n throw new MoneoError(\"approval_timeout\", `no answer within ${waitOptions.timeout}`, {\n approvalId,\n });\n });\n return Promise.race([slot.promise, timeout]);\n },\n };\n return decision;\n }\n\n const guard: Guard = {\n get policy() {\n return compiled;\n },\n ledger,\n\n async check(intent) {\n const resolved = await resolveIntent(intent);\n const now = clock.now();\n const window = longestWindowMs();\n const history = window > 0 ? await ledger.query({ since: now - window }) : [];\n\n const outcome = evaluate({ intent: resolved, policy: compiled, now, history });\n const verdict: Verdict = outcome?.verdict ?? \"allow\";\n\n const entry: LedgerEntry = {\n id: id(\"dec\"),\n at: now,\n ...(resolved.agent ? { agent: resolved.agent } : {}),\n action: resolved.action,\n ...(resolved.to ? { counterparty: resolved.to } : {}),\n asset: resolved.amount.asset,\n amount: toDecimalString(resolved.amount),\n usdCents: resolved.usd.units,\n verdict,\n reason: outcome?.reason ?? \"within policy\",\n rule: outcome?.rule ?? \"default\",\n policyVersion: compiled.version,\n status: verdict === \"block\" ? \"released\" : \"reserved\",\n ...(resolved.memo ? { memo: resolved.memo } : {}),\n ...(resolved.metadata ? { metadata: resolved.metadata } : {}),\n };\n await ledger.append(entry);\n\n let approvalId: string | undefined;\n if (verdict === \"hold\") {\n approvalId = id(\"apr\");\n let settle!: (outcome: ApprovalOutcome) => void;\n const promise = new Promise<ApprovalOutcome>((res) => {\n settle = res;\n });\n pending.set(approvalId, { resolve: settle, promise });\n }\n\n const decision = buildDecision(entry, resolved, approvalId);\n\n if (verdict === \"hold\" && approver && approvalId) {\n const answered = await approver.request(decision);\n if (answered) guard.resolve(approvalId, answered);\n }\n\n return decision;\n },\n\n wrap(fn, mapping) {\n return async (...args) => {\n const intent: Intent = {\n action: mapping.action,\n amount: mapping.amount(...args),\n };\n const to = mapping.to?.(...args);\n if (to !== undefined) intent.to = to;\n const agent = mapping.agent?.(...args);\n if (agent !== undefined) intent.agent = agent;\n const memo = mapping.memo?.(...args);\n if (memo !== undefined) intent.memo = memo;\n const metadata = mapping.metadata?.(...args);\n if (metadata !== undefined) intent.metadata = metadata;\n\n const decision = await guard.check(intent);\n if (decision.verdict === \"block\") throw new PolicyDeniedError(decision);\n if (decision.verdict === \"hold\") {\n const outcome = await decision.wait();\n if (!outcome.granted) throw new PolicyDeniedError(decision);\n }\n\n try {\n const result = await fn(...args);\n await decision.settle();\n return result;\n } catch (error) {\n // The call failed, so the money did not move. Give the budget back.\n await decision.release();\n throw error;\n }\n };\n },\n\n resolve(approvalId, outcome) {\n const slot = pending.get(approvalId);\n if (!slot) return;\n // Deliberately kept in the map: settle() and release() clean it up, so an\n // answer that arrives before anyone waits is not lost.\n slot.resolve({ ...outcome, at: outcome.at ?? clock.now() });\n },\n\n async usage(usageOptions = {}) {\n const now = clock.now();\n const budgets: BudgetUsage[] = [];\n for (const budget of compiled.budgets) {\n const query: LedgerQuery = { since: now - budget.windowMs };\n if (usageOptions.agent) query.agent = usageOptions.agent;\n const entries = await ledger.query(query);\n const scoped = budget.actions\n ? entries.filter((e) => budget.actions!.includes(e.action))\n : entries;\n const used = committedTotal(scoped);\n budgets.push({\n window: budget.label,\n windowMs: budget.windowMs,\n max: budget.max,\n used,\n remaining: clampToZero(subMoney(budget.max, used)),\n ...(budget.actions ? { actions: budget.actions } : {}),\n });\n }\n\n const usage: Usage = { policyVersion: compiled.version, budgets };\n\n if (compiled.velocity) {\n const entries = await ledger.query({ since: now - compiled.velocity.windowMs });\n const used = entries.filter((e) => e.status !== \"released\" && e.verdict !== \"block\").length;\n usage.velocity = {\n max: compiled.velocity.max,\n window: formatWindow(compiled.velocity.windowMs),\n used,\n remaining: Math.max(0, compiled.velocity.max - used),\n };\n }\n\n return usage;\n },\n\n history(query) {\n return ledger.query(query);\n },\n\n update(next) {\n compiled = compilePolicy(next);\n return compiled;\n },\n };\n\n return guard;\n}\n\nfunction clampToZero(m: Money): Money {\n return m.units < 0n ? fromUnits(0n, m.asset) : m;\n}\n\nfunction formatWindow(ms: number): string {\n const hours = ms / 3_600_000;\n if (Number.isInteger(hours) && hours >= 1) return `${hours}h`;\n const minutes = ms / 60_000;\n if (Number.isInteger(minutes) && minutes >= 1) return `${minutes}m`;\n return `${Math.round(ms / 1000)}s`;\n}\n\n/** Convenience for reading a running total straight out of a ledger. */\nexport async function spentSince(\n ledger: DecisionLedger,\n since: number,\n query: Omit<LedgerQuery, \"since\"> = {},\n): Promise<Money> {\n const entries = await ledger.query({ ...query, since });\n return committedTotal(entries);\n}\n","import { fromUnits, type Money } from \"@moneolabs/core\";\nimport type { ActionKind, Verdict } from \"./types.js\";\n\n/**\n * `reserved` means the guard said yes and the money has not been confirmed\n * moved yet. It still counts against budgets, because a payment in flight is\n * money you no longer have. `released` means it never happened.\n */\nexport type EntryStatus = \"reserved\" | \"settled\" | \"released\";\n\nexport interface LedgerEntry {\n id: string;\n at: number;\n agent?: string;\n action: ActionKind;\n counterparty?: string;\n asset: string;\n /** Amount in the asset's own minor units, as a decimal string. */\n amount: string;\n /** USD value in cents at the time of the decision. */\n usdCents: bigint;\n verdict: Verdict;\n reason: string;\n rule: string;\n policyVersion: string;\n status: EntryStatus;\n memo?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LedgerQuery {\n since?: number;\n until?: number;\n agent?: string;\n action?: ActionKind;\n counterparty?: string;\n verdict?: Verdict;\n status?: EntryStatus;\n limit?: number;\n}\n\n/**\n * Where verdicts are written and where budgets are read back from. The default\n * implementation keeps everything in memory. Swap it for Postgres, SQLite, or\n * whatever already holds your financial records.\n */\nexport interface DecisionLedger {\n append(entry: LedgerEntry): Promise<void>;\n update(\n id: string,\n patch: Partial<Pick<LedgerEntry, \"status\" | \"amount\" | \"usdCents\">>,\n ): Promise<void>;\n query(query?: LedgerQuery): Promise<LedgerEntry[]>;\n}\n\n/**\n * Whether an entry consumes budget and velocity.\n *\n * An allowed movement counts from the moment it is reserved, because money in\n * flight is money you no longer have. A held movement counts only once it has\n * settled, since a pending approval may never be granted. Blocks and releases\n * never count, which is what makes a refusal free.\n */\nexport function countsTowardLimits(entry: LedgerEntry): boolean {\n if (entry.status === \"released\") return false;\n if (entry.verdict === \"block\") return false;\n if (entry.verdict === \"hold\") return entry.status === \"settled\";\n return true;\n}\n\n/** Total USD counted against budgets. */\nexport function committedTotal(entries: readonly LedgerEntry[]): Money {\n let cents = 0n;\n for (const entry of entries) {\n if (countsTowardLimits(entry)) cents += entry.usdCents;\n }\n return fromUnits(cents, \"USD\");\n}\n\nexport function memoryLedger(seed: readonly LedgerEntry[] = []): DecisionLedger {\n const entries: LedgerEntry[] = [...seed];\n const index = new Map(entries.map((e) => [e.id, e]));\n\n return {\n async append(entry) {\n entries.push(entry);\n index.set(entry.id, entry);\n },\n async update(id, patch) {\n const entry = index.get(id);\n if (!entry) return;\n Object.assign(entry, patch);\n },\n async query(query = {}) {\n let out = entries;\n if (query.since !== undefined) out = out.filter((e) => e.at >= query.since!);\n if (query.until !== undefined) out = out.filter((e) => e.at <= query.until!);\n if (query.agent) out = out.filter((e) => e.agent === query.agent);\n if (query.action) out = out.filter((e) => e.action === query.action);\n if (query.counterparty) out = out.filter((e) => e.counterparty === query.counterparty);\n if (query.verdict) out = out.filter((e) => e.verdict === query.verdict);\n if (query.status) out = out.filter((e) => e.status === query.status);\n out = [...out].sort((a, b) => a.at - b.at);\n if (query.limit !== undefined) out = out.slice(-query.limit);\n return out;\n },\n };\n}\n","import {\n fingerprint,\n formatDuration,\n formatMoney,\n isPositiveMoney,\n parseDuration,\n parseMoney,\n toDecimalString,\n ValidationError,\n type DurationInput,\n type Money,\n type MoneyInput,\n} from \"@moneolabs/core\";\nimport type { ActionKind } from \"./types.js\";\n\n/** A rolling window and the most that may move through it. */\nexport interface BudgetRule {\n window: DurationInput;\n max: MoneyInput;\n /** Restrict this budget to certain actions. Omit to cover everything. */\n actions?: ActionKind[];\n}\n\nexport interface VelocityRule {\n /** Most decisions allowed inside the window. */\n max: number;\n per: DurationInput;\n /** Count only movements to the same counterparty. */\n perCounterparty?: boolean;\n}\n\nexport interface Policy {\n /** Ceiling on any single movement, in USD. */\n perTransaction?: { max: MoneyInput };\n /** Shorthand for a single 24 hour rolling budget. */\n rolling24h?: { max: MoneyInput };\n /** Any number of rolling windows, evaluated together. */\n budgets?: BudgetRule[];\n /** Rate limit on money, not on requests. */\n velocity?: VelocityRule;\n /**\n * \"allowlist-only\" blocks anything not matched by `allow`, and blocks\n * movements that name no counterparty at all. \"open\" runs `deny` only.\n */\n counterparties?: \"allowlist-only\" | \"open\";\n /** Patterns that may receive funds. `*` matches any run of characters. */\n allow?: string[];\n /** Patterns that may never receive funds. Checked before everything else. */\n deny?: string[];\n /** Assets this agent may move. Omit to allow all. */\n assets?: { allow?: string[]; deny?: string[] };\n /** Action kinds this agent may perform. Omit to allow all. */\n actions?: { allow?: ActionKind[]; deny?: ActionKind[] };\n /** Above this USD figure, a human has to say yes. */\n escalate?: { above: MoneyInput };\n /** Carried onto every decision. Useful for naming a policy in the ledger. */\n label?: string;\n}\n\nexport interface CompiledBudget {\n /**\n * How the window was written, for example \"24h\". Rule names and refusal\n * messages quote this back, so an author reading a blocked decision sees the\n * same words they put in the policy.\n */\n readonly label: string;\n readonly windowMs: number;\n readonly max: Money;\n readonly actions?: readonly ActionKind[];\n}\n\n/** A policy with every amount and duration resolved, plus a stable version. */\nexport interface CompiledPolicy {\n readonly version: string;\n readonly label?: string;\n readonly perTransactionMax?: Money;\n readonly budgets: readonly CompiledBudget[];\n readonly velocity?: { max: number; windowMs: number; perCounterparty: boolean };\n readonly counterparties: \"allowlist-only\" | \"open\";\n readonly allow: readonly string[];\n readonly deny: readonly string[];\n readonly assetsAllow?: readonly string[];\n readonly assetsDeny: readonly string[];\n readonly actionsAllow?: readonly ActionKind[];\n readonly actionsDeny: readonly ActionKind[];\n readonly escalateAbove?: Money;\n readonly source: Policy;\n}\n\n/**\n * Resolve a policy once, up front. Every amount is parsed, every window is\n * converted to milliseconds, and the result is fingerprinted so that each\n * verdict can name the exact policy that produced it.\n */\nexport function compilePolicy(policy: Policy): CompiledPolicy {\n const perTransactionMax = policy.perTransaction\n ? requirePositiveUsd(policy.perTransaction.max, \"perTransaction.max\")\n : undefined;\n\n const budgets: CompiledBudget[] = [];\n if (policy.rolling24h) {\n budgets.push({\n label: \"24h\",\n windowMs: parseDuration(\"24h\"),\n max: requirePositiveUsd(policy.rolling24h.max, \"rolling24h.max\"),\n });\n }\n for (const [index, budget] of (policy.budgets ?? []).entries()) {\n const windowMs = parseDuration(budget.window);\n if (windowMs <= 0) {\n throw new ValidationError(`budgets[${index}].window must be longer than zero`, { budget });\n }\n budgets.push({\n label: typeof budget.window === \"string\" ? budget.window.trim() : formatDuration(windowMs),\n windowMs,\n max: requirePositiveUsd(budget.max, `budgets[${index}].max`),\n ...(budget.actions ? { actions: [...budget.actions] } : {}),\n });\n }\n\n let velocity: CompiledPolicy[\"velocity\"];\n if (policy.velocity) {\n const windowMs = parseDuration(policy.velocity.per);\n if (!Number.isInteger(policy.velocity.max) || policy.velocity.max < 1) {\n throw new ValidationError(\"velocity.max must be a whole number of one or more\", {\n velocity: policy.velocity,\n });\n }\n if (windowMs <= 0) {\n throw new ValidationError(\"velocity.per must be longer than zero\", {\n velocity: policy.velocity,\n });\n }\n velocity = {\n max: policy.velocity.max,\n windowMs,\n perCounterparty: policy.velocity.perCounterparty ?? false,\n };\n }\n\n const counterparties = policy.counterparties ?? \"open\";\n if (counterparties === \"allowlist-only\" && (policy.allow ?? []).length === 0) {\n throw new ValidationError(\n \"counterparties is allowlist-only but allow is empty, which blocks every movement\",\n );\n }\n\n const escalateAbove = policy.escalate\n ? requirePositiveUsd(policy.escalate.above, \"escalate.above\")\n : undefined;\n\n const compiled: Omit<CompiledPolicy, \"version\"> = {\n ...(policy.label ? { label: policy.label } : {}),\n ...(perTransactionMax ? { perTransactionMax } : {}),\n budgets,\n ...(velocity ? { velocity } : {}),\n counterparties,\n allow: [...(policy.allow ?? [])],\n deny: [...(policy.deny ?? [])],\n ...(policy.assets?.allow ? { assetsAllow: policy.assets.allow.map(upper) } : {}),\n assetsDeny: (policy.assets?.deny ?? []).map(upper),\n ...(policy.actions?.allow ? { actionsAllow: [...policy.actions.allow] } : {}),\n actionsDeny: [...(policy.actions?.deny ?? [])],\n ...(escalateAbove ? { escalateAbove } : {}),\n source: policy,\n };\n\n return { ...compiled, version: versionOf(compiled) };\n}\n\n/**\n * A deterministic id for a policy. Two policies that differ only in key order\n * or in how an amount was written produce the same version, which is what makes\n * \"which policy blocked this\" answerable months later.\n */\nexport function versionOf(compiled: Omit<CompiledPolicy, \"version\">): string {\n const shape = {\n label: compiled.label,\n perTransactionMax: compiled.perTransactionMax && describe(compiled.perTransactionMax),\n // The budget's own label is left out: \"24h\" and 86400000 are the same rule.\n budgets: compiled.budgets.map((b) => ({\n windowMs: b.windowMs,\n max: describe(b.max),\n actions: b.actions ? [...b.actions].sort() : undefined,\n })),\n velocity: compiled.velocity,\n counterparties: compiled.counterparties,\n allow: [...compiled.allow].sort(),\n deny: [...compiled.deny].sort(),\n assetsAllow: compiled.assetsAllow ? [...compiled.assetsAllow].sort() : undefined,\n assetsDeny: [...compiled.assetsDeny].sort(),\n actionsAllow: compiled.actionsAllow ? [...compiled.actionsAllow].sort() : undefined,\n actionsDeny: [...compiled.actionsDeny].sort(),\n escalateAbove: compiled.escalateAbove && describe(compiled.escalateAbove),\n };\n return `pol_${fingerprint(shape)}`;\n}\n\nfunction describe(m: Money): string {\n return `${toDecimalString(m)} ${m.asset}`;\n}\n\nfunction upper(value: string): string {\n return value.toUpperCase();\n}\n\nfunction requirePositiveUsd(input: MoneyInput, field: string): Money {\n const amount = parseMoney(input, \"USD\");\n if (amount.asset !== \"USD\") {\n throw new ValidationError(`${field} must be in USD, got ${formatMoney(amount)}`, { field });\n }\n if (!isPositiveMoney(amount)) {\n throw new ValidationError(`${field} must be greater than zero`, { field });\n }\n return amount;\n}\n","import {\n addMoney,\n formatDuration,\n formatMoney,\n fromUnits,\n gtMoney,\n subMoney,\n type Money,\n} from \"@moneolabs/core\";\nimport { matchesAny } from \"./match.js\";\nimport type { CompiledPolicy } from \"./policy.js\";\nimport { committedTotal, countsTowardLimits, type LedgerEntry } from \"./ledger.js\";\nimport type { ResolvedIntent } from \"./types.js\";\n\nexport interface RuleContext {\n readonly intent: ResolvedIntent;\n readonly policy: CompiledPolicy;\n readonly now: number;\n /** Everything on the ledger inside the longest window the policy cares about. */\n readonly history: readonly LedgerEntry[];\n}\n\nexport interface RuleOutcome {\n verdict: \"hold\" | \"block\";\n rule: string;\n reason: string;\n}\n\ntype Rule = (context: RuleContext) => RuleOutcome | undefined;\n\n/**\n * Order is deliberate. Denylist first so a banned counterparty is never\n * described as merely over budget, and escalation last so a held decision is\n * one that passed every hard limit.\n */\nconst RULES: Rule[] = [\n denyList,\n assetRules,\n actionRules,\n allowList,\n perTransactionCap,\n velocityLimit,\n rollingBudgets,\n escalation,\n];\n\nexport function evaluate(context: RuleContext): RuleOutcome | undefined {\n let held: RuleOutcome | undefined;\n for (const rule of RULES) {\n const outcome = rule(context);\n if (!outcome) continue;\n if (outcome.verdict === \"block\") return outcome;\n held ??= outcome;\n }\n return held;\n}\n\n/* -------------------------------------------------------------------------- */\n\nfunction denyList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (!intent.to || policy.deny.length === 0) return undefined;\n const pattern = matchesAny(intent.to, policy.deny);\n if (!pattern) return undefined;\n return {\n verdict: \"block\",\n rule: \"denylist\",\n reason: `counterparty ${intent.to} is on the denylist (matched \"${pattern}\")`,\n };\n}\n\nfunction assetRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const asset = intent.amount.asset;\n if (policy.assetsDeny.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.deny\",\n reason: `this agent may not move ${asset}`,\n };\n }\n if (policy.assetsAllow && !policy.assetsAllow.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.allow\",\n reason: `this agent may only move ${policy.assetsAllow.join(\", \")}, not ${asset}`,\n };\n }\n return undefined;\n}\n\nfunction actionRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.actionsDeny.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.deny\",\n reason: `this agent may not perform \"${intent.action}\"`,\n };\n }\n if (policy.actionsAllow && !policy.actionsAllow.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.allow\",\n reason: `this agent may only perform ${policy.actionsAllow.join(\", \")}, not \"${intent.action}\"`,\n };\n }\n return undefined;\n}\n\nfunction allowList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.counterparties !== \"allowlist-only\") return undefined;\n\n // An intent with no counterparty must not be a way around the allowlist.\n if (!intent.to) {\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: \"policy is allowlist-only and this movement names no counterparty\",\n };\n }\n if (matchesAny(intent.to, policy.allow)) return undefined;\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: `counterparty ${intent.to} is not on the allowlist`,\n };\n}\n\nfunction perTransactionCap({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const max = policy.perTransactionMax;\n if (!max || !gtMoney(intent.usd, max)) return undefined;\n return {\n verdict: \"block\",\n rule: \"perTransaction\",\n reason: `${formatMoney(intent.usd)} exceeds the ${formatMoney(max)} per-transaction limit`,\n };\n}\n\nfunction velocityLimit({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n const velocity = policy.velocity;\n if (!velocity) return undefined;\n\n const since = now - velocity.windowMs;\n const recent = history.filter(\n (entry) =>\n entry.at > since &&\n countsTowardLimits(entry) &&\n (!velocity.perCounterparty || entry.counterparty === intent.to),\n );\n\n if (recent.length < velocity.max) return undefined;\n\n const scope = velocity.perCounterparty ? ` to ${intent.to}` : \"\";\n return {\n verdict: \"block\",\n rule: \"velocity\",\n reason:\n `${recent.length} movements${scope} in the last ${formatDuration(velocity.windowMs)} ` +\n `already meets the limit of ${velocity.max}`,\n };\n}\n\nfunction rollingBudgets({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n for (const budget of policy.budgets) {\n if (budget.actions && !budget.actions.includes(intent.action)) continue;\n\n const since = now - budget.windowMs;\n const window = history.filter(\n (entry) => entry.at > since && (!budget.actions || budget.actions.includes(entry.action)),\n );\n const used = committedTotal(window);\n const projected = addMoney(used, intent.usd);\n if (!gtMoney(projected, budget.max)) continue;\n\n const remaining = remainingOrZero(budget.max, used);\n return {\n verdict: \"block\",\n rule: `budget.${budget.label}`,\n reason:\n `${formatMoney(intent.usd)} exceeds the rolling ${budget.label} budget: ` +\n `${formatMoney(budget.max)} cap, ${formatMoney(used)} used, ` +\n `${formatMoney(remaining)} left`,\n };\n }\n return undefined;\n}\n\nfunction escalation({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const above = policy.escalateAbove;\n if (!above || !gtMoney(intent.usd, above)) return undefined;\n return {\n verdict: \"hold\",\n rule: \"escalate\",\n reason: `${formatMoney(intent.usd)} is above the ${formatMoney(above)} approval threshold`,\n };\n}\n\nfunction remainingOrZero(max: Money, used: Money): Money {\n const left = subMoney(max, used);\n return left.units < 0n ? fromUnits(0n, left.asset) : left;\n}\n","/**\n * Counterparty patterns. `*` matches any run of characters, everything else is\n * literal, and matching is case insensitive because hex addresses arrive in\n * whatever case the caller happened to have.\n *\n * Examples that all match \"x402:api.pricefeed.dev/quote\":\n * \"x402:*\" \"x402:api.pricefeed.dev/*\" \"*pricefeed*\"\n */\nconst cache = new Map<string, RegExp>();\n\nexport function matchesPattern(value: string, pattern: string): boolean {\n return toRegExp(pattern).test(value);\n}\n\nexport function matchesAny(value: string, patterns: readonly string[]): string | undefined {\n for (const pattern of patterns) {\n if (matchesPattern(value, pattern)) return pattern;\n }\n return undefined;\n}\n\nfunction toRegExp(pattern: string): RegExp {\n const hit = cache.get(pattern);\n if (hit) return hit;\n\n const source = `^${pattern.split(\"*\").map(escapeRegExp).join(\".*\")}$`;\n const compiled = new RegExp(source, \"iu\");\n\n // Patterns come from config, not user input, but the cache is still bounded\n // so a generated allowlist cannot grow it without limit.\n if (cache.size > 1000) cache.clear();\n cache.set(pattern, compiled);\n return compiled;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { formatMoney } from \"@moneolabs/core\";\nimport type { ApprovalOutcome, Approver, Decision } from \"./types.js\";\n\n/**\n * Leaves every hold pending until someone calls `guard.resolve()`. This is the\n * behaviour you get when no approver is configured, expressed explicitly.\n */\nexport const manualApprover: Approver = {\n async request() {\n return undefined;\n },\n};\n\n/** Answers every hold the same way. For tests and for local development. */\nexport function autoApprover(granted: boolean, by = \"auto\"): Approver {\n return {\n async request(decision) {\n return { granted, by, at: decision.at };\n },\n };\n}\n\n/** Prints the request and leaves it pending. Handy in a terminal session. */\nexport function loggingApprover(log: (message: string) => void = console.log): Approver {\n return {\n async request(decision) {\n log(\n `[moneo] approval needed: ${decision.intent.action} ${formatMoney(decision.intent.usd)}` +\n `${decision.intent.to ? ` to ${decision.intent.to}` : \"\"}` +\n ` (${decision.reason}). Resolve with id ${decision.approvalId}`,\n );\n return undefined;\n },\n };\n}\n\n/**\n * Posts the request to an HTTP endpoint. Return a JSON body of\n * `{ \"granted\": true }` to answer immediately, or anything else to leave the\n * decision pending for `guard.resolve()`.\n */\nexport function webhookApprover(\n url: string,\n options: { headers?: Record<string, string>; fetch?: typeof fetch } = {},\n): Approver {\n const doFetch = options.fetch ?? globalThis.fetch;\n return {\n async request(decision): Promise<ApprovalOutcome | undefined> {\n const response = await doFetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(summarize(decision)),\n });\n if (!response.ok) return undefined;\n\n const body = (await response.json().catch(() => null)) as {\n granted?: boolean;\n by?: string;\n note?: string;\n } | null;\n if (!body || typeof body.granted !== \"boolean\") return undefined;\n\n return {\n granted: body.granted,\n ...(body.by ? { by: body.by } : {}),\n ...(body.note ? { note: body.note } : {}),\n at: decision.at,\n };\n },\n };\n}\n\nfunction summarize(decision: Decision) {\n return {\n approvalId: decision.approvalId,\n decisionId: decision.id,\n policyVersion: decision.policyVersion,\n reason: decision.reason,\n rule: decision.rule,\n agent: decision.intent.agent,\n action: decision.intent.action,\n to: decision.intent.to,\n memo: decision.intent.memo,\n usd: formatMoney(decision.intent.usd),\n amount: formatMoney(decision.intent.amount),\n at: decision.at,\n };\n}\n","import {\n fromUnits,\n manualClock,\n peggedPrices,\n type Money,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport { createGuard } from \"./guard.js\";\nimport { memoryLedger, type LedgerEntry } from \"./ledger.js\";\nimport type { Policy } from \"./policy.js\";\nimport type { Intent, Verdict } from \"./types.js\";\n\nexport interface SimulationEvent extends Intent {\n /** When it happened. Defaults to the previous event's time. */\n at?: number;\n /**\n * Whether the movement actually completed. Reserved-but-never-settled spend\n * still counts against budgets, so replays should say.\n */\n settled?: boolean;\n}\n\nexport interface SimulationResult {\n event: SimulationEvent;\n verdict: Verdict;\n reason: string;\n rule: string;\n usd: Money;\n}\n\nexport interface SimulationReport {\n policyVersion: string;\n results: SimulationResult[];\n counts: Record<Verdict, number>;\n allowedUsd: Money;\n blockedUsd: Money;\n heldUsd: Money;\n entries: LedgerEntry[];\n}\n\n/**\n * Replay a list of movements against a policy without touching anything real.\n *\n * This is how you find out that the budget you are about to ship would have\n * blocked a third of last month before you ship it, rather than after.\n */\nexport async function simulate(\n policy: Policy,\n events: readonly SimulationEvent[],\n options: { prices?: PriceSource; startAt?: number } = {},\n): Promise<SimulationReport> {\n const clock = manualClock(options.startAt ?? events[0]?.at ?? 0);\n const guard = createGuard(policy, {\n ledger: memoryLedger(),\n clock,\n prices: options.prices ?? peggedPrices,\n });\n\n const results: SimulationResult[] = [];\n const counts: Record<Verdict, number> = { allow: 0, hold: 0, block: 0 };\n let allowedCents = 0n;\n let blockedCents = 0n;\n let heldCents = 0n;\n\n for (const event of events) {\n if (event.at !== undefined && event.at > clock.now()) {\n await clock.advance(event.at - clock.now());\n }\n\n const decision = await guard.check(event);\n counts[decision.verdict] += 1;\n\n const cents = decision.intent.usd.units;\n if (decision.verdict === \"allow\") {\n allowedCents += cents;\n if (event.settled !== false) await decision.settle();\n } else if (decision.verdict === \"hold\") {\n heldCents += cents;\n } else {\n blockedCents += cents;\n }\n\n results.push({\n event,\n verdict: decision.verdict,\n reason: decision.reason,\n rule: decision.rule,\n usd: decision.intent.usd,\n });\n }\n\n return {\n policyVersion: guard.policy.version,\n results,\n counts,\n allowedUsd: fromUnits(allowedCents, \"USD\"),\n blockedUsd: fromUnits(blockedCents, \"USD\"),\n heldUsd: fromUnits(heldCents, \"USD\"),\n entries: await guard.history(),\n };\n}\n\n/** Turn past ledger entries back into events, so you can replay real history. */\nexport function eventsFromLedger(entries: readonly LedgerEntry[]): SimulationEvent[] {\n return entries.map((entry) => ({\n action: entry.action,\n amount: `${entry.amount} ${entry.asset}`,\n ...(entry.counterparty ? { to: entry.counterparty } : {}),\n ...(entry.agent ? { agent: entry.agent } : {}),\n ...(entry.memo ? { memo: entry.memo } : {}),\n at: entry.at,\n settled: entry.status === \"settled\",\n }));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,eAiBO;;;ACjBP,kBAAsC;AA+D/B,SAAS,mBAAmB,OAA6B;AAC9D,MAAI,MAAM,WAAW,WAAY,QAAO;AACxC,MAAI,MAAM,YAAY,QAAS,QAAO;AACtC,MAAI,MAAM,YAAY,OAAQ,QAAO,MAAM,WAAW;AACtD,SAAO;AACT;AAGO,SAAS,eAAe,SAAwC;AACrE,MAAI,QAAQ;AACZ,aAAW,SAAS,SAAS;AAC3B,QAAI,mBAAmB,KAAK,EAAG,UAAS,MAAM;AAAA,EAChD;AACA,aAAO,uBAAU,OAAO,KAAK;AAC/B;AAEO,SAAS,aAAa,OAA+B,CAAC,GAAmB;AAC9E,QAAM,UAAyB,CAAC,GAAG,IAAI;AACvC,QAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAEnD,SAAO;AAAA,IACL,MAAM,OAAO,OAAO;AAClB,cAAQ,KAAK,KAAK;AAClB,YAAM,IAAI,MAAM,IAAI,KAAK;AAAA,IAC3B;AAAA,IACA,MAAM,OAAOC,KAAI,OAAO;AACtB,YAAM,QAAQ,MAAM,IAAIA,GAAE;AAC1B,UAAI,CAAC,MAAO;AACZ,aAAO,OAAO,OAAO,KAAK;AAAA,IAC5B;AAAA,IACA,MAAM,MAAM,QAAQ,CAAC,GAAG;AACtB,UAAI,MAAM;AACV,UAAI,MAAM,UAAU,OAAW,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,MAAM,KAAM;AAC3E,UAAI,MAAM,UAAU,OAAW,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,MAAM,KAAM;AAC3E,UAAI,MAAM,MAAO,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,KAAK;AAChE,UAAI,MAAM,OAAQ,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,MAAM;AACnE,UAAI,MAAM,aAAc,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,iBAAiB,MAAM,YAAY;AACrF,UAAI,MAAM,QAAS,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,YAAY,MAAM,OAAO;AACtE,UAAI,MAAM,OAAQ,OAAM,IAAI,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,MAAM;AACnE,YAAM,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;AACzC,UAAI,MAAM,UAAU,OAAW,OAAM,IAAI,MAAM,CAAC,MAAM,KAAK;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC3GA,IAAAC,eAYO;AAkFA,SAAS,cAAc,QAAgC;AAC5D,QAAM,oBAAoB,OAAO,iBAC7B,mBAAmB,OAAO,eAAe,KAAK,oBAAoB,IAClE;AAEJ,QAAM,UAA4B,CAAC;AACnC,MAAI,OAAO,YAAY;AACrB,YAAQ,KAAK;AAAA,MACX,OAAO;AAAA,MACP,cAAU,4BAAc,KAAK;AAAA,MAC7B,KAAK,mBAAmB,OAAO,WAAW,KAAK,gBAAgB;AAAA,IACjE,CAAC;AAAA,EACH;AACA,aAAW,CAAC,OAAO,MAAM,MAAM,OAAO,WAAW,CAAC,GAAG,QAAQ,GAAG;AAC9D,UAAM,eAAW,4BAAc,OAAO,MAAM;AAC5C,QAAI,YAAY,GAAG;AACjB,YAAM,IAAI,6BAAgB,WAAW,KAAK,qCAAqC,EAAE,OAAO,CAAC;AAAA,IAC3F;AACA,YAAQ,KAAK;AAAA,MACX,OAAO,OAAO,OAAO,WAAW,WAAW,OAAO,OAAO,KAAK,QAAI,6BAAe,QAAQ;AAAA,MACzF;AAAA,MACA,KAAK,mBAAmB,OAAO,KAAK,WAAW,KAAK,OAAO;AAAA,MAC3D,GAAI,OAAO,UAAU,EAAE,SAAS,CAAC,GAAG,OAAO,OAAO,EAAE,IAAI,CAAC;AAAA,IAC3D,CAAC;AAAA,EACH;AAEA,MAAI;AACJ,MAAI,OAAO,UAAU;AACnB,UAAM,eAAW,4BAAc,OAAO,SAAS,GAAG;AAClD,QAAI,CAAC,OAAO,UAAU,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,MAAM,GAAG;AACrE,YAAM,IAAI,6BAAgB,sDAAsD;AAAA,QAC9E,UAAU,OAAO;AAAA,MACnB,CAAC;AAAA,IACH;AACA,QAAI,YAAY,GAAG;AACjB,YAAM,IAAI,6BAAgB,yCAAyC;AAAA,QACjE,UAAU,OAAO;AAAA,MACnB,CAAC;AAAA,IACH;AACA,eAAW;AAAA,MACT,KAAK,OAAO,SAAS;AAAA,MACrB;AAAA,MACA,iBAAiB,OAAO,SAAS,mBAAmB;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,iBAAiB,OAAO,kBAAkB;AAChD,MAAI,mBAAmB,qBAAqB,OAAO,SAAS,CAAC,GAAG,WAAW,GAAG;AAC5E,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAAgB,OAAO,WACzB,mBAAmB,OAAO,SAAS,OAAO,gBAAgB,IAC1D;AAEJ,QAAM,WAA4C;AAAA,IAChD,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC9C,GAAI,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;AAAA,IACjD;AAAA,IACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B;AAAA,IACA,OAAO,CAAC,GAAI,OAAO,SAAS,CAAC,CAAE;AAAA,IAC/B,MAAM,CAAC,GAAI,OAAO,QAAQ,CAAC,CAAE;AAAA,IAC7B,GAAI,OAAO,QAAQ,QAAQ,EAAE,aAAa,OAAO,OAAO,MAAM,IAAI,KAAK,EAAE,IAAI,CAAC;AAAA,IAC9E,aAAa,OAAO,QAAQ,QAAQ,CAAC,GAAG,IAAI,KAAK;AAAA,IACjD,GAAI,OAAO,SAAS,QAAQ,EAAE,cAAc,CAAC,GAAG,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC;AAAA,IAC3E,aAAa,CAAC,GAAI,OAAO,SAAS,QAAQ,CAAC,CAAE;AAAA,IAC7C,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;AAAA,IACzC,QAAQ;AAAA,EACV;AAEA,SAAO,EAAE,GAAG,UAAU,SAAS,UAAU,QAAQ,EAAE;AACrD;AAOO,SAAS,UAAU,UAAmD;AAC3E,QAAM,QAAQ;AAAA,IACZ,OAAO,SAAS;AAAA,IAChB,mBAAmB,SAAS,qBAAqB,SAAS,SAAS,iBAAiB;AAAA;AAAA,IAEpF,SAAS,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,MACpC,UAAU,EAAE;AAAA,MACZ,KAAK,SAAS,EAAE,GAAG;AAAA,MACnB,SAAS,EAAE,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,IAAI;AAAA,IAC/C,EAAE;AAAA,IACF,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,IACzB,OAAO,CAAC,GAAG,SAAS,KAAK,EAAE,KAAK;AAAA,IAChC,MAAM,CAAC,GAAG,SAAS,IAAI,EAAE,KAAK;AAAA,IAC9B,aAAa,SAAS,cAAc,CAAC,GAAG,SAAS,WAAW,EAAE,KAAK,IAAI;AAAA,IACvE,YAAY,CAAC,GAAG,SAAS,UAAU,EAAE,KAAK;AAAA,IAC1C,cAAc,SAAS,eAAe,CAAC,GAAG,SAAS,YAAY,EAAE,KAAK,IAAI;AAAA,IAC1E,aAAa,CAAC,GAAG,SAAS,WAAW,EAAE,KAAK;AAAA,IAC5C,eAAe,SAAS,iBAAiB,SAAS,SAAS,aAAa;AAAA,EAC1E;AACA,SAAO,WAAO,0BAAY,KAAK,CAAC;AAClC;AAEA,SAAS,SAAS,GAAkB;AAClC,SAAO,OAAG,8BAAgB,CAAC,CAAC,IAAI,EAAE,KAAK;AACzC;AAEA,SAAS,MAAM,OAAuB;AACpC,SAAO,MAAM,YAAY;AAC3B;AAEA,SAAS,mBAAmB,OAAmB,OAAsB;AACnE,QAAM,aAAS,yBAAW,OAAO,KAAK;AACtC,MAAI,OAAO,UAAU,OAAO;AAC1B,UAAM,IAAI,6BAAgB,GAAG,KAAK,4BAAwB,0BAAY,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;AAAA,EAC5F;AACA,MAAI,KAAC,8BAAgB,MAAM,GAAG;AAC5B,UAAM,IAAI,6BAAgB,GAAG,KAAK,8BAA8B,EAAE,MAAM,CAAC;AAAA,EAC3E;AACA,SAAO;AACT;;;ACvNA,IAAAC,eAQO;;;ACAP,IAAM,QAAQ,oBAAI,IAAoB;AAE/B,SAAS,eAAe,OAAe,SAA0B;AACtE,SAAO,SAAS,OAAO,EAAE,KAAK,KAAK;AACrC;AAEO,SAAS,WAAW,OAAe,UAAiD;AACzF,aAAW,WAAW,UAAU;AAC9B,QAAI,eAAe,OAAO,OAAO,EAAG,QAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAEA,SAAS,SAAS,SAAyB;AACzC,QAAM,MAAM,MAAM,IAAI,OAAO;AAC7B,MAAI,IAAK,QAAO;AAEhB,QAAM,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,YAAY,EAAE,KAAK,IAAI,CAAC;AAClE,QAAM,WAAW,IAAI,OAAO,QAAQ,IAAI;AAIxC,MAAI,MAAM,OAAO,IAAM,OAAM,MAAM;AACnC,QAAM,IAAI,SAAS,QAAQ;AAC3B,SAAO;AACT;AAEA,SAAS,aAAa,MAAsB;AAC1C,SAAO,KAAK,QAAQ,uBAAuB,MAAM;AACnD;;;ADFA,IAAM,QAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,SAAS,SAA+C;AACtE,MAAI;AACJ,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,OAAO;AAC5B,QAAI,CAAC,QAAS;AACd,QAAI,QAAQ,YAAY,QAAS,QAAO;AACxC,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAIA,SAAS,SAAS,EAAE,QAAQ,OAAO,GAAyC;AAC1E,MAAI,CAAC,OAAO,MAAM,OAAO,KAAK,WAAW,EAAG,QAAO;AACnD,QAAM,UAAU,WAAW,OAAO,IAAI,OAAO,IAAI;AACjD,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,gBAAgB,OAAO,EAAE,iCAAiC,OAAO;AAAA,EAC3E;AACF;AAEA,SAAS,WAAW,EAAE,QAAQ,OAAO,GAAyC;AAC5E,QAAM,QAAQ,OAAO,OAAO;AAC5B,MAAI,OAAO,WAAW,SAAS,KAAK,GAAG;AACrC,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,2BAA2B,KAAK;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,OAAO,eAAe,CAAC,OAAO,YAAY,SAAS,KAAK,GAAG;AAC7D,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,4BAA4B,OAAO,YAAY,KAAK,IAAI,CAAC,SAAS,KAAK;AAAA,IACjF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,EAAE,QAAQ,OAAO,GAAyC;AAC7E,MAAI,OAAO,YAAY,SAAS,OAAO,MAAM,GAAG;AAC9C,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,+BAA+B,OAAO,MAAM;AAAA,IACtD;AAAA,EACF;AACA,MAAI,OAAO,gBAAgB,CAAC,OAAO,aAAa,SAAS,OAAO,MAAM,GAAG;AACvE,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,+BAA+B,OAAO,aAAa,KAAK,IAAI,CAAC,UAAU,OAAO,MAAM;AAAA,IAC9F;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,UAAU,EAAE,QAAQ,OAAO,GAAyC;AAC3E,MAAI,OAAO,mBAAmB,iBAAkB,QAAO;AAGvD,MAAI,CAAC,OAAO,IAAI;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACA,MAAI,WAAW,OAAO,IAAI,OAAO,KAAK,EAAG,QAAO;AAChD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,gBAAgB,OAAO,EAAE;AAAA,EACnC;AACF;AAEA,SAAS,kBAAkB,EAAE,QAAQ,OAAO,GAAyC;AACnF,QAAM,MAAM,OAAO;AACnB,MAAI,CAAC,OAAO,KAAC,sBAAQ,OAAO,KAAK,GAAG,EAAG,QAAO;AAC9C,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,OAAG,0BAAY,OAAO,GAAG,CAAC,oBAAgB,0BAAY,GAAG,CAAC;AAAA,EACpE;AACF;AAEA,SAAS,cAAc,EAAE,QAAQ,QAAQ,KAAK,QAAQ,GAAyC;AAC7F,QAAM,WAAW,OAAO;AACxB,MAAI,CAAC,SAAU,QAAO;AAEtB,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,SAAS,QAAQ;AAAA,IACrB,CAAC,UACC,MAAM,KAAK,SACX,mBAAmB,KAAK,MACvB,CAAC,SAAS,mBAAmB,MAAM,iBAAiB,OAAO;AAAA,EAChE;AAEA,MAAI,OAAO,SAAS,SAAS,IAAK,QAAO;AAEzC,QAAM,QAAQ,SAAS,kBAAkB,OAAO,OAAO,EAAE,KAAK;AAC9D,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QACE,GAAG,OAAO,MAAM,aAAa,KAAK,oBAAgB,6BAAe,SAAS,QAAQ,CAAC,+BACrD,SAAS,GAAG;AAAA,EAC9C;AACF;AAEA,SAAS,eAAe,EAAE,QAAQ,QAAQ,KAAK,QAAQ,GAAyC;AAC9F,aAAW,UAAU,OAAO,SAAS;AACnC,QAAI,OAAO,WAAW,CAAC,OAAO,QAAQ,SAAS,OAAO,MAAM,EAAG;AAE/D,UAAM,QAAQ,MAAM,OAAO;AAC3B,UAAM,SAAS,QAAQ;AAAA,MACrB,CAAC,UAAU,MAAM,KAAK,UAAU,CAAC,OAAO,WAAW,OAAO,QAAQ,SAAS,MAAM,MAAM;AAAA,IACzF;AACA,UAAM,OAAO,eAAe,MAAM;AAClC,UAAM,gBAAY,uBAAS,MAAM,OAAO,GAAG;AAC3C,QAAI,KAAC,sBAAQ,WAAW,OAAO,GAAG,EAAG;AAErC,UAAM,YAAY,gBAAgB,OAAO,KAAK,IAAI;AAClD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,UAAU,OAAO,KAAK;AAAA,MAC5B,QACE,OAAG,0BAAY,OAAO,GAAG,CAAC,wBAAwB,OAAO,KAAK,gBAC3D,0BAAY,OAAO,GAAG,CAAC,aAAS,0BAAY,IAAI,CAAC,cACjD,0BAAY,SAAS,CAAC;AAAA,IAC7B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,EAAE,QAAQ,OAAO,GAAyC;AAC5E,QAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,SAAS,KAAC,sBAAQ,OAAO,KAAK,KAAK,EAAG,QAAO;AAClD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,OAAG,0BAAY,OAAO,GAAG,CAAC,qBAAiB,0BAAY,KAAK,CAAC;AAAA,EACvE;AACF;AAEA,SAAS,gBAAgB,KAAY,MAAoB;AACvD,QAAM,WAAO,uBAAS,KAAK,IAAI;AAC/B,SAAO,KAAK,QAAQ,SAAK,wBAAU,IAAI,KAAK,KAAK,IAAI;AACvD;;;AH1HO,IAAM,oBAAN,cAAgC,wBAAW;AAAA,EACvC;AAAA,EACT,YAAY,UAAoB;AAC9B,UAAM,iBAAiB,SAAS,QAAQ;AAAA,MACtC,MAAM,SAAS;AAAA,MACf,eAAe,SAAS;AAAA,IAC1B,CAAC;AACD,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EAClB;AACF;AAuBO,SAAS,YAAY,QAAgB,UAAwB,CAAC,GAAU;AAC7E,MAAI,WAAW,cAAc,MAAM;AACnC,QAAM,SAAS,QAAQ,UAAU,aAAa;AAC9C,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ;AACzB,QAAM,eAAe,QAAQ;AAQ7B,QAAM,UAAU,oBAAI,IAGlB;AAEF,iBAAe,cAAc,QAAyC;AACpE,UAAM,aAAS,yBAAW,OAAO,QAAQ,KAAK;AAC9C,UAAM,MAAM,UAAM,yBAAW,QAAQ,MAAM;AAC3C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAK,OAAO,SAAS,eAAgB,EAAE,OAAO,OAAO,SAAS,aAAa,IAAI,CAAC;AAAA,MAChF;AAAA,MACA,SAAK,+BAAiB,KAAK,KAAK;AAAA,IAClC;AAAA,EACF;AAEA,WAAS,kBAA0B;AACjC,UAAM,UAAU,CAAC,GAAG,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,UAAU,YAAY,CAAC;AAC7F,WAAO,QAAQ,SAAS,IAAI,KAAK,IAAI,GAAG,OAAO,IAAI;AAAA,EACrD;AAEA,WAAS,cACP,OACA,QACA,YACU;AACV,UAAM,WAAqB;AAAA,MACzB,IAAI,MAAM;AAAA,MACV,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,eAAe,MAAM;AAAA,MACrB;AAAA,MACA,IAAI,MAAM;AAAA,MACV,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MAEnC,MAAM,OAAO,QAAqB;AAChC,YAAI,MAAM,YAAY,SAAS;AAC7B,gBAAM,IAAI;AAAA,YACR;AAAA,YACA;AAAA,YACA,EAAE,IAAI,MAAM,GAAG;AAAA,UACjB;AAAA,QACF;AACA,cAAM,QAAiD,EAAE,QAAQ,UAAU;AAC3E,YAAI,WAAW,QAAW;AACxB,gBAAM,oBAAgB,yBAAW,QAAQ,OAAO,OAAO,KAAK;AAC5D,gBAAM,iBAAa,+BAAiB,UAAM,yBAAW,eAAe,MAAM,GAAG,KAAK;AAClF,gBAAM,aAAS,8BAAgB,aAAa;AAC5C,gBAAM,WAAW,WAAW;AAAA,QAC9B;AACA,cAAM,OAAO,OAAO,MAAM,IAAI,KAAK;AACnC,YAAI,WAAY,SAAQ,OAAO,UAAU;AAAA,MAC3C;AAAA,MAEA,MAAM,UAAU;AACd,cAAM,OAAO,OAAO,MAAM,IAAI,EAAE,QAAQ,WAAW,CAAC;AACpD,YAAI,WAAY,SAAQ,OAAO,UAAU;AAAA,MAC3C;AAAA,MAEA,MAAM,KAAK,cAAc,CAAC,GAAG;AAC3B,YAAI,MAAM,YAAY,QAAQ;AAC5B,iBAAO,EAAE,SAAS,MAAM,YAAY,SAAS,IAAI,MAAM,IAAI,EAAE;AAAA,QAC/D;AACA,cAAM,OAAO,aAAa,QAAQ,IAAI,UAAU,IAAI;AACpD,YAAI,CAAC,KAAM,QAAO,EAAE,SAAS,OAAO,IAAI,MAAM,IAAI,EAAE;AACpD,YAAI,YAAY,YAAY,OAAW,QAAO,KAAK;AAEnD,cAAM,SAAK,4BAAc,YAAY,OAAwB;AAC7D,cAAM,UAAU,MAAM,MAAM,EAAE,EAAE,KAAK,MAAM;AACzC,gBAAM,IAAI,wBAAW,oBAAoB,oBAAoB,YAAY,OAAO,IAAI;AAAA,YAClF;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AACD,eAAO,QAAQ,KAAK,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,MAC7C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,QAAe;AAAA,IACnB,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA;AAAA,IAEA,MAAM,MAAM,QAAQ;AAClB,YAAM,WAAW,MAAM,cAAc,MAAM;AAC3C,YAAM,MAAM,MAAM,IAAI;AACtB,YAAM,SAAS,gBAAgB;AAC/B,YAAM,UAAU,SAAS,IAAI,MAAM,OAAO,MAAM,EAAE,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;AAE5E,YAAM,UAAU,SAAS,EAAE,QAAQ,UAAU,QAAQ,UAAU,KAAK,QAAQ,CAAC;AAC7E,YAAM,UAAmB,SAAS,WAAW;AAE7C,YAAM,QAAqB;AAAA,QACzB,QAAI,iBAAG,KAAK;AAAA,QACZ,IAAI;AAAA,QACJ,GAAI,SAAS,QAAQ,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,QAClD,QAAQ,SAAS;AAAA,QACjB,GAAI,SAAS,KAAK,EAAE,cAAc,SAAS,GAAG,IAAI,CAAC;AAAA,QACnD,OAAO,SAAS,OAAO;AAAA,QACvB,YAAQ,8BAAgB,SAAS,MAAM;AAAA,QACvC,UAAU,SAAS,IAAI;AAAA,QACvB;AAAA,QACA,QAAQ,SAAS,UAAU;AAAA,QAC3B,MAAM,SAAS,QAAQ;AAAA,QACvB,eAAe,SAAS;AAAA,QACxB,QAAQ,YAAY,UAAU,aAAa;AAAA,QAC3C,GAAI,SAAS,OAAO,EAAE,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,QAC/C,GAAI,SAAS,WAAW,EAAE,UAAU,SAAS,SAAS,IAAI,CAAC;AAAA,MAC7D;AACA,YAAM,OAAO,OAAO,KAAK;AAEzB,UAAI;AACJ,UAAI,YAAY,QAAQ;AACtB,yBAAa,iBAAG,KAAK;AACrB,YAAI;AACJ,cAAM,UAAU,IAAI,QAAyB,CAAC,QAAQ;AACpD,mBAAS;AAAA,QACX,CAAC;AACD,gBAAQ,IAAI,YAAY,EAAE,SAAS,QAAQ,QAAQ,CAAC;AAAA,MACtD;AAEA,YAAM,WAAW,cAAc,OAAO,UAAU,UAAU;AAE1D,UAAI,YAAY,UAAU,YAAY,YAAY;AAChD,cAAM,WAAW,MAAM,SAAS,QAAQ,QAAQ;AAChD,YAAI,SAAU,OAAM,QAAQ,YAAY,QAAQ;AAAA,MAClD;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAI,SAAS;AAChB,aAAO,UAAU,SAAS;AACxB,cAAM,SAAiB;AAAA,UACrB,QAAQ,QAAQ;AAAA,UAChB,QAAQ,QAAQ,OAAO,GAAG,IAAI;AAAA,QAChC;AACA,cAAM,KAAK,QAAQ,KAAK,GAAG,IAAI;AAC/B,YAAI,OAAO,OAAW,QAAO,KAAK;AAClC,cAAM,QAAQ,QAAQ,QAAQ,GAAG,IAAI;AACrC,YAAI,UAAU,OAAW,QAAO,QAAQ;AACxC,cAAM,OAAO,QAAQ,OAAO,GAAG,IAAI;AACnC,YAAI,SAAS,OAAW,QAAO,OAAO;AACtC,cAAM,WAAW,QAAQ,WAAW,GAAG,IAAI;AAC3C,YAAI,aAAa,OAAW,QAAO,WAAW;AAE9C,cAAM,WAAW,MAAM,MAAM,MAAM,MAAM;AACzC,YAAI,SAAS,YAAY,QAAS,OAAM,IAAI,kBAAkB,QAAQ;AACtE,YAAI,SAAS,YAAY,QAAQ;AAC/B,gBAAM,UAAU,MAAM,SAAS,KAAK;AACpC,cAAI,CAAC,QAAQ,QAAS,OAAM,IAAI,kBAAkB,QAAQ;AAAA,QAC5D;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,gBAAM,SAAS,OAAO;AACtB,iBAAO;AAAA,QACT,SAAS,OAAO;AAEd,gBAAM,SAAS,QAAQ;AACvB,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IAEA,QAAQ,YAAY,SAAS;AAC3B,YAAM,OAAO,QAAQ,IAAI,UAAU;AACnC,UAAI,CAAC,KAAM;AAGX,WAAK,QAAQ,EAAE,GAAG,SAAS,IAAI,QAAQ,MAAM,MAAM,IAAI,EAAE,CAAC;AAAA,IAC5D;AAAA,IAEA,MAAM,MAAM,eAAe,CAAC,GAAG;AAC7B,YAAM,MAAM,MAAM,IAAI;AACtB,YAAM,UAAyB,CAAC;AAChC,iBAAW,UAAU,SAAS,SAAS;AACrC,cAAM,QAAqB,EAAE,OAAO,MAAM,OAAO,SAAS;AAC1D,YAAI,aAAa,MAAO,OAAM,QAAQ,aAAa;AACnD,cAAM,UAAU,MAAM,OAAO,MAAM,KAAK;AACxC,cAAM,SAAS,OAAO,UAClB,QAAQ,OAAO,CAAC,MAAM,OAAO,QAAS,SAAS,EAAE,MAAM,CAAC,IACxD;AACJ,cAAM,OAAO,eAAe,MAAM;AAClC,gBAAQ,KAAK;AAAA,UACX,QAAQ,OAAO;AAAA,UACf,UAAU,OAAO;AAAA,UACjB,KAAK,OAAO;AAAA,UACZ;AAAA,UACA,WAAW,gBAAY,uBAAS,OAAO,KAAK,IAAI,CAAC;AAAA,UACjD,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,QACtD,CAAC;AAAA,MACH;AAEA,YAAM,QAAe,EAAE,eAAe,SAAS,SAAS,QAAQ;AAEhE,UAAI,SAAS,UAAU;AACrB,cAAM,UAAU,MAAM,OAAO,MAAM,EAAE,OAAO,MAAM,SAAS,SAAS,SAAS,CAAC;AAC9E,cAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,cAAc,EAAE,YAAY,OAAO,EAAE;AACrF,cAAM,WAAW;AAAA,UACf,KAAK,SAAS,SAAS;AAAA,UACvB,QAAQ,aAAa,SAAS,SAAS,QAAQ;AAAA,UAC/C;AAAA,UACA,WAAW,KAAK,IAAI,GAAG,SAAS,SAAS,MAAM,IAAI;AAAA,QACrD;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,QAAQ,OAAO;AACb,aAAO,OAAO,MAAM,KAAK;AAAA,IAC3B;AAAA,IAEA,OAAO,MAAM;AACX,iBAAW,cAAc,IAAI;AAC7B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,GAAiB;AACpC,SAAO,EAAE,QAAQ,SAAK,wBAAU,IAAI,EAAE,KAAK,IAAI;AACjD;AAEA,SAAS,aAAa,IAAoB;AACxC,QAAM,QAAQ,KAAK;AACnB,MAAI,OAAO,UAAU,KAAK,KAAK,SAAS,EAAG,QAAO,GAAG,KAAK;AAC1D,QAAM,UAAU,KAAK;AACrB,MAAI,OAAO,UAAU,OAAO,KAAK,WAAW,EAAG,QAAO,GAAG,OAAO;AAChE,SAAO,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AACjC;AAGA,eAAsB,WACpB,QACA,OACA,QAAoC,CAAC,GACrB;AAChB,QAAM,UAAU,MAAM,OAAO,MAAM,EAAE,GAAG,OAAO,MAAM,CAAC;AACtD,SAAO,eAAe,OAAO;AAC/B;;;AKjXA,IAAAC,eAA4B;AAOrB,IAAM,iBAA2B;AAAA,EACtC,MAAM,UAAU;AACd,WAAO;AAAA,EACT;AACF;AAGO,SAAS,aAAa,SAAkB,KAAK,QAAkB;AACpE,SAAO;AAAA,IACL,MAAM,QAAQ,UAAU;AACtB,aAAO,EAAE,SAAS,IAAI,IAAI,SAAS,GAAG;AAAA,IACxC;AAAA,EACF;AACF;AAGO,SAAS,gBAAgB,MAAiC,QAAQ,KAAe;AACtF,SAAO;AAAA,IACL,MAAM,QAAQ,UAAU;AACtB;AAAA,QACE,4BAA4B,SAAS,OAAO,MAAM,QAAI,0BAAY,SAAS,OAAO,GAAG,CAAC,GACjF,SAAS,OAAO,KAAK,OAAO,SAAS,OAAO,EAAE,KAAK,EAAE,KACnD,SAAS,MAAM,sBAAsB,SAAS,UAAU;AAAA,MACjE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAOO,SAAS,gBACd,KACA,UAAsE,CAAC,GAC7D;AACV,QAAM,UAAU,QAAQ,SAAS,WAAW;AAC5C,SAAO;AAAA,IACL,MAAM,QAAQ,UAAgD;AAC5D,YAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,QAAQ,QAAQ;AAAA,QAClE,MAAM,KAAK,UAAU,UAAU,QAAQ,CAAC;AAAA,MAC1C,CAAC;AACD,UAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,YAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AAKpD,UAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,UAAW,QAAO;AAEvD,aAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,GAAI,KAAK,KAAK,EAAE,IAAI,KAAK,GAAG,IAAI,CAAC;AAAA,QACjC,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,QACvC,IAAI,SAAS;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,UAAoB;AACrC,SAAO;AAAA,IACL,YAAY,SAAS;AAAA,IACrB,YAAY,SAAS;AAAA,IACrB,eAAe,SAAS;AAAA,IACxB,QAAQ,SAAS;AAAA,IACjB,MAAM,SAAS;AAAA,IACf,OAAO,SAAS,OAAO;AAAA,IACvB,QAAQ,SAAS,OAAO;AAAA,IACxB,IAAI,SAAS,OAAO;AAAA,IACpB,MAAM,SAAS,OAAO;AAAA,IACtB,SAAK,0BAAY,SAAS,OAAO,GAAG;AAAA,IACpC,YAAQ,0BAAY,SAAS,OAAO,MAAM;AAAA,IAC1C,IAAI,SAAS;AAAA,EACf;AACF;;;ACvFA,IAAAC,eAMO;AAwCP,eAAsB,SACpB,QACA,QACA,UAAsD,CAAC,GAC5B;AAC3B,QAAM,YAAQ,0BAAY,QAAQ,WAAW,OAAO,CAAC,GAAG,MAAM,CAAC;AAC/D,QAAM,QAAQ,YAAY,QAAQ;AAAA,IAChC,QAAQ,aAAa;AAAA,IACrB;AAAA,IACA,QAAQ,QAAQ,UAAU;AAAA,EAC5B,CAAC;AAED,QAAM,UAA8B,CAAC;AACrC,QAAM,SAAkC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,EAAE;AACtE,MAAI,eAAe;AACnB,MAAI,eAAe;AACnB,MAAI,YAAY;AAEhB,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,OAAO,UAAa,MAAM,KAAK,MAAM,IAAI,GAAG;AACpD,YAAM,MAAM,QAAQ,MAAM,KAAK,MAAM,IAAI,CAAC;AAAA,IAC5C;AAEA,UAAM,WAAW,MAAM,MAAM,MAAM,KAAK;AACxC,WAAO,SAAS,OAAO,KAAK;AAE5B,UAAM,QAAQ,SAAS,OAAO,IAAI;AAClC,QAAI,SAAS,YAAY,SAAS;AAChC,sBAAgB;AAChB,UAAI,MAAM,YAAY,MAAO,OAAM,SAAS,OAAO;AAAA,IACrD,WAAW,SAAS,YAAY,QAAQ;AACtC,mBAAa;AAAA,IACf,OAAO;AACL,sBAAgB;AAAA,IAClB;AAEA,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,SAAS,SAAS;AAAA,MAClB,QAAQ,SAAS;AAAA,MACjB,MAAM,SAAS;AAAA,MACf,KAAK,SAAS,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,eAAe,MAAM,OAAO;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,gBAAY,wBAAU,cAAc,KAAK;AAAA,IACzC,gBAAY,wBAAU,cAAc,KAAK;AAAA,IACzC,aAAS,wBAAU,WAAW,KAAK;AAAA,IACnC,SAAS,MAAM,MAAM,QAAQ;AAAA,EAC/B;AACF;AAGO,SAAS,iBAAiB,SAAoD;AACnF,SAAO,QAAQ,IAAI,CAAC,WAAW;AAAA,IAC7B,QAAQ,MAAM;AAAA,IACd,QAAQ,GAAG,MAAM,MAAM,IAAI,MAAM,KAAK;AAAA,IACtC,GAAI,MAAM,eAAe,EAAE,IAAI,MAAM,aAAa,IAAI,CAAC;AAAA,IACvD,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,IAAI,MAAM;AAAA,IACV,SAAS,MAAM,WAAW;AAAA,EAC5B,EAAE;AACJ;","names":["import_core","id","import_core","import_core","import_core","import_core"]}

@@ -632,3 +632,7 @@ // src/guard.ts

// src/simulate.ts
import { fromUnits as fromUnits4, manualClock, peggedPrices as peggedPrices2 } from "@moneolabs/core";
import {
fromUnits as fromUnits4,
manualClock,
peggedPrices as peggedPrices2
} from "@moneolabs/core";
async function simulate(policy, events, options = {}) {

@@ -635,0 +639,0 @@ const clock = manualClock(options.startAt ?? events[0]?.at ?? 0);

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/guard.ts","../src/ledger.ts","../src/policy.ts","../src/rules.ts","../src/match.ts","../src/approvers.ts","../src/simulate.ts"],"sourcesContent":["import {\n convertPrecision,\n fromUnits,\n id,\n MoneoError,\n parseDuration,\n parseMoney,\n peggedPrices,\n subMoney,\n systemClock,\n toDecimalString,\n valueInUsd,\n type Clock,\n type DurationInput,\n type Money,\n type MoneyInput,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport {\n committedTotal,\n memoryLedger,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n} from \"./ledger.js\";\nimport { compilePolicy, type CompiledPolicy, type Policy } from \"./policy.js\";\nimport { evaluate } from \"./rules.js\";\nimport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n\nexport interface GuardOptions {\n /** Where verdicts are written. Defaults to an in-memory ledger. */\n ledger?: DecisionLedger;\n /** Injected so tests do not have to wait out a rolling window. */\n clock?: Clock;\n /** How non-dollar assets are valued. Defaults to pegged assets only. */\n prices?: PriceSource;\n /** Where holds go for a human answer. */\n approver?: Approver;\n /** Recorded on every decision that does not name its own agent. */\n agent?: string;\n}\n\nexport interface BudgetUsage {\n window: string;\n windowMs: number;\n max: Money;\n used: Money;\n remaining: Money;\n actions?: readonly ActionKind[];\n}\n\nexport interface Usage {\n policyVersion: string;\n budgets: BudgetUsage[];\n velocity?: { max: number; window: string; used: number; remaining: number };\n}\n\n/** Maps the arguments of a wrapped function onto an intent. */\nexport interface WrapMapping<A extends unknown[]> {\n action: ActionKind;\n amount: (...args: A) => MoneyInput;\n to?: (...args: A) => string | undefined;\n agent?: (...args: A) => string | undefined;\n memo?: (...args: A) => string | undefined;\n metadata?: (...args: A) => Record<string, unknown> | undefined;\n}\n\n/** Thrown by a wrapped function when policy refuses the call. */\nexport class PolicyDeniedError extends MoneoError {\n readonly decision: Decision;\n constructor(decision: Decision) {\n super(\"policy_denied\", decision.reason, {\n rule: decision.rule,\n policyVersion: decision.policyVersion,\n });\n this.name = \"PolicyDeniedError\";\n this.decision = decision;\n }\n}\n\nexport interface Guard {\n readonly policy: CompiledPolicy;\n readonly ledger: DecisionLedger;\n\n /** Evaluate an intent. Nothing is signed and nothing is charged. */\n check(intent: Intent): Promise<Decision>;\n /** Put an existing spending function behind this policy. */\n wrap<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n mapping: WrapMapping<A>,\n ): (...args: A) => Promise<R>;\n /** Answer a held decision. */\n resolve(approvalId: string, outcome: Omit<ApprovalOutcome, \"at\"> & { at?: number }): void;\n /** What is left of each budget right now. */\n usage(options?: { agent?: string }): Promise<Usage>;\n /** Read verdicts back, blocks included. */\n history(query?: LedgerQuery): Promise<LedgerEntry[]>;\n /** Swap the policy. Later decisions record the new version. */\n update(policy: Policy): CompiledPolicy;\n}\n\nexport function createGuard(policy: Policy, options: GuardOptions = {}): Guard {\n let compiled = compilePolicy(policy);\n const ledger = options.ledger ?? memoryLedger();\n const clock = options.clock ?? systemClock;\n const prices = options.prices ?? peggedPrices;\n const approver = options.approver;\n const defaultAgent = options.agent;\n\n /**\n * Held decisions, keyed by approval id. An entry survives being resolved so\n * that `wait()` still returns the answer when the approver replied before\n * anyone started waiting. Entries are dropped once the decision settles or is\n * released, which is the point at which nothing can ask again.\n */\n const pending = new Map<\n string,\n { resolve: (outcome: ApprovalOutcome) => void; promise: Promise<ApprovalOutcome> }\n >();\n\n async function resolveIntent(intent: Intent): Promise<ResolvedIntent> {\n const amount = parseMoney(intent.amount, \"USD\");\n const usd = await valueInUsd(amount, prices);\n return {\n ...intent,\n ...((intent.agent ?? defaultAgent) ? { agent: intent.agent ?? defaultAgent } : {}),\n amount,\n usd: convertPrecision(usd, \"USD\"),\n };\n }\n\n function longestWindowMs(): number {\n const windows = [...compiled.budgets.map((b) => b.windowMs), compiled.velocity?.windowMs ?? 0];\n return windows.length > 0 ? Math.max(...windows) : 0;\n }\n\n function buildDecision(\n entry: LedgerEntry,\n intent: ResolvedIntent,\n approvalId: string | undefined,\n ): Decision {\n const decision: Decision = {\n id: entry.id,\n verdict: entry.verdict,\n reason: entry.reason,\n rule: entry.rule,\n policyVersion: entry.policyVersion,\n intent,\n at: entry.at,\n ...(approvalId ? { approvalId } : {}),\n\n async settle(actual?: MoneyInput) {\n if (entry.verdict === \"block\") {\n throw new MoneoError(\n \"settle_blocked\",\n \"a blocked decision cannot settle: nothing was signed\",\n { id: entry.id },\n );\n }\n const patch: Parameters<DecisionLedger[\"update\"]>[1] = { status: \"settled\" };\n if (actual !== undefined) {\n const settledAmount = parseMoney(actual, intent.amount.asset);\n const settledUsd = convertPrecision(await valueInUsd(settledAmount, prices), \"USD\");\n patch.amount = toDecimalString(settledAmount);\n patch.usdCents = settledUsd.units;\n }\n await ledger.update(entry.id, patch);\n if (approvalId) pending.delete(approvalId);\n },\n\n async release() {\n await ledger.update(entry.id, { status: \"released\" });\n if (approvalId) pending.delete(approvalId);\n },\n\n async wait(waitOptions = {}) {\n if (entry.verdict !== \"hold\") {\n return { granted: entry.verdict === \"allow\", at: clock.now() };\n }\n const slot = approvalId ? pending.get(approvalId) : undefined;\n if (!slot) return { granted: false, at: clock.now() };\n if (waitOptions.timeout === undefined) return slot.promise;\n\n const ms = parseDuration(waitOptions.timeout as DurationInput);\n const timeout = clock.sleep(ms).then(() => {\n throw new MoneoError(\"approval_timeout\", `no answer within ${waitOptions.timeout}`, {\n approvalId,\n });\n });\n return Promise.race([slot.promise, timeout]);\n },\n };\n return decision;\n }\n\n const guard: Guard = {\n get policy() {\n return compiled;\n },\n ledger,\n\n async check(intent) {\n const resolved = await resolveIntent(intent);\n const now = clock.now();\n const window = longestWindowMs();\n const history = window > 0 ? await ledger.query({ since: now - window }) : [];\n\n const outcome = evaluate({ intent: resolved, policy: compiled, now, history });\n const verdict: Verdict = outcome?.verdict ?? \"allow\";\n\n const entry: LedgerEntry = {\n id: id(\"dec\"),\n at: now,\n ...(resolved.agent ? { agent: resolved.agent } : {}),\n action: resolved.action,\n ...(resolved.to ? { counterparty: resolved.to } : {}),\n asset: resolved.amount.asset,\n amount: toDecimalString(resolved.amount),\n usdCents: resolved.usd.units,\n verdict,\n reason: outcome?.reason ?? \"within policy\",\n rule: outcome?.rule ?? \"default\",\n policyVersion: compiled.version,\n status: verdict === \"block\" ? \"released\" : \"reserved\",\n ...(resolved.memo ? { memo: resolved.memo } : {}),\n ...(resolved.metadata ? { metadata: resolved.metadata } : {}),\n };\n await ledger.append(entry);\n\n let approvalId: string | undefined;\n if (verdict === \"hold\") {\n approvalId = id(\"apr\");\n let settle!: (outcome: ApprovalOutcome) => void;\n const promise = new Promise<ApprovalOutcome>((res) => {\n settle = res;\n });\n pending.set(approvalId, { resolve: settle, promise });\n }\n\n const decision = buildDecision(entry, resolved, approvalId);\n\n if (verdict === \"hold\" && approver && approvalId) {\n const answered = await approver.request(decision);\n if (answered) guard.resolve(approvalId, answered);\n }\n\n return decision;\n },\n\n wrap(fn, mapping) {\n return async (...args) => {\n const intent: Intent = {\n action: mapping.action,\n amount: mapping.amount(...args),\n };\n const to = mapping.to?.(...args);\n if (to !== undefined) intent.to = to;\n const agent = mapping.agent?.(...args);\n if (agent !== undefined) intent.agent = agent;\n const memo = mapping.memo?.(...args);\n if (memo !== undefined) intent.memo = memo;\n const metadata = mapping.metadata?.(...args);\n if (metadata !== undefined) intent.metadata = metadata;\n\n const decision = await guard.check(intent);\n if (decision.verdict === \"block\") throw new PolicyDeniedError(decision);\n if (decision.verdict === \"hold\") {\n const outcome = await decision.wait();\n if (!outcome.granted) throw new PolicyDeniedError(decision);\n }\n\n try {\n const result = await fn(...args);\n await decision.settle();\n return result;\n } catch (error) {\n // The call failed, so the money did not move. Give the budget back.\n await decision.release();\n throw error;\n }\n };\n },\n\n resolve(approvalId, outcome) {\n const slot = pending.get(approvalId);\n if (!slot) return;\n // Deliberately kept in the map: settle() and release() clean it up, so an\n // answer that arrives before anyone waits is not lost.\n slot.resolve({ ...outcome, at: outcome.at ?? clock.now() });\n },\n\n async usage(usageOptions = {}) {\n const now = clock.now();\n const budgets: BudgetUsage[] = [];\n for (const budget of compiled.budgets) {\n const query: LedgerQuery = { since: now - budget.windowMs };\n if (usageOptions.agent) query.agent = usageOptions.agent;\n const entries = await ledger.query(query);\n const scoped = budget.actions\n ? entries.filter((e) => budget.actions!.includes(e.action))\n : entries;\n const used = committedTotal(scoped);\n budgets.push({\n window: budget.label,\n windowMs: budget.windowMs,\n max: budget.max,\n used,\n remaining: clampToZero(subMoney(budget.max, used)),\n ...(budget.actions ? { actions: budget.actions } : {}),\n });\n }\n\n const usage: Usage = { policyVersion: compiled.version, budgets };\n\n if (compiled.velocity) {\n const entries = await ledger.query({ since: now - compiled.velocity.windowMs });\n const used = entries.filter((e) => e.status !== \"released\" && e.verdict !== \"block\").length;\n usage.velocity = {\n max: compiled.velocity.max,\n window: formatWindow(compiled.velocity.windowMs),\n used,\n remaining: Math.max(0, compiled.velocity.max - used),\n };\n }\n\n return usage;\n },\n\n history(query) {\n return ledger.query(query);\n },\n\n update(next) {\n compiled = compilePolicy(next);\n return compiled;\n },\n };\n\n return guard;\n}\n\nfunction clampToZero(m: Money): Money {\n return m.units < 0n ? fromUnits(0n, m.asset) : m;\n}\n\nfunction formatWindow(ms: number): string {\n const hours = ms / 3_600_000;\n if (Number.isInteger(hours) && hours >= 1) return `${hours}h`;\n const minutes = ms / 60_000;\n if (Number.isInteger(minutes) && minutes >= 1) return `${minutes}m`;\n return `${Math.round(ms / 1000)}s`;\n}\n\n/** Convenience for reading a running total straight out of a ledger. */\nexport async function spentSince(\n ledger: DecisionLedger,\n since: number,\n query: Omit<LedgerQuery, \"since\"> = {},\n): Promise<Money> {\n const entries = await ledger.query({ ...query, since });\n return committedTotal(entries);\n}\n","import { fromUnits, type Money } from \"@moneolabs/core\";\nimport type { ActionKind, Verdict } from \"./types.js\";\n\n/**\n * `reserved` means the guard said yes and the money has not been confirmed\n * moved yet. It still counts against budgets, because a payment in flight is\n * money you no longer have. `released` means it never happened.\n */\nexport type EntryStatus = \"reserved\" | \"settled\" | \"released\";\n\nexport interface LedgerEntry {\n id: string;\n at: number;\n agent?: string;\n action: ActionKind;\n counterparty?: string;\n asset: string;\n /** Amount in the asset's own minor units, as a decimal string. */\n amount: string;\n /** USD value in cents at the time of the decision. */\n usdCents: bigint;\n verdict: Verdict;\n reason: string;\n rule: string;\n policyVersion: string;\n status: EntryStatus;\n memo?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LedgerQuery {\n since?: number;\n until?: number;\n agent?: string;\n action?: ActionKind;\n counterparty?: string;\n verdict?: Verdict;\n status?: EntryStatus;\n limit?: number;\n}\n\n/**\n * Where verdicts are written and where budgets are read back from. The default\n * implementation keeps everything in memory. Swap it for Postgres, SQLite, or\n * whatever already holds your financial records.\n */\nexport interface DecisionLedger {\n append(entry: LedgerEntry): Promise<void>;\n update(\n id: string,\n patch: Partial<Pick<LedgerEntry, \"status\" | \"amount\" | \"usdCents\">>,\n ): Promise<void>;\n query(query?: LedgerQuery): Promise<LedgerEntry[]>;\n}\n\n/**\n * Whether an entry consumes budget and velocity.\n *\n * An allowed movement counts from the moment it is reserved, because money in\n * flight is money you no longer have. A held movement counts only once it has\n * settled, since a pending approval may never be granted. Blocks and releases\n * never count, which is what makes a refusal free.\n */\nexport function countsTowardLimits(entry: LedgerEntry): boolean {\n if (entry.status === \"released\") return false;\n if (entry.verdict === \"block\") return false;\n if (entry.verdict === \"hold\") return entry.status === \"settled\";\n return true;\n}\n\n/** Total USD counted against budgets. */\nexport function committedTotal(entries: readonly LedgerEntry[]): Money {\n let cents = 0n;\n for (const entry of entries) {\n if (countsTowardLimits(entry)) cents += entry.usdCents;\n }\n return fromUnits(cents, \"USD\");\n}\n\nexport function memoryLedger(seed: readonly LedgerEntry[] = []): DecisionLedger {\n const entries: LedgerEntry[] = [...seed];\n const index = new Map(entries.map((e) => [e.id, e]));\n\n return {\n async append(entry) {\n entries.push(entry);\n index.set(entry.id, entry);\n },\n async update(id, patch) {\n const entry = index.get(id);\n if (!entry) return;\n Object.assign(entry, patch);\n },\n async query(query = {}) {\n let out = entries;\n if (query.since !== undefined) out = out.filter((e) => e.at >= query.since!);\n if (query.until !== undefined) out = out.filter((e) => e.at <= query.until!);\n if (query.agent) out = out.filter((e) => e.agent === query.agent);\n if (query.action) out = out.filter((e) => e.action === query.action);\n if (query.counterparty) out = out.filter((e) => e.counterparty === query.counterparty);\n if (query.verdict) out = out.filter((e) => e.verdict === query.verdict);\n if (query.status) out = out.filter((e) => e.status === query.status);\n out = [...out].sort((a, b) => a.at - b.at);\n if (query.limit !== undefined) out = out.slice(-query.limit);\n return out;\n },\n };\n}\n","import {\n fingerprint,\n formatDuration,\n formatMoney,\n isPositiveMoney,\n parseDuration,\n parseMoney,\n toDecimalString,\n ValidationError,\n type DurationInput,\n type Money,\n type MoneyInput,\n} from \"@moneolabs/core\";\nimport type { ActionKind } from \"./types.js\";\n\n/** A rolling window and the most that may move through it. */\nexport interface BudgetRule {\n window: DurationInput;\n max: MoneyInput;\n /** Restrict this budget to certain actions. Omit to cover everything. */\n actions?: ActionKind[];\n}\n\nexport interface VelocityRule {\n /** Most decisions allowed inside the window. */\n max: number;\n per: DurationInput;\n /** Count only movements to the same counterparty. */\n perCounterparty?: boolean;\n}\n\nexport interface Policy {\n /** Ceiling on any single movement, in USD. */\n perTransaction?: { max: MoneyInput };\n /** Shorthand for a single 24 hour rolling budget. */\n rolling24h?: { max: MoneyInput };\n /** Any number of rolling windows, evaluated together. */\n budgets?: BudgetRule[];\n /** Rate limit on money, not on requests. */\n velocity?: VelocityRule;\n /**\n * \"allowlist-only\" blocks anything not matched by `allow`, and blocks\n * movements that name no counterparty at all. \"open\" runs `deny` only.\n */\n counterparties?: \"allowlist-only\" | \"open\";\n /** Patterns that may receive funds. `*` matches any run of characters. */\n allow?: string[];\n /** Patterns that may never receive funds. Checked before everything else. */\n deny?: string[];\n /** Assets this agent may move. Omit to allow all. */\n assets?: { allow?: string[]; deny?: string[] };\n /** Action kinds this agent may perform. Omit to allow all. */\n actions?: { allow?: ActionKind[]; deny?: ActionKind[] };\n /** Above this USD figure, a human has to say yes. */\n escalate?: { above: MoneyInput };\n /** Carried onto every decision. Useful for naming a policy in the ledger. */\n label?: string;\n}\n\nexport interface CompiledBudget {\n /**\n * How the window was written, for example \"24h\". Rule names and refusal\n * messages quote this back, so an author reading a blocked decision sees the\n * same words they put in the policy.\n */\n readonly label: string;\n readonly windowMs: number;\n readonly max: Money;\n readonly actions?: readonly ActionKind[];\n}\n\n/** A policy with every amount and duration resolved, plus a stable version. */\nexport interface CompiledPolicy {\n readonly version: string;\n readonly label?: string;\n readonly perTransactionMax?: Money;\n readonly budgets: readonly CompiledBudget[];\n readonly velocity?: { max: number; windowMs: number; perCounterparty: boolean };\n readonly counterparties: \"allowlist-only\" | \"open\";\n readonly allow: readonly string[];\n readonly deny: readonly string[];\n readonly assetsAllow?: readonly string[];\n readonly assetsDeny: readonly string[];\n readonly actionsAllow?: readonly ActionKind[];\n readonly actionsDeny: readonly ActionKind[];\n readonly escalateAbove?: Money;\n readonly source: Policy;\n}\n\n/**\n * Resolve a policy once, up front. Every amount is parsed, every window is\n * converted to milliseconds, and the result is fingerprinted so that each\n * verdict can name the exact policy that produced it.\n */\nexport function compilePolicy(policy: Policy): CompiledPolicy {\n const perTransactionMax = policy.perTransaction\n ? requirePositiveUsd(policy.perTransaction.max, \"perTransaction.max\")\n : undefined;\n\n const budgets: CompiledBudget[] = [];\n if (policy.rolling24h) {\n budgets.push({\n label: \"24h\",\n windowMs: parseDuration(\"24h\"),\n max: requirePositiveUsd(policy.rolling24h.max, \"rolling24h.max\"),\n });\n }\n for (const [index, budget] of (policy.budgets ?? []).entries()) {\n const windowMs = parseDuration(budget.window);\n if (windowMs <= 0) {\n throw new ValidationError(`budgets[${index}].window must be longer than zero`, { budget });\n }\n budgets.push({\n label: typeof budget.window === \"string\" ? budget.window.trim() : formatDuration(windowMs),\n windowMs,\n max: requirePositiveUsd(budget.max, `budgets[${index}].max`),\n ...(budget.actions ? { actions: [...budget.actions] } : {}),\n });\n }\n\n let velocity: CompiledPolicy[\"velocity\"];\n if (policy.velocity) {\n const windowMs = parseDuration(policy.velocity.per);\n if (!Number.isInteger(policy.velocity.max) || policy.velocity.max < 1) {\n throw new ValidationError(\"velocity.max must be a whole number of one or more\", {\n velocity: policy.velocity,\n });\n }\n if (windowMs <= 0) {\n throw new ValidationError(\"velocity.per must be longer than zero\", {\n velocity: policy.velocity,\n });\n }\n velocity = {\n max: policy.velocity.max,\n windowMs,\n perCounterparty: policy.velocity.perCounterparty ?? false,\n };\n }\n\n const counterparties = policy.counterparties ?? \"open\";\n if (counterparties === \"allowlist-only\" && (policy.allow ?? []).length === 0) {\n throw new ValidationError(\n \"counterparties is allowlist-only but allow is empty, which blocks every movement\",\n );\n }\n\n const escalateAbove = policy.escalate\n ? requirePositiveUsd(policy.escalate.above, \"escalate.above\")\n : undefined;\n\n const compiled: Omit<CompiledPolicy, \"version\"> = {\n ...(policy.label ? { label: policy.label } : {}),\n ...(perTransactionMax ? { perTransactionMax } : {}),\n budgets,\n ...(velocity ? { velocity } : {}),\n counterparties,\n allow: [...(policy.allow ?? [])],\n deny: [...(policy.deny ?? [])],\n ...(policy.assets?.allow ? { assetsAllow: policy.assets.allow.map(upper) } : {}),\n assetsDeny: (policy.assets?.deny ?? []).map(upper),\n ...(policy.actions?.allow ? { actionsAllow: [...policy.actions.allow] } : {}),\n actionsDeny: [...(policy.actions?.deny ?? [])],\n ...(escalateAbove ? { escalateAbove } : {}),\n source: policy,\n };\n\n return { ...compiled, version: versionOf(compiled) };\n}\n\n/**\n * A deterministic id for a policy. Two policies that differ only in key order\n * or in how an amount was written produce the same version, which is what makes\n * \"which policy blocked this\" answerable months later.\n */\nexport function versionOf(compiled: Omit<CompiledPolicy, \"version\">): string {\n const shape = {\n label: compiled.label,\n perTransactionMax: compiled.perTransactionMax && describe(compiled.perTransactionMax),\n // The budget's own label is left out: \"24h\" and 86400000 are the same rule.\n budgets: compiled.budgets.map((b) => ({\n windowMs: b.windowMs,\n max: describe(b.max),\n actions: b.actions ? [...b.actions].sort() : undefined,\n })),\n velocity: compiled.velocity,\n counterparties: compiled.counterparties,\n allow: [...compiled.allow].sort(),\n deny: [...compiled.deny].sort(),\n assetsAllow: compiled.assetsAllow ? [...compiled.assetsAllow].sort() : undefined,\n assetsDeny: [...compiled.assetsDeny].sort(),\n actionsAllow: compiled.actionsAllow ? [...compiled.actionsAllow].sort() : undefined,\n actionsDeny: [...compiled.actionsDeny].sort(),\n escalateAbove: compiled.escalateAbove && describe(compiled.escalateAbove),\n };\n return `pol_${fingerprint(shape)}`;\n}\n\nfunction describe(m: Money): string {\n return `${toDecimalString(m)} ${m.asset}`;\n}\n\nfunction upper(value: string): string {\n return value.toUpperCase();\n}\n\nfunction requirePositiveUsd(input: MoneyInput, field: string): Money {\n const amount = parseMoney(input, \"USD\");\n if (amount.asset !== \"USD\") {\n throw new ValidationError(`${field} must be in USD, got ${formatMoney(amount)}`, { field });\n }\n if (!isPositiveMoney(amount)) {\n throw new ValidationError(`${field} must be greater than zero`, { field });\n }\n return amount;\n}\n","import {\n addMoney,\n formatDuration,\n formatMoney,\n fromUnits,\n gtMoney,\n subMoney,\n type Money,\n} from \"@moneolabs/core\";\nimport { matchesAny } from \"./match.js\";\nimport type { CompiledPolicy } from \"./policy.js\";\nimport { committedTotal, countsTowardLimits, type LedgerEntry } from \"./ledger.js\";\nimport type { ResolvedIntent } from \"./types.js\";\n\nexport interface RuleContext {\n readonly intent: ResolvedIntent;\n readonly policy: CompiledPolicy;\n readonly now: number;\n /** Everything on the ledger inside the longest window the policy cares about. */\n readonly history: readonly LedgerEntry[];\n}\n\nexport interface RuleOutcome {\n verdict: \"hold\" | \"block\";\n rule: string;\n reason: string;\n}\n\ntype Rule = (context: RuleContext) => RuleOutcome | undefined;\n\n/**\n * Order is deliberate. Denylist first so a banned counterparty is never\n * described as merely over budget, and escalation last so a held decision is\n * one that passed every hard limit.\n */\nconst RULES: Rule[] = [\n denyList,\n assetRules,\n actionRules,\n allowList,\n perTransactionCap,\n velocityLimit,\n rollingBudgets,\n escalation,\n];\n\nexport function evaluate(context: RuleContext): RuleOutcome | undefined {\n let held: RuleOutcome | undefined;\n for (const rule of RULES) {\n const outcome = rule(context);\n if (!outcome) continue;\n if (outcome.verdict === \"block\") return outcome;\n held ??= outcome;\n }\n return held;\n}\n\n/* -------------------------------------------------------------------------- */\n\nfunction denyList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (!intent.to || policy.deny.length === 0) return undefined;\n const pattern = matchesAny(intent.to, policy.deny);\n if (!pattern) return undefined;\n return {\n verdict: \"block\",\n rule: \"denylist\",\n reason: `counterparty ${intent.to} is on the denylist (matched \"${pattern}\")`,\n };\n}\n\nfunction assetRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const asset = intent.amount.asset;\n if (policy.assetsDeny.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.deny\",\n reason: `this agent may not move ${asset}`,\n };\n }\n if (policy.assetsAllow && !policy.assetsAllow.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.allow\",\n reason: `this agent may only move ${policy.assetsAllow.join(\", \")}, not ${asset}`,\n };\n }\n return undefined;\n}\n\nfunction actionRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.actionsDeny.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.deny\",\n reason: `this agent may not perform \"${intent.action}\"`,\n };\n }\n if (policy.actionsAllow && !policy.actionsAllow.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.allow\",\n reason: `this agent may only perform ${policy.actionsAllow.join(\", \")}, not \"${intent.action}\"`,\n };\n }\n return undefined;\n}\n\nfunction allowList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.counterparties !== \"allowlist-only\") return undefined;\n\n // An intent with no counterparty must not be a way around the allowlist.\n if (!intent.to) {\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: \"policy is allowlist-only and this movement names no counterparty\",\n };\n }\n if (matchesAny(intent.to, policy.allow)) return undefined;\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: `counterparty ${intent.to} is not on the allowlist`,\n };\n}\n\nfunction perTransactionCap({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const max = policy.perTransactionMax;\n if (!max || !gtMoney(intent.usd, max)) return undefined;\n return {\n verdict: \"block\",\n rule: \"perTransaction\",\n reason: `${formatMoney(intent.usd)} exceeds the ${formatMoney(max)} per-transaction limit`,\n };\n}\n\nfunction velocityLimit({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n const velocity = policy.velocity;\n if (!velocity) return undefined;\n\n const since = now - velocity.windowMs;\n const recent = history.filter(\n (entry) =>\n entry.at > since &&\n countsTowardLimits(entry) &&\n (!velocity.perCounterparty || entry.counterparty === intent.to),\n );\n\n if (recent.length < velocity.max) return undefined;\n\n const scope = velocity.perCounterparty ? ` to ${intent.to}` : \"\";\n return {\n verdict: \"block\",\n rule: \"velocity\",\n reason:\n `${recent.length} movements${scope} in the last ${formatDuration(velocity.windowMs)} ` +\n `already meets the limit of ${velocity.max}`,\n };\n}\n\nfunction rollingBudgets({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n for (const budget of policy.budgets) {\n if (budget.actions && !budget.actions.includes(intent.action)) continue;\n\n const since = now - budget.windowMs;\n const window = history.filter(\n (entry) => entry.at > since && (!budget.actions || budget.actions.includes(entry.action)),\n );\n const used = committedTotal(window);\n const projected = addMoney(used, intent.usd);\n if (!gtMoney(projected, budget.max)) continue;\n\n const remaining = remainingOrZero(budget.max, used);\n return {\n verdict: \"block\",\n rule: `budget.${budget.label}`,\n reason:\n `${formatMoney(intent.usd)} exceeds the rolling ${budget.label} budget: ` +\n `${formatMoney(budget.max)} cap, ${formatMoney(used)} used, ` +\n `${formatMoney(remaining)} left`,\n };\n }\n return undefined;\n}\n\nfunction escalation({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const above = policy.escalateAbove;\n if (!above || !gtMoney(intent.usd, above)) return undefined;\n return {\n verdict: \"hold\",\n rule: \"escalate\",\n reason: `${formatMoney(intent.usd)} is above the ${formatMoney(above)} approval threshold`,\n };\n}\n\nfunction remainingOrZero(max: Money, used: Money): Money {\n const left = subMoney(max, used);\n return left.units < 0n ? fromUnits(0n, left.asset) : left;\n}\n","/**\n * Counterparty patterns. `*` matches any run of characters, everything else is\n * literal, and matching is case insensitive because hex addresses arrive in\n * whatever case the caller happened to have.\n *\n * Examples that all match \"x402:api.pricefeed.dev/quote\":\n * \"x402:*\" \"x402:api.pricefeed.dev/*\" \"*pricefeed*\"\n */\nconst cache = new Map<string, RegExp>();\n\nexport function matchesPattern(value: string, pattern: string): boolean {\n return toRegExp(pattern).test(value);\n}\n\nexport function matchesAny(value: string, patterns: readonly string[]): string | undefined {\n for (const pattern of patterns) {\n if (matchesPattern(value, pattern)) return pattern;\n }\n return undefined;\n}\n\nfunction toRegExp(pattern: string): RegExp {\n const hit = cache.get(pattern);\n if (hit) return hit;\n\n const source = `^${pattern.split(\"*\").map(escapeRegExp).join(\".*\")}$`;\n const compiled = new RegExp(source, \"iu\");\n\n // Patterns come from config, not user input, but the cache is still bounded\n // so a generated allowlist cannot grow it without limit.\n if (cache.size > 1000) cache.clear();\n cache.set(pattern, compiled);\n return compiled;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { formatMoney } from \"@moneolabs/core\";\nimport type { ApprovalOutcome, Approver, Decision } from \"./types.js\";\n\n/**\n * Leaves every hold pending until someone calls `guard.resolve()`. This is the\n * behaviour you get when no approver is configured, expressed explicitly.\n */\nexport const manualApprover: Approver = {\n async request() {\n return undefined;\n },\n};\n\n/** Answers every hold the same way. For tests and for local development. */\nexport function autoApprover(granted: boolean, by = \"auto\"): Approver {\n return {\n async request(decision) {\n return { granted, by, at: decision.at };\n },\n };\n}\n\n/** Prints the request and leaves it pending. Handy in a terminal session. */\nexport function loggingApprover(log: (message: string) => void = console.log): Approver {\n return {\n async request(decision) {\n log(\n `[moneo] approval needed: ${decision.intent.action} ${formatMoney(decision.intent.usd)}` +\n `${decision.intent.to ? ` to ${decision.intent.to}` : \"\"}` +\n ` (${decision.reason}). Resolve with id ${decision.approvalId}`,\n );\n return undefined;\n },\n };\n}\n\n/**\n * Posts the request to an HTTP endpoint. Return a JSON body of\n * `{ \"granted\": true }` to answer immediately, or anything else to leave the\n * decision pending for `guard.resolve()`.\n */\nexport function webhookApprover(\n url: string,\n options: { headers?: Record<string, string>; fetch?: typeof fetch } = {},\n): Approver {\n const doFetch = options.fetch ?? globalThis.fetch;\n return {\n async request(decision): Promise<ApprovalOutcome | undefined> {\n const response = await doFetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(summarize(decision)),\n });\n if (!response.ok) return undefined;\n\n const body = (await response.json().catch(() => null)) as {\n granted?: boolean;\n by?: string;\n note?: string;\n } | null;\n if (!body || typeof body.granted !== \"boolean\") return undefined;\n\n return {\n granted: body.granted,\n ...(body.by ? { by: body.by } : {}),\n ...(body.note ? { note: body.note } : {}),\n at: decision.at,\n };\n },\n };\n}\n\nfunction summarize(decision: Decision) {\n return {\n approvalId: decision.approvalId,\n decisionId: decision.id,\n policyVersion: decision.policyVersion,\n reason: decision.reason,\n rule: decision.rule,\n agent: decision.intent.agent,\n action: decision.intent.action,\n to: decision.intent.to,\n memo: decision.intent.memo,\n usd: formatMoney(decision.intent.usd),\n amount: formatMoney(decision.intent.amount),\n at: decision.at,\n };\n}\n","import { fromUnits, manualClock, peggedPrices, type Money, type PriceSource } from \"@moneolabs/core\";\nimport { createGuard } from \"./guard.js\";\nimport { memoryLedger, type LedgerEntry } from \"./ledger.js\";\nimport type { Policy } from \"./policy.js\";\nimport type { Intent, Verdict } from \"./types.js\";\n\nexport interface SimulationEvent extends Intent {\n /** When it happened. Defaults to the previous event's time. */\n at?: number;\n /**\n * Whether the movement actually completed. Reserved-but-never-settled spend\n * still counts against budgets, so replays should say.\n */\n settled?: boolean;\n}\n\nexport interface SimulationResult {\n event: SimulationEvent;\n verdict: Verdict;\n reason: string;\n rule: string;\n usd: Money;\n}\n\nexport interface SimulationReport {\n policyVersion: string;\n results: SimulationResult[];\n counts: Record<Verdict, number>;\n allowedUsd: Money;\n blockedUsd: Money;\n heldUsd: Money;\n entries: LedgerEntry[];\n}\n\n/**\n * Replay a list of movements against a policy without touching anything real.\n *\n * This is how you find out that the budget you are about to ship would have\n * blocked a third of last month before you ship it, rather than after.\n */\nexport async function simulate(\n policy: Policy,\n events: readonly SimulationEvent[],\n options: { prices?: PriceSource; startAt?: number } = {},\n): Promise<SimulationReport> {\n const clock = manualClock(options.startAt ?? events[0]?.at ?? 0);\n const guard = createGuard(policy, {\n ledger: memoryLedger(),\n clock,\n prices: options.prices ?? peggedPrices,\n });\n\n const results: SimulationResult[] = [];\n const counts: Record<Verdict, number> = { allow: 0, hold: 0, block: 0 };\n let allowedCents = 0n;\n let blockedCents = 0n;\n let heldCents = 0n;\n\n for (const event of events) {\n if (event.at !== undefined && event.at > clock.now()) {\n await clock.advance(event.at - clock.now());\n }\n\n const decision = await guard.check(event);\n counts[decision.verdict] += 1;\n\n const cents = decision.intent.usd.units;\n if (decision.verdict === \"allow\") {\n allowedCents += cents;\n if (event.settled !== false) await decision.settle();\n } else if (decision.verdict === \"hold\") {\n heldCents += cents;\n } else {\n blockedCents += cents;\n }\n\n results.push({\n event,\n verdict: decision.verdict,\n reason: decision.reason,\n rule: decision.rule,\n usd: decision.intent.usd,\n });\n }\n\n return {\n policyVersion: guard.policy.version,\n results,\n counts,\n allowedUsd: fromUnits(allowedCents, \"USD\"),\n blockedUsd: fromUnits(blockedCents, \"USD\"),\n heldUsd: fromUnits(heldCents, \"USD\"),\n entries: await guard.history(),\n };\n}\n\n/** Turn past ledger entries back into events, so you can replay real history. */\nexport function eventsFromLedger(entries: readonly LedgerEntry[]): SimulationEvent[] {\n return entries.map((entry) => ({\n action: entry.action,\n amount: `${entry.amount} ${entry.asset}`,\n ...(entry.counterparty ? { to: entry.counterparty } : {}),\n ...(entry.agent ? { agent: entry.agent } : {}),\n ...(entry.memo ? { memo: entry.memo } : {}),\n at: entry.at,\n settled: entry.status === \"settled\",\n }));\n}\n"],"mappings":"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","names":["fromUnits","parseDuration","parseMoney","subMoney","toDecimalString","id","formatDuration","formatMoney","fromUnits","formatMoney","formatDuration","fromUnits","parseMoney","toDecimalString","parseDuration","subMoney","fromUnits","formatMoney","fromUnits","peggedPrices","peggedPrices","fromUnits"]}
{"version":3,"sources":["../src/guard.ts","../src/ledger.ts","../src/policy.ts","../src/rules.ts","../src/match.ts","../src/approvers.ts","../src/simulate.ts"],"sourcesContent":["import {\n convertPrecision,\n fromUnits,\n id,\n MoneoError,\n parseDuration,\n parseMoney,\n peggedPrices,\n subMoney,\n systemClock,\n toDecimalString,\n valueInUsd,\n type Clock,\n type DurationInput,\n type Money,\n type MoneyInput,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport {\n committedTotal,\n memoryLedger,\n type DecisionLedger,\n type LedgerEntry,\n type LedgerQuery,\n} from \"./ledger.js\";\nimport { compilePolicy, type CompiledPolicy, type Policy } from \"./policy.js\";\nimport { evaluate } from \"./rules.js\";\nimport type {\n ActionKind,\n ApprovalOutcome,\n Approver,\n Decision,\n Intent,\n ResolvedIntent,\n Verdict,\n} from \"./types.js\";\n\nexport interface GuardOptions {\n /** Where verdicts are written. Defaults to an in-memory ledger. */\n ledger?: DecisionLedger;\n /** Injected so tests do not have to wait out a rolling window. */\n clock?: Clock;\n /** How non-dollar assets are valued. Defaults to pegged assets only. */\n prices?: PriceSource;\n /** Where holds go for a human answer. */\n approver?: Approver;\n /** Recorded on every decision that does not name its own agent. */\n agent?: string;\n}\n\nexport interface BudgetUsage {\n window: string;\n windowMs: number;\n max: Money;\n used: Money;\n remaining: Money;\n actions?: readonly ActionKind[];\n}\n\nexport interface Usage {\n policyVersion: string;\n budgets: BudgetUsage[];\n velocity?: { max: number; window: string; used: number; remaining: number };\n}\n\n/** Maps the arguments of a wrapped function onto an intent. */\nexport interface WrapMapping<A extends unknown[]> {\n action: ActionKind;\n amount: (...args: A) => MoneyInput;\n to?: (...args: A) => string | undefined;\n agent?: (...args: A) => string | undefined;\n memo?: (...args: A) => string | undefined;\n metadata?: (...args: A) => Record<string, unknown> | undefined;\n}\n\n/** Thrown by a wrapped function when policy refuses the call. */\nexport class PolicyDeniedError extends MoneoError {\n readonly decision: Decision;\n constructor(decision: Decision) {\n super(\"policy_denied\", decision.reason, {\n rule: decision.rule,\n policyVersion: decision.policyVersion,\n });\n this.name = \"PolicyDeniedError\";\n this.decision = decision;\n }\n}\n\nexport interface Guard {\n readonly policy: CompiledPolicy;\n readonly ledger: DecisionLedger;\n\n /** Evaluate an intent. Nothing is signed and nothing is charged. */\n check(intent: Intent): Promise<Decision>;\n /** Put an existing spending function behind this policy. */\n wrap<A extends unknown[], R>(\n fn: (...args: A) => Promise<R>,\n mapping: WrapMapping<A>,\n ): (...args: A) => Promise<R>;\n /** Answer a held decision. */\n resolve(approvalId: string, outcome: Omit<ApprovalOutcome, \"at\"> & { at?: number }): void;\n /** What is left of each budget right now. */\n usage(options?: { agent?: string }): Promise<Usage>;\n /** Read verdicts back, blocks included. */\n history(query?: LedgerQuery): Promise<LedgerEntry[]>;\n /** Swap the policy. Later decisions record the new version. */\n update(policy: Policy): CompiledPolicy;\n}\n\nexport function createGuard(policy: Policy, options: GuardOptions = {}): Guard {\n let compiled = compilePolicy(policy);\n const ledger = options.ledger ?? memoryLedger();\n const clock = options.clock ?? systemClock;\n const prices = options.prices ?? peggedPrices;\n const approver = options.approver;\n const defaultAgent = options.agent;\n\n /**\n * Held decisions, keyed by approval id. An entry survives being resolved so\n * that `wait()` still returns the answer when the approver replied before\n * anyone started waiting. Entries are dropped once the decision settles or is\n * released, which is the point at which nothing can ask again.\n */\n const pending = new Map<\n string,\n { resolve: (outcome: ApprovalOutcome) => void; promise: Promise<ApprovalOutcome> }\n >();\n\n async function resolveIntent(intent: Intent): Promise<ResolvedIntent> {\n const amount = parseMoney(intent.amount, \"USD\");\n const usd = await valueInUsd(amount, prices);\n return {\n ...intent,\n ...((intent.agent ?? defaultAgent) ? { agent: intent.agent ?? defaultAgent } : {}),\n amount,\n usd: convertPrecision(usd, \"USD\"),\n };\n }\n\n function longestWindowMs(): number {\n const windows = [...compiled.budgets.map((b) => b.windowMs), compiled.velocity?.windowMs ?? 0];\n return windows.length > 0 ? Math.max(...windows) : 0;\n }\n\n function buildDecision(\n entry: LedgerEntry,\n intent: ResolvedIntent,\n approvalId: string | undefined,\n ): Decision {\n const decision: Decision = {\n id: entry.id,\n verdict: entry.verdict,\n reason: entry.reason,\n rule: entry.rule,\n policyVersion: entry.policyVersion,\n intent,\n at: entry.at,\n ...(approvalId ? { approvalId } : {}),\n\n async settle(actual?: MoneyInput) {\n if (entry.verdict === \"block\") {\n throw new MoneoError(\n \"settle_blocked\",\n \"a blocked decision cannot settle: nothing was signed\",\n { id: entry.id },\n );\n }\n const patch: Parameters<DecisionLedger[\"update\"]>[1] = { status: \"settled\" };\n if (actual !== undefined) {\n const settledAmount = parseMoney(actual, intent.amount.asset);\n const settledUsd = convertPrecision(await valueInUsd(settledAmount, prices), \"USD\");\n patch.amount = toDecimalString(settledAmount);\n patch.usdCents = settledUsd.units;\n }\n await ledger.update(entry.id, patch);\n if (approvalId) pending.delete(approvalId);\n },\n\n async release() {\n await ledger.update(entry.id, { status: \"released\" });\n if (approvalId) pending.delete(approvalId);\n },\n\n async wait(waitOptions = {}) {\n if (entry.verdict !== \"hold\") {\n return { granted: entry.verdict === \"allow\", at: clock.now() };\n }\n const slot = approvalId ? pending.get(approvalId) : undefined;\n if (!slot) return { granted: false, at: clock.now() };\n if (waitOptions.timeout === undefined) return slot.promise;\n\n const ms = parseDuration(waitOptions.timeout as DurationInput);\n const timeout = clock.sleep(ms).then(() => {\n throw new MoneoError(\"approval_timeout\", `no answer within ${waitOptions.timeout}`, {\n approvalId,\n });\n });\n return Promise.race([slot.promise, timeout]);\n },\n };\n return decision;\n }\n\n const guard: Guard = {\n get policy() {\n return compiled;\n },\n ledger,\n\n async check(intent) {\n const resolved = await resolveIntent(intent);\n const now = clock.now();\n const window = longestWindowMs();\n const history = window > 0 ? await ledger.query({ since: now - window }) : [];\n\n const outcome = evaluate({ intent: resolved, policy: compiled, now, history });\n const verdict: Verdict = outcome?.verdict ?? \"allow\";\n\n const entry: LedgerEntry = {\n id: id(\"dec\"),\n at: now,\n ...(resolved.agent ? { agent: resolved.agent } : {}),\n action: resolved.action,\n ...(resolved.to ? { counterparty: resolved.to } : {}),\n asset: resolved.amount.asset,\n amount: toDecimalString(resolved.amount),\n usdCents: resolved.usd.units,\n verdict,\n reason: outcome?.reason ?? \"within policy\",\n rule: outcome?.rule ?? \"default\",\n policyVersion: compiled.version,\n status: verdict === \"block\" ? \"released\" : \"reserved\",\n ...(resolved.memo ? { memo: resolved.memo } : {}),\n ...(resolved.metadata ? { metadata: resolved.metadata } : {}),\n };\n await ledger.append(entry);\n\n let approvalId: string | undefined;\n if (verdict === \"hold\") {\n approvalId = id(\"apr\");\n let settle!: (outcome: ApprovalOutcome) => void;\n const promise = new Promise<ApprovalOutcome>((res) => {\n settle = res;\n });\n pending.set(approvalId, { resolve: settle, promise });\n }\n\n const decision = buildDecision(entry, resolved, approvalId);\n\n if (verdict === \"hold\" && approver && approvalId) {\n const answered = await approver.request(decision);\n if (answered) guard.resolve(approvalId, answered);\n }\n\n return decision;\n },\n\n wrap(fn, mapping) {\n return async (...args) => {\n const intent: Intent = {\n action: mapping.action,\n amount: mapping.amount(...args),\n };\n const to = mapping.to?.(...args);\n if (to !== undefined) intent.to = to;\n const agent = mapping.agent?.(...args);\n if (agent !== undefined) intent.agent = agent;\n const memo = mapping.memo?.(...args);\n if (memo !== undefined) intent.memo = memo;\n const metadata = mapping.metadata?.(...args);\n if (metadata !== undefined) intent.metadata = metadata;\n\n const decision = await guard.check(intent);\n if (decision.verdict === \"block\") throw new PolicyDeniedError(decision);\n if (decision.verdict === \"hold\") {\n const outcome = await decision.wait();\n if (!outcome.granted) throw new PolicyDeniedError(decision);\n }\n\n try {\n const result = await fn(...args);\n await decision.settle();\n return result;\n } catch (error) {\n // The call failed, so the money did not move. Give the budget back.\n await decision.release();\n throw error;\n }\n };\n },\n\n resolve(approvalId, outcome) {\n const slot = pending.get(approvalId);\n if (!slot) return;\n // Deliberately kept in the map: settle() and release() clean it up, so an\n // answer that arrives before anyone waits is not lost.\n slot.resolve({ ...outcome, at: outcome.at ?? clock.now() });\n },\n\n async usage(usageOptions = {}) {\n const now = clock.now();\n const budgets: BudgetUsage[] = [];\n for (const budget of compiled.budgets) {\n const query: LedgerQuery = { since: now - budget.windowMs };\n if (usageOptions.agent) query.agent = usageOptions.agent;\n const entries = await ledger.query(query);\n const scoped = budget.actions\n ? entries.filter((e) => budget.actions!.includes(e.action))\n : entries;\n const used = committedTotal(scoped);\n budgets.push({\n window: budget.label,\n windowMs: budget.windowMs,\n max: budget.max,\n used,\n remaining: clampToZero(subMoney(budget.max, used)),\n ...(budget.actions ? { actions: budget.actions } : {}),\n });\n }\n\n const usage: Usage = { policyVersion: compiled.version, budgets };\n\n if (compiled.velocity) {\n const entries = await ledger.query({ since: now - compiled.velocity.windowMs });\n const used = entries.filter((e) => e.status !== \"released\" && e.verdict !== \"block\").length;\n usage.velocity = {\n max: compiled.velocity.max,\n window: formatWindow(compiled.velocity.windowMs),\n used,\n remaining: Math.max(0, compiled.velocity.max - used),\n };\n }\n\n return usage;\n },\n\n history(query) {\n return ledger.query(query);\n },\n\n update(next) {\n compiled = compilePolicy(next);\n return compiled;\n },\n };\n\n return guard;\n}\n\nfunction clampToZero(m: Money): Money {\n return m.units < 0n ? fromUnits(0n, m.asset) : m;\n}\n\nfunction formatWindow(ms: number): string {\n const hours = ms / 3_600_000;\n if (Number.isInteger(hours) && hours >= 1) return `${hours}h`;\n const minutes = ms / 60_000;\n if (Number.isInteger(minutes) && minutes >= 1) return `${minutes}m`;\n return `${Math.round(ms / 1000)}s`;\n}\n\n/** Convenience for reading a running total straight out of a ledger. */\nexport async function spentSince(\n ledger: DecisionLedger,\n since: number,\n query: Omit<LedgerQuery, \"since\"> = {},\n): Promise<Money> {\n const entries = await ledger.query({ ...query, since });\n return committedTotal(entries);\n}\n","import { fromUnits, type Money } from \"@moneolabs/core\";\nimport type { ActionKind, Verdict } from \"./types.js\";\n\n/**\n * `reserved` means the guard said yes and the money has not been confirmed\n * moved yet. It still counts against budgets, because a payment in flight is\n * money you no longer have. `released` means it never happened.\n */\nexport type EntryStatus = \"reserved\" | \"settled\" | \"released\";\n\nexport interface LedgerEntry {\n id: string;\n at: number;\n agent?: string;\n action: ActionKind;\n counterparty?: string;\n asset: string;\n /** Amount in the asset's own minor units, as a decimal string. */\n amount: string;\n /** USD value in cents at the time of the decision. */\n usdCents: bigint;\n verdict: Verdict;\n reason: string;\n rule: string;\n policyVersion: string;\n status: EntryStatus;\n memo?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface LedgerQuery {\n since?: number;\n until?: number;\n agent?: string;\n action?: ActionKind;\n counterparty?: string;\n verdict?: Verdict;\n status?: EntryStatus;\n limit?: number;\n}\n\n/**\n * Where verdicts are written and where budgets are read back from. The default\n * implementation keeps everything in memory. Swap it for Postgres, SQLite, or\n * whatever already holds your financial records.\n */\nexport interface DecisionLedger {\n append(entry: LedgerEntry): Promise<void>;\n update(\n id: string,\n patch: Partial<Pick<LedgerEntry, \"status\" | \"amount\" | \"usdCents\">>,\n ): Promise<void>;\n query(query?: LedgerQuery): Promise<LedgerEntry[]>;\n}\n\n/**\n * Whether an entry consumes budget and velocity.\n *\n * An allowed movement counts from the moment it is reserved, because money in\n * flight is money you no longer have. A held movement counts only once it has\n * settled, since a pending approval may never be granted. Blocks and releases\n * never count, which is what makes a refusal free.\n */\nexport function countsTowardLimits(entry: LedgerEntry): boolean {\n if (entry.status === \"released\") return false;\n if (entry.verdict === \"block\") return false;\n if (entry.verdict === \"hold\") return entry.status === \"settled\";\n return true;\n}\n\n/** Total USD counted against budgets. */\nexport function committedTotal(entries: readonly LedgerEntry[]): Money {\n let cents = 0n;\n for (const entry of entries) {\n if (countsTowardLimits(entry)) cents += entry.usdCents;\n }\n return fromUnits(cents, \"USD\");\n}\n\nexport function memoryLedger(seed: readonly LedgerEntry[] = []): DecisionLedger {\n const entries: LedgerEntry[] = [...seed];\n const index = new Map(entries.map((e) => [e.id, e]));\n\n return {\n async append(entry) {\n entries.push(entry);\n index.set(entry.id, entry);\n },\n async update(id, patch) {\n const entry = index.get(id);\n if (!entry) return;\n Object.assign(entry, patch);\n },\n async query(query = {}) {\n let out = entries;\n if (query.since !== undefined) out = out.filter((e) => e.at >= query.since!);\n if (query.until !== undefined) out = out.filter((e) => e.at <= query.until!);\n if (query.agent) out = out.filter((e) => e.agent === query.agent);\n if (query.action) out = out.filter((e) => e.action === query.action);\n if (query.counterparty) out = out.filter((e) => e.counterparty === query.counterparty);\n if (query.verdict) out = out.filter((e) => e.verdict === query.verdict);\n if (query.status) out = out.filter((e) => e.status === query.status);\n out = [...out].sort((a, b) => a.at - b.at);\n if (query.limit !== undefined) out = out.slice(-query.limit);\n return out;\n },\n };\n}\n","import {\n fingerprint,\n formatDuration,\n formatMoney,\n isPositiveMoney,\n parseDuration,\n parseMoney,\n toDecimalString,\n ValidationError,\n type DurationInput,\n type Money,\n type MoneyInput,\n} from \"@moneolabs/core\";\nimport type { ActionKind } from \"./types.js\";\n\n/** A rolling window and the most that may move through it. */\nexport interface BudgetRule {\n window: DurationInput;\n max: MoneyInput;\n /** Restrict this budget to certain actions. Omit to cover everything. */\n actions?: ActionKind[];\n}\n\nexport interface VelocityRule {\n /** Most decisions allowed inside the window. */\n max: number;\n per: DurationInput;\n /** Count only movements to the same counterparty. */\n perCounterparty?: boolean;\n}\n\nexport interface Policy {\n /** Ceiling on any single movement, in USD. */\n perTransaction?: { max: MoneyInput };\n /** Shorthand for a single 24 hour rolling budget. */\n rolling24h?: { max: MoneyInput };\n /** Any number of rolling windows, evaluated together. */\n budgets?: BudgetRule[];\n /** Rate limit on money, not on requests. */\n velocity?: VelocityRule;\n /**\n * \"allowlist-only\" blocks anything not matched by `allow`, and blocks\n * movements that name no counterparty at all. \"open\" runs `deny` only.\n */\n counterparties?: \"allowlist-only\" | \"open\";\n /** Patterns that may receive funds. `*` matches any run of characters. */\n allow?: string[];\n /** Patterns that may never receive funds. Checked before everything else. */\n deny?: string[];\n /** Assets this agent may move. Omit to allow all. */\n assets?: { allow?: string[]; deny?: string[] };\n /** Action kinds this agent may perform. Omit to allow all. */\n actions?: { allow?: ActionKind[]; deny?: ActionKind[] };\n /** Above this USD figure, a human has to say yes. */\n escalate?: { above: MoneyInput };\n /** Carried onto every decision. Useful for naming a policy in the ledger. */\n label?: string;\n}\n\nexport interface CompiledBudget {\n /**\n * How the window was written, for example \"24h\". Rule names and refusal\n * messages quote this back, so an author reading a blocked decision sees the\n * same words they put in the policy.\n */\n readonly label: string;\n readonly windowMs: number;\n readonly max: Money;\n readonly actions?: readonly ActionKind[];\n}\n\n/** A policy with every amount and duration resolved, plus a stable version. */\nexport interface CompiledPolicy {\n readonly version: string;\n readonly label?: string;\n readonly perTransactionMax?: Money;\n readonly budgets: readonly CompiledBudget[];\n readonly velocity?: { max: number; windowMs: number; perCounterparty: boolean };\n readonly counterparties: \"allowlist-only\" | \"open\";\n readonly allow: readonly string[];\n readonly deny: readonly string[];\n readonly assetsAllow?: readonly string[];\n readonly assetsDeny: readonly string[];\n readonly actionsAllow?: readonly ActionKind[];\n readonly actionsDeny: readonly ActionKind[];\n readonly escalateAbove?: Money;\n readonly source: Policy;\n}\n\n/**\n * Resolve a policy once, up front. Every amount is parsed, every window is\n * converted to milliseconds, and the result is fingerprinted so that each\n * verdict can name the exact policy that produced it.\n */\nexport function compilePolicy(policy: Policy): CompiledPolicy {\n const perTransactionMax = policy.perTransaction\n ? requirePositiveUsd(policy.perTransaction.max, \"perTransaction.max\")\n : undefined;\n\n const budgets: CompiledBudget[] = [];\n if (policy.rolling24h) {\n budgets.push({\n label: \"24h\",\n windowMs: parseDuration(\"24h\"),\n max: requirePositiveUsd(policy.rolling24h.max, \"rolling24h.max\"),\n });\n }\n for (const [index, budget] of (policy.budgets ?? []).entries()) {\n const windowMs = parseDuration(budget.window);\n if (windowMs <= 0) {\n throw new ValidationError(`budgets[${index}].window must be longer than zero`, { budget });\n }\n budgets.push({\n label: typeof budget.window === \"string\" ? budget.window.trim() : formatDuration(windowMs),\n windowMs,\n max: requirePositiveUsd(budget.max, `budgets[${index}].max`),\n ...(budget.actions ? { actions: [...budget.actions] } : {}),\n });\n }\n\n let velocity: CompiledPolicy[\"velocity\"];\n if (policy.velocity) {\n const windowMs = parseDuration(policy.velocity.per);\n if (!Number.isInteger(policy.velocity.max) || policy.velocity.max < 1) {\n throw new ValidationError(\"velocity.max must be a whole number of one or more\", {\n velocity: policy.velocity,\n });\n }\n if (windowMs <= 0) {\n throw new ValidationError(\"velocity.per must be longer than zero\", {\n velocity: policy.velocity,\n });\n }\n velocity = {\n max: policy.velocity.max,\n windowMs,\n perCounterparty: policy.velocity.perCounterparty ?? false,\n };\n }\n\n const counterparties = policy.counterparties ?? \"open\";\n if (counterparties === \"allowlist-only\" && (policy.allow ?? []).length === 0) {\n throw new ValidationError(\n \"counterparties is allowlist-only but allow is empty, which blocks every movement\",\n );\n }\n\n const escalateAbove = policy.escalate\n ? requirePositiveUsd(policy.escalate.above, \"escalate.above\")\n : undefined;\n\n const compiled: Omit<CompiledPolicy, \"version\"> = {\n ...(policy.label ? { label: policy.label } : {}),\n ...(perTransactionMax ? { perTransactionMax } : {}),\n budgets,\n ...(velocity ? { velocity } : {}),\n counterparties,\n allow: [...(policy.allow ?? [])],\n deny: [...(policy.deny ?? [])],\n ...(policy.assets?.allow ? { assetsAllow: policy.assets.allow.map(upper) } : {}),\n assetsDeny: (policy.assets?.deny ?? []).map(upper),\n ...(policy.actions?.allow ? { actionsAllow: [...policy.actions.allow] } : {}),\n actionsDeny: [...(policy.actions?.deny ?? [])],\n ...(escalateAbove ? { escalateAbove } : {}),\n source: policy,\n };\n\n return { ...compiled, version: versionOf(compiled) };\n}\n\n/**\n * A deterministic id for a policy. Two policies that differ only in key order\n * or in how an amount was written produce the same version, which is what makes\n * \"which policy blocked this\" answerable months later.\n */\nexport function versionOf(compiled: Omit<CompiledPolicy, \"version\">): string {\n const shape = {\n label: compiled.label,\n perTransactionMax: compiled.perTransactionMax && describe(compiled.perTransactionMax),\n // The budget's own label is left out: \"24h\" and 86400000 are the same rule.\n budgets: compiled.budgets.map((b) => ({\n windowMs: b.windowMs,\n max: describe(b.max),\n actions: b.actions ? [...b.actions].sort() : undefined,\n })),\n velocity: compiled.velocity,\n counterparties: compiled.counterparties,\n allow: [...compiled.allow].sort(),\n deny: [...compiled.deny].sort(),\n assetsAllow: compiled.assetsAllow ? [...compiled.assetsAllow].sort() : undefined,\n assetsDeny: [...compiled.assetsDeny].sort(),\n actionsAllow: compiled.actionsAllow ? [...compiled.actionsAllow].sort() : undefined,\n actionsDeny: [...compiled.actionsDeny].sort(),\n escalateAbove: compiled.escalateAbove && describe(compiled.escalateAbove),\n };\n return `pol_${fingerprint(shape)}`;\n}\n\nfunction describe(m: Money): string {\n return `${toDecimalString(m)} ${m.asset}`;\n}\n\nfunction upper(value: string): string {\n return value.toUpperCase();\n}\n\nfunction requirePositiveUsd(input: MoneyInput, field: string): Money {\n const amount = parseMoney(input, \"USD\");\n if (amount.asset !== \"USD\") {\n throw new ValidationError(`${field} must be in USD, got ${formatMoney(amount)}`, { field });\n }\n if (!isPositiveMoney(amount)) {\n throw new ValidationError(`${field} must be greater than zero`, { field });\n }\n return amount;\n}\n","import {\n addMoney,\n formatDuration,\n formatMoney,\n fromUnits,\n gtMoney,\n subMoney,\n type Money,\n} from \"@moneolabs/core\";\nimport { matchesAny } from \"./match.js\";\nimport type { CompiledPolicy } from \"./policy.js\";\nimport { committedTotal, countsTowardLimits, type LedgerEntry } from \"./ledger.js\";\nimport type { ResolvedIntent } from \"./types.js\";\n\nexport interface RuleContext {\n readonly intent: ResolvedIntent;\n readonly policy: CompiledPolicy;\n readonly now: number;\n /** Everything on the ledger inside the longest window the policy cares about. */\n readonly history: readonly LedgerEntry[];\n}\n\nexport interface RuleOutcome {\n verdict: \"hold\" | \"block\";\n rule: string;\n reason: string;\n}\n\ntype Rule = (context: RuleContext) => RuleOutcome | undefined;\n\n/**\n * Order is deliberate. Denylist first so a banned counterparty is never\n * described as merely over budget, and escalation last so a held decision is\n * one that passed every hard limit.\n */\nconst RULES: Rule[] = [\n denyList,\n assetRules,\n actionRules,\n allowList,\n perTransactionCap,\n velocityLimit,\n rollingBudgets,\n escalation,\n];\n\nexport function evaluate(context: RuleContext): RuleOutcome | undefined {\n let held: RuleOutcome | undefined;\n for (const rule of RULES) {\n const outcome = rule(context);\n if (!outcome) continue;\n if (outcome.verdict === \"block\") return outcome;\n held ??= outcome;\n }\n return held;\n}\n\n/* -------------------------------------------------------------------------- */\n\nfunction denyList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (!intent.to || policy.deny.length === 0) return undefined;\n const pattern = matchesAny(intent.to, policy.deny);\n if (!pattern) return undefined;\n return {\n verdict: \"block\",\n rule: \"denylist\",\n reason: `counterparty ${intent.to} is on the denylist (matched \"${pattern}\")`,\n };\n}\n\nfunction assetRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const asset = intent.amount.asset;\n if (policy.assetsDeny.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.deny\",\n reason: `this agent may not move ${asset}`,\n };\n }\n if (policy.assetsAllow && !policy.assetsAllow.includes(asset)) {\n return {\n verdict: \"block\",\n rule: \"assets.allow\",\n reason: `this agent may only move ${policy.assetsAllow.join(\", \")}, not ${asset}`,\n };\n }\n return undefined;\n}\n\nfunction actionRules({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.actionsDeny.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.deny\",\n reason: `this agent may not perform \"${intent.action}\"`,\n };\n }\n if (policy.actionsAllow && !policy.actionsAllow.includes(intent.action)) {\n return {\n verdict: \"block\",\n rule: \"actions.allow\",\n reason: `this agent may only perform ${policy.actionsAllow.join(\", \")}, not \"${intent.action}\"`,\n };\n }\n return undefined;\n}\n\nfunction allowList({ intent, policy }: RuleContext): RuleOutcome | undefined {\n if (policy.counterparties !== \"allowlist-only\") return undefined;\n\n // An intent with no counterparty must not be a way around the allowlist.\n if (!intent.to) {\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: \"policy is allowlist-only and this movement names no counterparty\",\n };\n }\n if (matchesAny(intent.to, policy.allow)) return undefined;\n return {\n verdict: \"block\",\n rule: \"allowlist\",\n reason: `counterparty ${intent.to} is not on the allowlist`,\n };\n}\n\nfunction perTransactionCap({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const max = policy.perTransactionMax;\n if (!max || !gtMoney(intent.usd, max)) return undefined;\n return {\n verdict: \"block\",\n rule: \"perTransaction\",\n reason: `${formatMoney(intent.usd)} exceeds the ${formatMoney(max)} per-transaction limit`,\n };\n}\n\nfunction velocityLimit({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n const velocity = policy.velocity;\n if (!velocity) return undefined;\n\n const since = now - velocity.windowMs;\n const recent = history.filter(\n (entry) =>\n entry.at > since &&\n countsTowardLimits(entry) &&\n (!velocity.perCounterparty || entry.counterparty === intent.to),\n );\n\n if (recent.length < velocity.max) return undefined;\n\n const scope = velocity.perCounterparty ? ` to ${intent.to}` : \"\";\n return {\n verdict: \"block\",\n rule: \"velocity\",\n reason:\n `${recent.length} movements${scope} in the last ${formatDuration(velocity.windowMs)} ` +\n `already meets the limit of ${velocity.max}`,\n };\n}\n\nfunction rollingBudgets({ intent, policy, now, history }: RuleContext): RuleOutcome | undefined {\n for (const budget of policy.budgets) {\n if (budget.actions && !budget.actions.includes(intent.action)) continue;\n\n const since = now - budget.windowMs;\n const window = history.filter(\n (entry) => entry.at > since && (!budget.actions || budget.actions.includes(entry.action)),\n );\n const used = committedTotal(window);\n const projected = addMoney(used, intent.usd);\n if (!gtMoney(projected, budget.max)) continue;\n\n const remaining = remainingOrZero(budget.max, used);\n return {\n verdict: \"block\",\n rule: `budget.${budget.label}`,\n reason:\n `${formatMoney(intent.usd)} exceeds the rolling ${budget.label} budget: ` +\n `${formatMoney(budget.max)} cap, ${formatMoney(used)} used, ` +\n `${formatMoney(remaining)} left`,\n };\n }\n return undefined;\n}\n\nfunction escalation({ intent, policy }: RuleContext): RuleOutcome | undefined {\n const above = policy.escalateAbove;\n if (!above || !gtMoney(intent.usd, above)) return undefined;\n return {\n verdict: \"hold\",\n rule: \"escalate\",\n reason: `${formatMoney(intent.usd)} is above the ${formatMoney(above)} approval threshold`,\n };\n}\n\nfunction remainingOrZero(max: Money, used: Money): Money {\n const left = subMoney(max, used);\n return left.units < 0n ? fromUnits(0n, left.asset) : left;\n}\n","/**\n * Counterparty patterns. `*` matches any run of characters, everything else is\n * literal, and matching is case insensitive because hex addresses arrive in\n * whatever case the caller happened to have.\n *\n * Examples that all match \"x402:api.pricefeed.dev/quote\":\n * \"x402:*\" \"x402:api.pricefeed.dev/*\" \"*pricefeed*\"\n */\nconst cache = new Map<string, RegExp>();\n\nexport function matchesPattern(value: string, pattern: string): boolean {\n return toRegExp(pattern).test(value);\n}\n\nexport function matchesAny(value: string, patterns: readonly string[]): string | undefined {\n for (const pattern of patterns) {\n if (matchesPattern(value, pattern)) return pattern;\n }\n return undefined;\n}\n\nfunction toRegExp(pattern: string): RegExp {\n const hit = cache.get(pattern);\n if (hit) return hit;\n\n const source = `^${pattern.split(\"*\").map(escapeRegExp).join(\".*\")}$`;\n const compiled = new RegExp(source, \"iu\");\n\n // Patterns come from config, not user input, but the cache is still bounded\n // so a generated allowlist cannot grow it without limit.\n if (cache.size > 1000) cache.clear();\n cache.set(pattern, compiled);\n return compiled;\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { formatMoney } from \"@moneolabs/core\";\nimport type { ApprovalOutcome, Approver, Decision } from \"./types.js\";\n\n/**\n * Leaves every hold pending until someone calls `guard.resolve()`. This is the\n * behaviour you get when no approver is configured, expressed explicitly.\n */\nexport const manualApprover: Approver = {\n async request() {\n return undefined;\n },\n};\n\n/** Answers every hold the same way. For tests and for local development. */\nexport function autoApprover(granted: boolean, by = \"auto\"): Approver {\n return {\n async request(decision) {\n return { granted, by, at: decision.at };\n },\n };\n}\n\n/** Prints the request and leaves it pending. Handy in a terminal session. */\nexport function loggingApprover(log: (message: string) => void = console.log): Approver {\n return {\n async request(decision) {\n log(\n `[moneo] approval needed: ${decision.intent.action} ${formatMoney(decision.intent.usd)}` +\n `${decision.intent.to ? ` to ${decision.intent.to}` : \"\"}` +\n ` (${decision.reason}). Resolve with id ${decision.approvalId}`,\n );\n return undefined;\n },\n };\n}\n\n/**\n * Posts the request to an HTTP endpoint. Return a JSON body of\n * `{ \"granted\": true }` to answer immediately, or anything else to leave the\n * decision pending for `guard.resolve()`.\n */\nexport function webhookApprover(\n url: string,\n options: { headers?: Record<string, string>; fetch?: typeof fetch } = {},\n): Approver {\n const doFetch = options.fetch ?? globalThis.fetch;\n return {\n async request(decision): Promise<ApprovalOutcome | undefined> {\n const response = await doFetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...options.headers },\n body: JSON.stringify(summarize(decision)),\n });\n if (!response.ok) return undefined;\n\n const body = (await response.json().catch(() => null)) as {\n granted?: boolean;\n by?: string;\n note?: string;\n } | null;\n if (!body || typeof body.granted !== \"boolean\") return undefined;\n\n return {\n granted: body.granted,\n ...(body.by ? { by: body.by } : {}),\n ...(body.note ? { note: body.note } : {}),\n at: decision.at,\n };\n },\n };\n}\n\nfunction summarize(decision: Decision) {\n return {\n approvalId: decision.approvalId,\n decisionId: decision.id,\n policyVersion: decision.policyVersion,\n reason: decision.reason,\n rule: decision.rule,\n agent: decision.intent.agent,\n action: decision.intent.action,\n to: decision.intent.to,\n memo: decision.intent.memo,\n usd: formatMoney(decision.intent.usd),\n amount: formatMoney(decision.intent.amount),\n at: decision.at,\n };\n}\n","import {\n fromUnits,\n manualClock,\n peggedPrices,\n type Money,\n type PriceSource,\n} from \"@moneolabs/core\";\nimport { createGuard } from \"./guard.js\";\nimport { memoryLedger, type LedgerEntry } from \"./ledger.js\";\nimport type { Policy } from \"./policy.js\";\nimport type { Intent, Verdict } from \"./types.js\";\n\nexport interface SimulationEvent extends Intent {\n /** When it happened. Defaults to the previous event's time. */\n at?: number;\n /**\n * Whether the movement actually completed. Reserved-but-never-settled spend\n * still counts against budgets, so replays should say.\n */\n settled?: boolean;\n}\n\nexport interface SimulationResult {\n event: SimulationEvent;\n verdict: Verdict;\n reason: string;\n rule: string;\n usd: Money;\n}\n\nexport interface SimulationReport {\n policyVersion: string;\n results: SimulationResult[];\n counts: Record<Verdict, number>;\n allowedUsd: Money;\n blockedUsd: Money;\n heldUsd: Money;\n entries: LedgerEntry[];\n}\n\n/**\n * Replay a list of movements against a policy without touching anything real.\n *\n * This is how you find out that the budget you are about to ship would have\n * blocked a third of last month before you ship it, rather than after.\n */\nexport async function simulate(\n policy: Policy,\n events: readonly SimulationEvent[],\n options: { prices?: PriceSource; startAt?: number } = {},\n): Promise<SimulationReport> {\n const clock = manualClock(options.startAt ?? events[0]?.at ?? 0);\n const guard = createGuard(policy, {\n ledger: memoryLedger(),\n clock,\n prices: options.prices ?? peggedPrices,\n });\n\n const results: SimulationResult[] = [];\n const counts: Record<Verdict, number> = { allow: 0, hold: 0, block: 0 };\n let allowedCents = 0n;\n let blockedCents = 0n;\n let heldCents = 0n;\n\n for (const event of events) {\n if (event.at !== undefined && event.at > clock.now()) {\n await clock.advance(event.at - clock.now());\n }\n\n const decision = await guard.check(event);\n counts[decision.verdict] += 1;\n\n const cents = decision.intent.usd.units;\n if (decision.verdict === \"allow\") {\n allowedCents += cents;\n if (event.settled !== false) await decision.settle();\n } else if (decision.verdict === \"hold\") {\n heldCents += cents;\n } else {\n blockedCents += cents;\n }\n\n results.push({\n event,\n verdict: decision.verdict,\n reason: decision.reason,\n rule: decision.rule,\n usd: decision.intent.usd,\n });\n }\n\n return {\n policyVersion: guard.policy.version,\n results,\n counts,\n allowedUsd: fromUnits(allowedCents, \"USD\"),\n blockedUsd: fromUnits(blockedCents, \"USD\"),\n heldUsd: fromUnits(heldCents, \"USD\"),\n entries: await guard.history(),\n };\n}\n\n/** Turn past ledger entries back into events, so you can replay real history. */\nexport function eventsFromLedger(entries: readonly LedgerEntry[]): SimulationEvent[] {\n return entries.map((entry) => ({\n action: entry.action,\n amount: `${entry.amount} ${entry.asset}`,\n ...(entry.counterparty ? { to: entry.counterparty } : {}),\n ...(entry.agent ? { agent: entry.agent } : {}),\n ...(entry.memo ? { memo: entry.memo } : {}),\n at: entry.at,\n settled: entry.status === \"settled\",\n }));\n}\n"],"mappings":"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","names":["fromUnits","parseDuration","parseMoney","subMoney","toDecimalString","id","formatDuration","formatMoney","fromUnits","formatMoney","formatDuration","fromUnits","parseMoney","toDecimalString","parseDuration","subMoney","fromUnits","formatMoney","fromUnits","peggedPrices","peggedPrices","fromUnits"]}
{
"name": "@moneolabs/guard",
"version": "0.1.0",
"version": "0.2.0",
"description": "Spending policy for AI agents, evaluated before anything is signed.",

@@ -45,6 +45,10 @@ "license": "MIT",

"ai-agents",
"spend-guard",
"spending-limits",
"spending-policy",
"agent-payments",
"policy",
"guardrails",
"budget"
"budget",
"llm-safety"
],

@@ -56,4 +60,4 @@ "scripts": {

"dependencies": {
"@moneolabs/core": "0.1.0"
"@moneolabs/core": "0.2.0"
}
}