Skip to content

Integrate @keelstack/guard for idempotent tool calls, budget controls, and approval gates - #1

Merged
siddhant-jain-18 merged 6 commits into
mainfrom
copilot/integrate-keelstack-guard
Apr 22, 2026
Merged

siddhant-jain-18 merged 6 commits into
mainfrom
copilot/integrate-keelstack-guard

Conversation

Copilot AI commented Apr 22, 2026

Copy link
Copy Markdown

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)

    • Integrated @keelstack/guard into examples/next-agent/tool/weather-tool.ts.
    • Added stable idempotency key construction with hashed arguments:
    const idempotencyKey = `tool:${toolName}:${userId}:${createHash('sha256')
      .update(JSON.stringify(toolArgs))
      .digest('hex')}`;
  • Budget limits + approval gates

    • Added optional budget enforcement via GUARD_BUDGET_LIMIT_USD.
    • Added approval/risk gate controls via GUARD_APPROVAL_POLICY and GUARD_RISK_LEVEL.
    • Blocked calls now return explicit reason mapping (budgetExceeded / riskPolicyViolation) with guard status.
  • Examples + test coverage

    • Added examples/with-guard.ts to show first-call execute + second-call replay (cached), with one side effect.
    • Added examples/ai-functions/src/with-guard.test.ts to assert duplicate-call replay behavior.
  • Docs rewrite for fork discoverability

    • Reworked README content (root README target) to focus on:
      • production duplicate prevention
      • quick-start guard usage
      • original vs fork comparison
      • attribution and contribution routing
  • Compatibility shim

    • Added temporary loader shim files for @keelstack/guard@0.1.0 export-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:

  • first invocation returns executed with fromCache: false
  • second identical invocation returns replayed with fromCache: true
  • side effect executes exactly once

Checklist

  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Future Work

  • Replace local shim once @keelstack/guard publishes a corrected ESM export target.
  • Remove visible SEO keyword block from README and move keywords into metadata channels better aligned with repo conventions.
  • Consolidate duplicated shim logic into a shared internal helper.

Related Issues

N/A

@siddhant-jain-18
siddhant-jain-18 marked this pull request as ready for review April 22, 2026 09:03
Copilot AI review requested due to automatic review settings April 22, 2026 09:03
@siddhant-jain-18
siddhant-jain-18 merged commit f921ebc into main Apr 22, 2026
18 checks passed
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
15.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.0 to example packages and lockfile.
  • Wrapped examples/next-agent weather 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 enforcing engines: { 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",
Comment thread packages/ai/README.md
# AI SDK
[![npm version](https://img.shields.io/npm/v/@keelstack/guard)](https://www.npmjs.com/package/@keelstack/guard)
[![GitHub stars](https://img.shields.io/github/stars/KeelStack-me/ai-with-guard)](https://github.com/KeelStack-me/ai-with-guard)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
Comment thread packages/ai/README.md
Comment on lines +1 to +9
# AI SDK + KeelStack Guard – Production-ready duplicate prevention

# AI SDK
[![npm version](https://img.shields.io/npm/v/@keelstack/guard)](https://www.npmjs.com/package/@keelstack/guard)
[![GitHub stars](https://img.shields.io/github/stars/KeelStack-me/ai-with-guard)](https://github.com/KeelStack-me/ai-with-guard)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./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' };
@siddhant-jain-18
siddhant-jain-18 deleted the copilot/integrate-keelstack-guard branch April 22, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants