Integrate @keelstack/guard for idempotent tool calls, budget controls, and approval gates - #1
Merged
Conversation
Copilot created this pull request from a session on behalf of
siddhant-jain-18
April 22, 2026 08:54
View session
siddhant-jain-18
marked this pull request as ready for review
April 22, 2026 09:03
|
There was a problem hiding this comment.
Pull request overview
Integrates @keelstack/guard into the example tool-execution path to add idempotent tool calls (replay on duplicates), optional per-user budget enforcement, and risk/approval gating, plus a demo script and an example test.
Changes:
- Added
@keelstack/guard@0.1.0to example packages and lockfile. - Wrapped
examples/next-agentweather tool execution with Guard (idempotency key, optional budget/risk policy env config, and blocked-call mapping). - Added Guard loader shims (export-map workaround), a runnable demo (
examples/with-guard.ts), and a Vitest idempotency test in@example/ai-functions.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds @keelstack/guard@0.1.0 resolution and importer entries. |
| packages/ai/README.md | Rewrites the published ai package README to describe the fork and Guard usage. |
| examples/with-guard.ts | Adds a demo showing executed vs replayed Guard behavior for a side-effecting tool. |
| examples/next-agent/tool/weather-tool.ts | Wraps weather tool execution with Guard (idempotency key + optional budget/risk gating). |
| examples/next-agent/package.json | Adds @keelstack/guard dependency for the Next.js example. |
| examples/next-agent/lib/keelstack-guard.ts | Adds a temporary createRequire shim to load Guard due to export-map mismatch. |
| examples/ai-functions/src/with-guard.test.ts | Adds a test asserting duplicate tool calls replay cached results. |
| examples/ai-functions/src/keelstack-guard.ts | Adds the same Guard loader shim for the ai-functions example. |
| examples/ai-functions/package.json | Adds @keelstack/guard dependency for the ai-functions example. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
examples/ai-functions/package.json:55
- @keelstack/guard@0.1.0 requires Node >=20 (per its
engines), but this example doesn’t declare an engine requirement and the monorepo supports Node 18. This can lead to runtime failures for users running examples on Node 18. Consider enforcingengines: { node: ">=20" }for this example and/or selecting a guard version that supports Node 18.
"@ai-sdk/voyage": "workspace:*",
"@ai-sdk/xai": "workspace:*",
"@google/generative-ai": "0.21.0",
"@keelstack/guard": "0.1.0",
"@langfuse/otel": "^4.5.0",
"@opentelemetry/auto-instrumentations-node": "0.54.0",
"@opentelemetry/sdk-node": "^0.210.0",
"@opentelemetry/sdk-trace-node": "^2.5.0",
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1
to
+25
| import { createRequire } from 'node:module'; | ||
|
|
||
| // TODO(keelstack-guard>=0.1.1): remove this shim once package exports include dist/index.mjs. | ||
| const require = createRequire(import.meta.url); | ||
| const guardModule = require('@keelstack/guard') as { | ||
| guard: <T>(options: { | ||
| key: string; | ||
| action: () => Promise<T>; | ||
| ttlMs?: number; | ||
| budget?: unknown; | ||
| extractCost?: (result: T) => number; | ||
| risk?: unknown; | ||
| ledger?: unknown; | ||
| budgetStore?: unknown; | ||
| }) => Promise<{ | ||
| status: 'executed' | 'replayed' | 'blocked:budget' | 'blocked:risk'; | ||
| value?: T; | ||
| fromCache: boolean; | ||
| replayCount: number; | ||
| budgetInfo?: unknown; | ||
| riskInfo?: unknown; | ||
| }>; | ||
| }; | ||
|
|
||
| export const guard = guardModule.guard; |
Comment on lines
+1
to
+25
| import { createRequire } from 'node:module'; | ||
|
|
||
| // TODO(keelstack-guard>=0.1.1): remove this shim once package exports include dist/index.mjs. | ||
| const require = createRequire(import.meta.url); | ||
| const guardModule = require('@keelstack/guard') as { | ||
| guard: <T>(options: { | ||
| key: string; | ||
| action: () => Promise<T>; | ||
| ttlMs?: number; | ||
| budget?: unknown; | ||
| extractCost?: (result: T) => number; | ||
| risk?: unknown; | ||
| ledger?: unknown; | ||
| budgetStore?: unknown; | ||
| }) => Promise<{ | ||
| status: 'executed' | 'replayed' | 'blocked:budget' | 'blocked:risk'; | ||
| value?: T; | ||
| fromCache: boolean; | ||
| replayCount: number; | ||
| budgetInfo?: unknown; | ||
| riskInfo?: unknown; | ||
| }>; | ||
| }; | ||
|
|
||
| export const guard = guardModule.guard; |
Comment on lines
12
to
15
| "@ai-sdk/openai": "4.0.0-beta.38", | ||
| "@ai-sdk/react": "4.0.0-beta.111", | ||
| "@keelstack/guard": "0.1.0", | ||
| "@vercel/blob": "^0.26.0", |
| # AI SDK | ||
| [](https://www.npmjs.com/package/@keelstack/guard) | ||
| [](https://github.com/KeelStack-me/ai-with-guard) | ||
| [](./LICENSE) |
Comment on lines
+1
to
+9
| # AI SDK + KeelStack Guard – Production-ready duplicate prevention | ||
|
|
||
| # AI SDK | ||
| [](https://www.npmjs.com/package/@keelstack/guard) | ||
| [](https://github.com/KeelStack-me/ai-with-guard) | ||
| [](./LICENSE) | ||
|
|
||
| The [AI SDK](https://ai-sdk.dev/docs) is a provider-agnostic TypeScript toolkit designed to help you build AI-powered applications and agents using popular UI frameworks like Next.js, React, Svelte, Vue, Angular, and runtimes like Node.js. | ||
| Duplicate tool calls cost money and cause errors. This fork adds idempotency, budgets, and approval gates using `@keelstack/guard`. | ||
|
|
||
| To learn more about how to use the AI SDK, check out our [API Reference](https://ai-sdk.dev/docs/reference) and [Documentation](https://ai-sdk.dev/docs). | ||
| ## Why this fork exists |
Comment on lines
+76
to
+87
| yield { | ||
| state: 'ready' as const, | ||
| temperature: null, | ||
| weather: null, | ||
| error: | ||
| weatherResult.status === 'blocked:budget' | ||
| ? 'budgetExceeded' | ||
| : 'riskPolicyViolation', | ||
| guardStatus: weatherResult.status, | ||
| fromCache: weatherResult.fromCache, | ||
| }; | ||
| return; |
Comment on lines
+9
to
+12
| describe('guard idempotency', () => { | ||
| it('returns cached result on duplicate tool call', async () => { | ||
| let executions = 0; | ||
| const args = { city: 'Berlin' }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Background
This fork is intended to be production-ready for “ai with guardrails” by adding concrete runtime guardrails where tool side effects occur. The main gap was duplicate tool execution risk (cost + correctness), plus missing budget and approval controls.
Summary
Guarded tool execution (core path)
@keelstack/guardintoexamples/next-agent/tool/weather-tool.ts.Budget limits + approval gates
GUARD_BUDGET_LIMIT_USD.GUARD_APPROVAL_POLICYandGUARD_RISK_LEVEL.budgetExceeded/riskPolicyViolation) with guard status.Examples + test coverage
examples/with-guard.tsto show first-call execute + second-call replay (cached), with one side effect.examples/ai-functions/src/with-guard.test.tsto assert duplicate-call replay behavior.Docs rewrite for fork discoverability
Compatibility shim
@keelstack/guard@0.1.0export-map mismatch, with TODO markers for removal once upstream export is fixed.Manual Verification
Executed the new guard demo (
examples/with-guard.ts) and confirmed:executedwithfromCache: falsereplayedwithfromCache: trueChecklist
pnpm changesetin the project root)Future Work
@keelstack/guardpublishes a corrected ESM export target.Related Issues
N/A