Confirmation-gated tool
A dangerous tool the runtime refuses to execute without explicit human approval.
A tool that deletes files. The agent can call it. The runtime pauses for human approval before anything happens. No timeout-based auto-approval β security-critical by design.
#Install
npm install @agentskit/runtime @agentskit/adapters#The tool
import type { ToolDefinition } from '@agentskit/core'
import { unlink } from 'node:fs/promises'
export const deleteFile: ToolDefinition = {
name: 'delete_file',
description: 'Permanently delete a file.',
schema: {
type: 'object',
properties: {
path: { type: 'string', description: 'Absolute or relative path.' },
},
required: ['path'],
},
requiresConfirmation: true, // β the gate
async execute(args) {
await unlink(args.path as string)
return { ok: true }
},
}#The runtime with onConfirm
import { createRuntime } from '@agentskit/runtime'
import { anthropic } from '@agentskit/adapters'
import { deleteFile } from './delete-file-tool'
import { createInterface } from 'node:readline/promises'
const rl = createInterface({ input: process.stdin, output: process.stdout })
const runtime = createRuntime({
adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }),
tools: [deleteFile],
onConfirm: async (call) => {
const args = JSON.stringify(call.args)
const answer = await rl.question(
`\nβ Approve "${call.name}(${args})"? [y/N] `,
)
return answer.trim().toLowerCase() === 'y'
},
})
const result = await runtime.run('Delete the file ./scratch.txt')
console.log(result.content)
rl.close()#Run it
npx tsx agent.ts
# β Approve "delete_file({"path":"./scratch.txt"})"? [y/N] y
# Done. ./scratch.txt has been deleted.If you answer n, the runtime feeds a refusal back to the model as a tool error, and the agent decides what to do next (typically: explain why it stopped).
#What's enforced by the contract
| Behavior | Where it's defined |
|---|---|
requiresConfirmation: true exists | Tool contract T9 |
Runtime MUST call onConfirm first | Runtime contract RT6 |
If onConfirm is absent, execution is REFUSED (not allowed) | Runtime contract RT6 |
| No timeout-based auto-approval | Tool T9 + Runtime RT6 (non-negotiable) |
This means a tool author can mark a tool dangerous and trust the runtime to gate it. No "but what if the user forgets to wire onConfirm?" β the runtime refuses to execute, period.
#Tighten the recipe
- Web UI instead of stdin β
onConfirmreturns a Promise that resolves when the user clicks - Slack / Discord approval β post a message with β/β buttons; resolve on click
- Per-tool policies β a wrapper that auto-approves
readoperations, requires approval forwriteoperations, and always refusesdelete - Audit log β wrap
onConfirmto log every approval/refusal with the args
#Related
- Concepts: Tool
- Concepts: Runtime β RT6 confirmation
Explore nearby
- PeerRecipes
Copy-paste solutions grouped by theme. Every recipe end-to-end, runs as written.
- PeerCustom adapter
Wrap any LLM API as an AgentsKit adapter. Plug-and-play with the rest of the kit in 30 lines.
- PeerAdapter contract tests
Verify any adapter against the ADR 0001 invariants A1βA10 with the shared test harness.