{"_id":"@carloscortezcloud/sayay-guard","_rev":"6-d8fbe2cd6492463853a827e2e5af8b01","name":"@carloscortezcloud/sayay-guard","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.0":{"name":"@carloscortezcloud/sayay-guard","version":"0.1.0","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"author":{"name":"Carlos Cortez","email":"carlos@finoptix.dev"},"license":"Apache-2.0","_id":"@carloscortezcloud/sayay-guard@0.1.0","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"homepage":"https://github.com/breakingthecloud/sayay#readme","bugs":{"url":"https://github.com/breakingthecloud/sayay/issues"},"dist":{"shasum":"893d65ce7d36e775e239ca66cdd4475d8dad39c6","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.1.0.tgz","fileCount":4,"integrity":"sha512-zmD2HnM2+e+a8rYB63Gr+JDQLIREm0oMv4FOj4m6I+TK1I+kBiEeVqCBViihQnyWq7V8IZcEWX5WXj5LiaOqqA==","signatures":[{"sig":"MEUCIDwnGmDPyK252C5h72GHuqbHGnhvoG7/Kj5BsTYNFFCxAiEA92OTWjX7oVvBwjlp6vaS7EP/cSnypnZwiK35wxqRFOQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17176},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"f80f14d780062aa3cd79b23e1853060bbbf0634b","scripts":{"test":"vitest run","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"repository":{"url":"git+https://github.com/breakingthecloud/sayay.git","type":"git"},"_npmVersion":"10.8.2","description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sayay-guard_0.1.0_1784763094017_0.4413245252235767","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@carloscortezcloud/sayay-guard","version":"0.1.1","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"author":{"name":"Carlos Cortez","email":"carlos@finoptix.dev"},"license":"Apache-2.0","_id":"@carloscortezcloud/sayay-guard@0.1.1","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"homepage":"https://github.com/breakingthecloud/sayay#readme","bugs":{"url":"https://github.com/breakingthecloud/sayay/issues"},"dist":{"shasum":"17dfdc0e5c561635f14aa562f60088063badc19c","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.1.1.tgz","fileCount":4,"integrity":"sha512-8IYQIX2Bw9TBtYkEjHKZA9MCKRjz2Hm2qctMCUKkBIBw9eoBBRmLcaPJ8EGgtONgl0vXBBiN9WQU68JSGB8rzg==","signatures":[{"sig":"MEUCIQCsvUymxxDXBm1yuLZIpVMCj+maD/X1kyeFUzcQN17G5QIgNWNEmhKKhnxBZBpAHBQpPp/NRj0HSHXlISGwOkvMP4g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17196},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","gitHead":"f80f14d780062aa3cd79b23e1853060bbbf0634b","scripts":{"test":"vitest run","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"repository":{"url":"git+https://github.com/breakingthecloud/sayay.git","type":"git"},"_npmVersion":"10.8.2","description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.5.0"},"_npmOperationalInternal":{"tmp":"tmp/sayay-guard_0.1.1_1784763379164_0.9460904259496641","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@carloscortezcloud/sayay-guard","version":"0.2.0","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"author":"Carlos Cortez <carlos@finoptix.dev>","license":"Apache-2.0","_id":"@carloscortezcloud/sayay-guard@0.2.0","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"dist":{"shasum":"d0a0e0a505c20696ec2e5cbc686f70aa4e3d4104","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.2.0.tgz","fileCount":4,"integrity":"sha512-PSsXT4dFYSPWGd8boHq0XJGaCO+qg9d6uiUiNErcsDTbAoFlsy+FU4O0gygnBvukaVWgOolZhd+GcAw4XEdIkg==","signatures":[{"sig":"MEUCIQCD0t7/I9j/ftfG9f/1zlSnaEMooNmXK+fCH1pbCLhoGgIgUODfBNykW/pYCk+tdw9IXVoS7tWq21LwRf+Fbk0a21s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32544},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","scripts":{"test":"vitest run","build":"tsc"},"_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"repository":{"url":"https://github.com/breakingthecloud/sayay","type":"git"},"description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^26.1.2","@aws-sdk/lib-dynamodb":"^3.1101.0","@aws-sdk/client-dynamodb":"^3.1101.0","@aws-sdk/client-cloudwatch":"^3.1101.0"},"_npmOperationalInternal":{"tmp":"tmp/sayay-guard_0.2.0_1785706245256_0.9276191501981674","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@carloscortezcloud/sayay-guard","version":"0.3.0","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"author":"Carlos Cortez <carlos@finoptix.dev>","license":"Apache-2.0","_id":"@carloscortezcloud/sayay-guard@0.3.0","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"dist":{"shasum":"12f8a5ffc424a6e02a01530c5b5b6393d2fd46bb","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.3.0.tgz","fileCount":6,"integrity":"sha512-lGgN/+UjfNQRMIFHzUV8mX5Ga/sgyInfFcmCxYZ0Y9/9CJiQJ6luHx7PVa4AKs4XF4E8aVD50Lj6eKm3s0CIJA==","signatures":[{"sig":"MEYCIQD54HKt4ilCZLHMIxeZ+pbcLv+fOHuygsBXjQkhfkHMSAIhAMQsS4OrqFvB2NCRcAmEJulloe2KBwu0wAmFAV9jbWur","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39690},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsc"},"_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"repository":{"url":"https://github.com/breakingthecloud/sayay","type":"git"},"description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"_nodeVersion":"22.23.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^26.1.2","@aws-sdk/lib-dynamodb":"^3.1101.0","@aws-sdk/client-dynamodb":"^3.1101.0","@carloscortezcloud/qhaway":"^0.10.0","@aws-sdk/client-cloudwatch":"^3.1101.0","@carloscortezcloud/styrr-llm":"^0.4.0","@carloscortezcloud/tinkuy-agent":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sayay-guard_0.3.0_1786757509543_0.13890576321953874","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@carloscortezcloud/sayay-guard","version":"0.4.0","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"author":"Carlos Cortez <carlos@finoptix.dev>","license":"Apache-2.0","_id":"@carloscortezcloud/sayay-guard@0.4.0","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"bin":{"sayay-mcp":"bin/sayay-mcp.mjs","sayay-claude-code":"bin/sayay-claude-code.mjs"},"dist":{"shasum":"ef6e000276835bc8996fba53fe2e5ae6409c2fbf","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.4.0.tgz","fileCount":14,"integrity":"sha512-V0dcxAOoOgwPoMzmxQy06SNRa8m5kojGcZ+IbHZOhsyzzCWrNw5pE1hRHIlQxTOiwjXUhn4PVVnsq+ijENrYdg==","signatures":[{"sig":"MEUCIQCWrmdAczqB9aBsdUTad5RicN7HIFpkFX3Stj0T8+KtJAIgH35KoMxtx/wzV35alj+xb/aUj057/UWpOCkfAZracYA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQC2URRtxTZVc+vkQEnsQe8DhpzcHz0UPrKMllF+Uf14SwIgPC23Oh75OnMD/9h4PzmRYAvWueEERUKYSvFYA8kYUKU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":68456},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./mcp":{"types":"./dist/adapters/mcp.d.ts","import":"./dist/adapters/mcp.js"},"./opencode":{"types":"./dist/adapters/opencode.d.ts","import":"./dist/adapters/opencode.js"},"./claude-code":{"types":"./dist/adapters/claude-code.d.ts","import":"./dist/adapters/claude-code.js"}},"scripts":{"test":"vitest run","build":"tsc"},"_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"repository":{"url":"https://github.com/breakingthecloud/sayay","type":"git"},"description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"_nodeVersion":"22.23.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^26.1.2","@aws-sdk/lib-dynamodb":"^3.1101.0","@aws-sdk/client-dynamodb":"^3.1101.0","@carloscortezcloud/qhaway":"^0.11.0","@aws-sdk/client-cloudwatch":"^3.1101.0","@carloscortezcloud/styrr-llm":"^0.6.0","@carloscortezcloud/tinkuy-agent":"^0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/sayay-guard_0.4.0_1789329790634_0.1004714602255985","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"_id":"@carloscortezcloud/sayay-guard@0.5.0","bin":{"sayay-mcp":"bin/sayay-mcp.mjs","sayay-claude-code":"bin/sayay-claude-code.mjs"},"dist":{"shasum":"78828a5abb3dc1e50be65ad84cda11749b63b245","tarball":"https://registry.npmjs.org/@carloscortezcloud/sayay-guard/-/sayay-guard-0.5.0.tgz","fileCount":28,"integrity":"sha512-9PWdMqK2x7Ccy3IrN7OGY9G+qS2f+nwhdEiPEKwpLMMlJDwlvVAdVwIpcEDXD4JfabDYGzTvkwKu1WRwrTLV6A==","signatures":[{"sig":"MEUCIQDLg5yiz69ytUvV+DJnefqN1jrP5WuGP5BZfUUrgcr3sgIgYfMUnnk7OaXtQMdhl27K735I31ruQUnrWN6ke62npNE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC7tC+xbFjZxCblB4JaYu8uptqB/xnpP2z1XTmLU3O+NwIgNLQLXy9ncje4fZMEK5dfYG/zevfXaHFIOvb9dN0Z+1I="}],"unpackedSize":81661},"main":"dist/index.js","name":"@carloscortezcloud/sayay-guard","type":"module","types":"dist/index.d.ts","author":"Carlos Cortez <carlos@finoptix.dev>","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./mcp":{"types":"./dist/adapters/mcp.d.ts","import":"./dist/adapters/mcp.js"},"./bedrock":{"types":"./dist/adapters/bedrock.d.ts","import":"./dist/adapters/bedrock.js"},"./storage":{"types":"./dist/storage/index.d.ts","import":"./dist/storage/index.js"},"./opencode":{"types":"./dist/adapters/opencode.d.ts","import":"./dist/adapters/opencode.js"},"./agents-cf":{"types":"./dist/adapters/agents-cf.d.ts","import":"./dist/adapters/agents-cf.js"},"./langchain":{"types":"./dist/adapters/langchain.d.ts","import":"./dist/adapters/langchain.js"},"./claude-code":{"types":"./dist/adapters/claude-code.d.ts","import":"./dist/adapters/claude-code.js"}},"license":"Apache-2.0","scripts":{"test":"vitest run","build":"tsc"},"version":"0.5.0","_npmUser":{"name":"carloscortezcloud","email":"carlos@cortez.cloud"},"keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"repository":{"url":"https://github.com/breakingthecloud/sayay","type":"git"},"description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","directories":{},"maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"_nodeVersion":"22.23.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","vitest":"^2.0.0","typescript":"^5.5.0","@types/node":"^26.1.2","@aws-sdk/lib-dynamodb":"^3.1101.0","@aws-sdk/client-dynamodb":"^3.1101.0","@carloscortezcloud/qhaway":"^0.11.0","@aws-sdk/client-cloudwatch":"^3.1101.0","@carloscortezcloud/styrr-llm":"^0.6.0","@carloscortezcloud/tinkuy-agent":"^0.8.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sayay-guard_0.5.0_1789352715320_0.25710570891371476"}}},"time":{"created":"2026-07-22T23:31:33.700Z","modified":"2026-09-14T02:25:15.540Z","0.1.0":"2026-07-22T23:31:34.147Z","0.1.1":"2026-07-22T23:36:19.309Z","0.2.0":"2026-08-02T21:30:45.479Z","0.3.0":"2026-08-15T01:31:49.694Z","0.4.0":"2026-09-13T20:03:10.721Z","0.5.0":"2026-09-14T02:25:15.396Z"},"author":"Carlos Cortez <carlos@finoptix.dev>","license":"Apache-2.0","keywords":["ai","guardrails","budget","cost","llm","agents","rate-limit","credits","finops"],"repository":{"url":"https://github.com/breakingthecloud/sayay","type":"git"},"description":"AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.","maintainers":[{"name":"carloscortezcloud","email":"carlos@cortez.cloud"}],"readme":"<p align=\"center\">\n  <img alt=\"Sayay\" src=\"https://img.shields.io/badge/⚓-Sayay-F59E0B?style=for-the-badge\" height=\"50\">\n</p>\n\n<p align=\"center\">\n  <b>AI Agent Cost Guardrails</b><br>\n  Budget enforcement middleware for LLM calls. Prevent runaway AI costs.\n</p>\n\n<p align=\"center\">\n  <a href=\"#quick-start\">Quick Start</a>\n  ·\n  <a href=\"#actions\">Actions</a>\n  ·\n  <a href=\"#storage\">Storage</a>\n  ·\n  <a href=\"#ecosystem\">Ecosystem</a>\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/license-Apache_2.0-F59E0B?style=flat-square\" alt=\"License\">\n  <img src=\"https://img.shields.io/badge/TypeScript-5.5%2B-3178C6?style=flat-square&logo=typescript\" alt=\"TypeScript\">\n  <img src=\"https://img.shields.io/badge/dependencies-0-success?style=flat-square\" alt=\"Zero deps\">\n  <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen?style=flat-square\" alt=\"PRs\">\n</p>\n\n---\n\n## What Is Sayay?\n\nSayay (Quechua: \"to stop/detain\") stops your AI costs from running away. Set per-user daily/monthly budgets or credit systems. Before every LLM call, Sayay decides: allow, warn, degrade, or block.\n\n```typescript\nimport { SayayGuard, MemoryStorage } from 'sayay';\n\nconst guard = new SayayGuard({\n  storage: new MemoryStorage(),\n  budget: { dailyUsd: 5.00, monthlyUsd: 50.00 },\n  onExceeded: 'block',\n  degradeToModel: 'meta-llama/llama-3.3-70b-instruct:free',\n});\n\n// Before LLM call:\nconst decision = await guard.check('user-123', 0.003);\nif (decision.action === 'block') {\n  throw new Error(`Budget exceeded: ${decision.reason}`);\n}\nif (decision.action === 'degrade') {\n  // Use decision.suggestedModel instead of expensive model\n}\n\n// After LLM call:\nawait guard.record('user-123', 0.0025);\n```\n\n## Install\n\n```bash\nnpm install github:breakingthecloud/sayay\n```\n\n## Quick Start\n\n```typescript\nimport { SayayGuard, MemoryStorage } from 'sayay';\n\nconst guard = new SayayGuard({\n  storage: new MemoryStorage(),\n  budget: { dailyUsd: 5.00, monthlyUsd: 50.00 },\n  onExceeded: 'block',\n  degradeToModel: 'meta-llama/llama-3.3-70b-instruct:free',\n});\n\nconst decision = await guard.check('user-123', 0.003);\nconsole.log(decision.action); // 'allow' | 'warn' | 'degrade' | 'block'\n```\n\n## Actions\n\n| Action | What happens |\n|--------|--------------|\n| `allow` | Call proceeds normally |\n| `warn` | Call proceeds, but threshold reached (log it) |\n| `degrade` | Call proceeds with cheaper model (`decision.suggestedModel`) |\n| `block` | Call rejected, return error to user |\n\n## Thresholds\n\n```\n0%────────80%──────95%──────100%\n  allow    │  warn  │degrade│ block\n```\n\nConfigurable via `warnThreshold` and `degradeThreshold`.\n\n## Credit-Based System\n\n```typescript\nconst guard = new SayayGuard({\n  storage: new MemoryStorage(),\n  budget: { credits: 50, creditsPerCall: 1 },\n  onExceeded: 'block',\n  warnThreshold: 80,\n});\n\nawait guard.record('user-123', 0, 1);\n\nconst usage = await guard.getUsage('user-123');\nconsole.log(`Credits used: ${usage.credits}/50`);\n```\n\n## Storage Adapters\n\nSayay needs a storage backend to track usage. Built-in: `MemoryStorage` (testing) and `DynamoStorage` (DynamoDB, optional AWS dependency).\n\nFor production, implement `SayayStorage`:\n\n```typescript\n// Cloudflare KV example:\nclass KVStorage implements SayayStorage {\n  constructor(private kv: KVNamespace) {}\n  async get(key: string) { return parseFloat(await this.kv.get(key) || '0'); }\n  async increment(key: string, amount: number, ttl?: number) {\n    const current = await this.get(key);\n    const newVal = current + amount;\n    await this.kv.put(key, String(newVal), ttl ? { expirationTtl: ttl } : undefined);\n    return newVal;\n  }\n  async reset(key: string) { await this.kv.delete(key); }\n}\n```\n\n### DynamoStorage (optional)\n\nReal-time token/cost ledger per customer/session in DynamoDB — survives Lambda warm\nstarts and is the \"Sayay = Cost Guardrail in Lambda + DynamoDB\" pattern. Requires\n`@aws-sdk/lib-dynamodb` + `@aws-sdk/client-dynamodb` (lazy-imported, so the package\nkeeps zero hard dependencies).\n\n```typescript\nimport { SayayGuard, DynamoStorage } from '@carloscortezcloud/sayay-guard';\n\n// Table: partition key `pk` (S), attribute `value` (N), TTL on `ttl` (N)\nconst guard = new SayayGuard({\n  storage: new DynamoStorage({ tableName: 'sayay-ledger', region: 'us-east-1' }),\n  budget: { dailyUsd: 10 },\n});\n```\n\n## Step Functions: TokenBudgetExceededException\n\nUse `checkOrThrow()` to raise a native exception when the budget is exhausted.\nIn AWS Step Functions, matching `ErrorEquals: [\"TokenBudgetExceededException\"]`\nin a Catch block instantly jumps to the error handler — stopping the workflow\nbefore retries rack up more cost.\n\n```typescript\nimport { SayayGuard, MemoryStorage } from '@carloscortezcloud/sayay-guard';\n\nconst guard = new SayayGuard({ storage: new MemoryStorage(), budget: { dailyUsd: 10 } });\n\n// Throws TokenBudgetExceededException on block; returns decision otherwise\nconst decision = await guard.checkOrThrow('user-42', 0.005);\n```\n\n```jsonc\n// ASL snippet\n\"Catch\": [\n  {\n    \"ErrorEquals\": [\"TokenBudgetExceededException\"],\n    \"Next\": \"HandleBudgetExceeded\"\n  }\n]\n```\n\n`TokenBudgetExceededException` extends `BudgetExceededError`, so existing\n`instanceof BudgetExceededError` checks keep working (backward compatible).\n\n## CloudWatch observability (optional)\n\nPass `cloudWatch` in config to emit a metric per decision. Requires\n`@aws-sdk/client-cloudwatch` (lazy-imported). Emits `Decision`, `RemainingBudget`,\nand `UsagePercent` metrics under the `Sayay` namespace (configurable).\n\n```typescript\nconst guard = new SayayGuard({\n  storage,\n  budget: { dailyUsd: 10 },\n  cloudWatch: { metricNamespace: 'MyApp', region: 'us-east-1' },\n});\n```\n\n## Qhaway observability (Sayay → Qhaway)\n\nPipe every budget decision (allow/warn/degrade/block) into [Qhaway](https://github.com/breakingthecloud/qhaway)\nas a `sayay.check` span. The Qhaway metrics pipeline then exposes\n`qhaway_sayay_decisions_total{action,user}` and the Grafana **\"Budget Guardrails\"**\npanel visualizes blocks vs degradations vs allows.\n\n```typescript\nimport { SayayGuard, MemoryStorage, SayayQhawayPlugin } from '@carloscortezcloud/sayay-guard';\nimport { MemoryStorage as QhawayStorage } from '@carloscortezcloud/qhaway';\n\nconst qhawayStorage = new QhawayStorage();\nconst qhaway = new SayayQhawayPlugin({\n  storage: qhawayStorage,\n  agentId: 'finops-agent',\n  sessionId: 'session-demo-001',\n  budgetKey: 'team-budget',\n});\n\nconst guard = new SayayGuard({\n  storage: new MemoryStorage(),\n  budget: { dailyUsd: 10 },\n  onDecision: qhaway.onDecision,\n});\n```\n\n`onDecision` writes a span with `tool_name: \"sayay.check\"`, `model: \"budget-guard\"`,\n`cost_usd: 0`, and `metadata: { action, budgetKey, spent, limit, usagePercent, suggestedModel }`.\nA failed write only logs — observability never breaks the guard path.\n\nWorks with any Tinkuy agent via its `guard` option (see\n[`examples/README.md`](examples/README.md) for the run guide and\n`examples/sayay-qhaway.ts` for the full wiring):\n\n```typescript\nconst agent = new Agent({ router, guard, tools: [...], systemPrompt: '...' });\n```\n\n## Integration with Styrr\n\n```typescript\nimport { StyrRouter } from 'styrr';\nimport { SayayGuard, MemoryStorage } from 'sayay';\n\nconst guard = new SayayGuard({ storage: new MemoryStorage(), budget: { dailyUsd: 10 } });\nconst router = new StyrRouter({ apiKey: '...', models: [...] });\n\nasync function safeLLMCall(userId: string, prompt: string) {\n  const decision = await guard.check(userId, 0.005);\n  if (decision.action === 'block') throw new Error(decision.reason);\n\n  const result = await router.prompt(prompt);\n  await guard.record(userId, result.usage?.totalTokens || 0.003);\n  return result;\n}\n```\n\n## Ecosystem\n\n| Package | Role | npm |\n|---------|------|-----|\n| **Sayay** | Cost guardrails (this) | GitHub |\n| **Styrr** | LLM router | `styrr` |\n| **Tinkuy** | Agent framework | `@carloscortezcloud/tinkuy-agent` |\n| **Qhaway** | Agent observability | `@carloscortezcloud/qhaway` |\n| **TideRAG** | Edge RAG pipeline | `@carloscortezcloud/tiderag` |\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n\n---\n\n<p align=\"center\">\n  Built by engineers who got tired of surprise AWS bills.<br>\n  <a href=\"https://github.com/breakingthecloud/tinkuylabs\">Tinkuy Labs</a> · <a href=\"https://finoptix.dev\">finoptix.dev</a>\n</p>\n<p align=\"center\">\n  <sub>Your AI costs should have a stop button. Sayay is that button.</sub>\n</p>\n","readmeFilename":""}