Runtime Controls wrap your functions with composable guardrails so agents can run autonomously without spiraling into infinite loops, duplicate writes, or runaway tool-call volume. No API key required — they work standalone around your own code, or combined with Buildfunctions hardware-isolated sandboxes.
#Install
npm install buildfunctions
yarn add buildfunctions
pnpm add buildfunctions
pip install buildfunctions
#Why Runtime Controls
When agents run unattended, things go wrong:
Infinite loops - the agent retries the same failing call forever
Runaway tool-call volume - unchecked tool calls can spike API usage quickly
Duplicate side effects - the same write runs twice
Unsafe actions - an agent executes destructive operations
Cascading failures - one broken dependency can fan out failures
Runtime Controls reduce these failure modes, but you still need to configure the right controls for your workflow. maxToolCalls is a call-count guardrail, not direct provider billing telemetry.
#Examples
#Wrap Any Async Call (No API Key)
import { RuntimeControls } from 'buildfunctions'
const controls = RuntimeControls.create({
maxToolCalls: 50,
timeoutMs: 30_000,
retry: { maxAttempts: 3, initialDelayMs: 200, backoffFactor: 2 },
loopBreaker: { warningThreshold: 5, quarantineThreshold: 8, stopThreshold: 12 },
onEvent: (event) => console.log(`[controls] ${event.type}: ${event.message}`),
})
const guardedFetch = controls.wrap({
toolName: 'api-call',
runKey: 'agent-run-1',
destination: 'https://api.example.com',
run: async ([payload]) => {
const res = await fetch('https://api.example.com/data', {
method: 'POST',
body: JSON.stringify(payload),
})
return res.json()
},
})
const result = await guardedFetch({ query: 'latest results' })
import httpx
from buildfunctions import RuntimeControls
controls = RuntimeControls.create({
"maxToolCalls": 50,
"timeoutMs": 30_000,
"retry": {"maxAttempts": 3, "initialDelayMs": 200, "backoffFactor": 2},
"loopBreaker": {"warningThreshold": 5, "quarantineThreshold": 8, "stopThreshold": 12},
"onEvent": lambda event: print(f"[controls] {event['type']}: {event['message']}"),
})
async def run_api(args, runtime):
_ = runtime
payload = args[0]
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.example.com/data",
json=payload,
timeout=30,
)
response.raise_for_status()
return response.json()
wrapped_fetch = controls.wrap({
"toolName": "api-call",
"runKey": "agent-run-1",
"destination": "https://api.example.com",
"run": run_api,
})
result = await wrapped_fetch({"query": "latest results"})
#Use run() Directly
import { RuntimeControls } from 'buildfunctions'
const controls = RuntimeControls.create({ retry: { maxAttempts: 2 } })
const result = await controls.run(
{ toolName: 'simple-tool' },
async () => 'ok'
)
from buildfunctions import RuntimeControls
controls = RuntimeControls.create({"retry": {"maxAttempts": 2}})
async def execute(runtime):
return "ok"
result = await controls.run({"toolName": "simple-tool"}, execute)
runtime["signal"] is available in handlers but optional. Use it when your tool call supports cancellation.
#Control Layers
Every control is opt-in and composable. Enable only what you need.
#Retry with Exponential Backoff
Automatically retries transient failures (503, 429, timeouts) with configurable backoff.
const controls = RuntimeControls.create({
retry: {
maxAttempts: 4,
initialDelayMs: 250,
maxDelayMs: 10_000,
backoffFactor: 2,
jitterRatio: 0.2,
},
})
controls = RuntimeControls.create({
"retry": {
"maxAttempts": 4,
"initialDelayMs": 250,
"maxDelayMs": 10_000,
"backoffFactor": 2,
"jitterRatio": 0.2,
}
})
Custom retry decisions:
const controls = RuntimeControls.create({
retry: { maxAttempts: 4 },
retryClassifier: ({ error, statusCode }) => {
if (statusCode === 409) {
return { retryable: true, delayMs: 500, reason: 'conflict_backoff' }
}
return { retryable: error.code === 'NETWORK_ERROR' }
},
})
def retry_classifier(ctx):
if ctx.get("statusCode") == 409:
return {"retryable": True, "delayMs": 500, "reason": "conflict_backoff"}
return {"retryable": ctx["error"]["code"] == "NETWORK_ERROR"}
controls = RuntimeControls.create({
"retry": {"maxAttempts": 4},
"retryClassifier": retry_classifier,
})
#Tool-Call Budget
Per-run cap on the number of tool calls when maxToolCalls is configured. Budgets are scoped by runKey. This is a tool-call-count budget, not a dollar-based cost meter.
const controls = RuntimeControls.create({
maxToolCalls: 50,
})
await controls.run({ toolName: 'shell', runKey: 'agent-run-1' }, async () => 'ok')
// Reset budget when starting a new run.
await controls.reset('agent-run-1')
controls = RuntimeControls.create({"maxToolCalls": 50})
async def execute_step(runtime):
_ = runtime
return await do_work()
await controls.run({"toolName": "shell", "runKey": "agent-run-1"}, execute_step)
await controls.reset("agent-run-1")
#Loop Breaker
Detects repeated no-progress patterns and escalates from warning to quarantine to stop.
const controls = RuntimeControls.create({
loopBreaker: {
enabled: true,
warningThreshold: 5,
quarantineThreshold: 8,
stopThreshold: 12,
quarantineMs: 15_000,
stopCooldownMs: 120_000,
},
})
controls = RuntimeControls.create({
"loopBreaker": {
"enabled": True,
"warningThreshold": 5,
"quarantineThreshold": 8,
"stopThreshold": 12,
"quarantineMs": 15_000,
"stopCooldownMs": 120_000,
}
})
#Circuit Breaker
Temporarily blocks calls to a failing dependency. Keyed by (tenant, toolName, destination).
const controls = RuntimeControls.create({
circuitBreaker: {
enabled: true,
windowMs: 30_000,
minRequests: 20,
failureRateThreshold: 0.6,
cooldownMs: 60_000,
},
})
controls = RuntimeControls.create({
"circuitBreaker": {
"enabled": True,
"windowMs": 30_000,
"minRequests": 20,
"failureRateThreshold": 0.6,
"cooldownMs": 60_000,
}
})
When failure rate exceeds threshold inside the sliding window, the circuit opens for cooldownMs.
#Timeout and Cancellation
Per-call timeout with abort signal propagation.
const controls = RuntimeControls.create({ timeoutMs: 30_000 })
const controller = new AbortController()
await controls.run(
{ toolName: 'shell', signal: controller.signal },
async ({ signal }) => {
return runCommand('npm test', { signal })
}
)
from buildfunctions import RuntimeControls, create_abort_controller
controls = RuntimeControls.create({"timeoutMs": 30_000})
controller = create_abort_controller()
async def execute(runtime):
signal = runtime["signal"]
return await run_command("npm test", signal=signal)
task = controls.run({"toolName": "shell", "signal": controller.signal}, execute)
# later if needed:
# controller.abort(Exception("cancelled"))
result = await task
#Policy Gates
Deterministic allow / deny / require_approval rules with specificity-based precedence.
const controls = RuntimeControls.create({
policy: {
mode: 'enforce',
rules: [
{
id: 'deny-destructive',
action: 'deny',
tools: ['repo-admin'],
actionPrefixes: ['delete'],
reason: 'Destructive repo operations are blocked',
},
{
id: 'approve-external-writes',
action: 'require_approval',
tools: ['ticket-write'],
destinations: ['*.external.example.com'],
reason: 'External writes require human approval',
},
],
approvalHandler: async () => false,
},
})
controls = RuntimeControls.create({
"policy": {
"mode": "enforce", # or "dryRun"
"rules": [
{
"id": "deny-destructive",
"action": "deny",
"tools": ["repo-admin"],
"actionPrefixes": ["delete"],
"reason": "Destructive repo operations are blocked",
},
{
"id": "approve-external-writes",
"action": "require_approval",
"tools": ["ticket-write"],
"destinations": ["*.external.example.com"],
"reason": "External writes require human approval",
},
],
"approvalHandler": lambda ctx: False,
}
})
Policy precedence (highest wins):
Higher tool specificity (exact name > prefix > wildcard)
Higher destination specificity (exact host > wildcard subdomain >
*)Longer action prefix match
Stricter action (
deny>require_approval>allow)Earlier rule index on exact tie (current behavior)
#Idempotency
Prevents duplicate side effects by replaying prior results for calls with the same idempotencyKey.
const controls = RuntimeControls.create({
idempotency: {
enabled: true,
ttlMs: 600_000,
includeErrors: false,
namespaceByRunKey: true,
},
})
await controls.run(
{
toolName: 'pr-comment',
runKey: 'pr-4821',
idempotencyKey: 'comment:pr-4821:summary',
},
async () => postComment('LGTM')
)
await controls.run(
{
toolName: 'pr-comment',
runKey: 'pr-4821',
idempotencyKey: 'comment:pr-4821:summary',
},
async () => postComment('LGTM') // replayed, does not execute again
)
controls = RuntimeControls.create({
"idempotency": {
"enabled": True,
"ttlMs": 600_000,
"includeErrors": False,
"namespaceByRunKey": True,
}
})
context = {
"toolName": "pr-comment",
"runKey": "pr-4821",
"idempotencyKey": "comment:pr-4821:summary",
}
async def post_summary(runtime):
_ = runtime
return await post_comment("LGTM")
await controls.run(context, post_summary)
await controls.run(context, post_summary) # replayed, does not execute again
#Concurrency Locks
Prevents simultaneous conflicting writes to the same resource.
const controls = RuntimeControls.create({
concurrency: {
enabled: true,
waitMode: 'reject', // or 'wait'
leaseMs: 30_000,
waitTimeoutMs: 5_000,
},
})
await controls.run(
{
toolName: 'repo-write',
resourceKey: 'repo:buildfunctions/web-app',
},
async () => gitPush('main')
)
controls = RuntimeControls.create({
"concurrency": {
"enabled": True,
"waitMode": "reject", # or "wait"
"leaseMs": 30_000,
"waitTimeoutMs": 5_000,
"pollIntervalMs": 50,
}
})
async def push_main(runtime):
_ = runtime
return await git_push("main")
await controls.run(
{"toolName": "repo-write", "resourceKey": "repo:buildfunctions/web-app"},
push_main,
)
#Verifier Hooks
Custom correctness gates that run before execution, after success, and after errors.
const controls = RuntimeControls.create({
verifiers: {
beforeCall: ({ toolName, action }) => {
if (toolName === 'repo-admin' && action?.startsWith('delete')) {
return { allow: false, reason: 'delete blocked' }
}
return { allow: true }
},
afterSuccess: ({ result }) => {
if (!result || typeof result !== 'object') {
return { allow: false, reason: 'unexpected result shape' }
}
return { allow: true }
},
afterError: ({ error }) => {
return { allow: true }
},
},
})
def before_call(ctx):
if ctx["toolName"] == "repo-admin" and str(ctx.get("action", "")).startswith("delete"):
return {"allow": False, "reason": "delete blocked"}
return {"allow": True}
def after_success(ctx):
result = ctx.get("result")
if not isinstance(result, dict):
return {"allow": False, "reason": "unexpected result shape"}
return {"allow": True}
controls = RuntimeControls.create({
"verifiers": {
"beforeCall": before_call,
"afterSuccess": after_success,
}
})
#Per-Tool and Per-Destination Overrides
Apply stricter or looser controls to specific tools or destinations.
const controls = RuntimeControls.create({
timeoutMs: 60_000,
retry: { maxAttempts: 4 },
overrides: {
tools: {
shell: { timeoutMs: 10_000, retry: { maxAttempts: 2 } },
},
destinations: {
'*.internal-api.example.com': { timeoutMs: 5_000 },
},
},
})
controls = RuntimeControls.create({
"timeoutMs": 60_000,
"retry": {"maxAttempts": 4},
"overrides": {
"tools": {
"shell": {"timeoutMs": 10_000, "retry": {"maxAttempts": 2}},
},
"destinations": {
"*.internal-api.example.com": {"timeoutMs": 5_000},
},
},
})
Tool overrides win when both tool and destination set the same field.
#Agent Logic Safety
applyAgentLogicSafety adds pre-execution safety checks without changing RuntimeControls internals.
#Injection Guard
Scans tool name, action, destination, and args for injection patterns before execution.
import { RuntimeControls, applyAgentLogicSafety } from 'buildfunctions'
const controls = RuntimeControls.create(
applyAgentLogicSafety(
{ retry: { maxAttempts: 2 } },
{
injectionGuard: {
enabled: true,
patterns: [
/ignore\s+previous\s+instructions/i,
/\brm\s+-rf\b/i,
/<script\b/i,
],
},
}
)
)
import re
from buildfunctions import RuntimeControls, applyAgentLogicSafety
controls = RuntimeControls.create(
applyAgentLogicSafety(
{"retry": {"maxAttempts": 2}},
{
"injectionGuard": {
"enabled": True,
"patterns": [
re.compile(r"ignore\s+previous\s+instructions", re.I),
re.compile(r"\brm\s+-rf\b", re.I),
re.compile(r"<script\b", re.I),
],
},
},
)
)
Default patterns (when patterns is omitted):
ignore (all|any|previous) instructionssystem promptdeveloper message<scriptrm -rf
#Exit Conditions
Enforces maximum steps per run and can block calls after a terminal action.
const controls = RuntimeControls.create(
applyAgentLogicSafety(
{},
{
exitCondition: {
enabled: true,
maxStepsPerRun: 30,
terminalActions: [{ toolNamePattern: 'agent-control', actionPrefix: 'finish' }],
blockAfterTerminal: true,
},
}
)
)
from buildfunctions import RuntimeControls, applyAgentLogicSafety
controls = RuntimeControls.create(
applyAgentLogicSafety(
{},
{
"exitCondition": {
"enabled": True,
"maxStepsPerRun": 30,
"terminalActions": [{"toolNamePattern": "agent-control", "actionPrefix": "finish"}],
"blockAfterTerminal": True,
},
},
)
)
#Intent Allowlist
Restricts which tool + action combinations are permitted. Anything not explicitly allowed is denied.
const controls = RuntimeControls.create(
applyAgentLogicSafety(
{},
{
intentAllowlist: {
enabled: true,
rules: [
{ toolNamePattern: 'cpu-sandbox', actionPrefixes: ['run_'] },
{ toolNamePattern: 'repo-write', actionPrefixes: ['push_'] },
{ toolNamePattern: 'pr-comment', actionPrefixes: ['post_'] },
],
},
}
)
)
from buildfunctions import RuntimeControls, applyAgentLogicSafety
controls = RuntimeControls.create(
applyAgentLogicSafety(
{},
{
"intentAllowlist": {
"enabled": True,
"rules": [
{"toolNamePattern": "cpu-sandbox", "actionPrefixes": ["run_"]},
{"toolNamePattern": "repo-write", "actionPrefixes": ["push_"]},
{"toolNamePattern": "pr-comment", "actionPrefixes": ["post_"]},
],
},
},
)
)
#Combining All Three
const controls = RuntimeControls.create(
applyAgentLogicSafety(
{
tenantKey: 'my-team-prod',
maxToolCalls: 100,
retry: { maxAttempts: 3 },
concurrency: { enabled: true, waitMode: 'reject', leaseMs: 30_000 },
},
{
injectionGuard: { enabled: true },
exitCondition: {
enabled: true,
maxStepsPerRun: 50,
terminalActions: [{ toolNamePattern: 'agent-control', actionPrefix: 'finish' }],
blockAfterTerminal: true,
},
intentAllowlist: {
enabled: true,
rules: [
{ toolNamePattern: 'cpu-sandbox', actionPrefixes: ['run_'] },
{ toolNamePattern: 'repo-write', actionPrefixes: ['push_'] },
{ toolNamePattern: 'pr-comment', actionPrefixes: ['post_'] },
],
},
}
)
)
import re
from buildfunctions import RuntimeControls, applyAgentLogicSafety
controls = RuntimeControls.create(
applyAgentLogicSafety(
{
"tenantKey": "my-team-prod",
"maxToolCalls": 100,
"retry": {"maxAttempts": 3},
"concurrency": {"enabled": True, "waitMode": "reject", "leaseMs": 30_000},
},
{
"injectionGuard": {
"enabled": True,
"patterns": [
re.compile(r"ignore\s+previous\s+instructions", re.I),
re.compile(r"\brm\s+-rf\b", re.I),
],
},
"exitCondition": {
"enabled": True,
"maxStepsPerRun": 50,
"terminalActions": [{"toolNamePattern": "agent-control", "actionPrefix": "finish"}],
"blockAfterTerminal": True,
},
"intentAllowlist": {
"enabled": True,
"rules": [
{"toolNamePattern": "cpu-sandbox", "actionPrefixes": ["run_"]},
{"toolNamePattern": "repo-write", "actionPrefixes": ["push_"]},
{"toolNamePattern": "pr-comment", "actionPrefixes": ["post_"]},
],
},
},
)
)
#Observability
#Event Callback
const controls = RuntimeControls.create({
onEvent: (event) => {
console.log(`[${event.type}] ${event.message}`, event.details)
},
})
controls = RuntimeControls.create({
"onEvent": lambda event: print(f"[{event['type']}] {event['message']} {event.get('details')}")
})
#Event Sinks
const controls = RuntimeControls.create({
eventSinks: [
async (event) => await writeToDatabase(event),
async (event) => await sendToMonitoring(event),
],
onEventSinkFailure: ({ failure, sinkIndex }) => {
console.error(`Event sink ${sinkIndex} failed:`, failure)
},
})
async def sink_a(event):
await write_to_database(event)
async def sink_b(event):
await send_to_monitoring(event)
controls = RuntimeControls.create({
"eventSinks": [sink_a, sink_b],
"onEventSinkFailure": lambda params: print(f"sink {params['sinkIndex']} failed: {params['failure']}"),
})
#Event Types
| Event | Meaning |
|---|---|
retry | A retry attempt was scheduled |
loop_warning | Repeated no-progress pattern detected |
loop_quarantine | Pattern quarantined (temporarily blocked) |
loop_stop | Pattern hard-stopped |
circuit_open | Dependency circuit breaker opened |
budget_stop | Run tool-call budget exceeded |
policy_denied | Policy denied a call |
policy_approval_required | Approval required by policy |
policy_approved | Approval granted by handler |
policy_dry_run | Simulated policy decision (dry-run mode) |
verifier_rejected | Verifier rejected call, result, or error |
idempotency_replay | Prior result replayed |
concurrency_wait | Waiting for resource lock |
concurrency_rejected | Lock rejected or wait timed out |
#State Adapters
By default, state is in-memory. To persist guardrail state across processes, provide adapters.
const stateAdapter = {
get: async (key) => await store.get(key),
set: async (key, value) => await store.set(key, value),
delete: async (key) => await store.delete(key),
keys: async (prefix) => await store.keys(prefix),
}
const controls = RuntimeControls.create({
state: {
budget: stateAdapter,
circuit: stateAdapter,
loop: stateAdapter,
lock: stateAdapter,
idempotency: stateAdapter,
},
})
async def adapter_get(key: str):
return await store.get(key)
async def adapter_set(key: str, value):
await store.set(key, value)
async def adapter_delete(key: str):
await store.delete(key)
async def adapter_keys(prefix: str):
return await store.keys(prefix)
adapter = {
"get": adapter_get,
"set": adapter_set,
"delete": adapter_delete,
"keys": adapter_keys,
}
controls = RuntimeControls.create({
"state": {
"budget": adapter,
"circuit": adapter,
"loop": adapter,
"lock": adapter,
"idempotency": adapter,
}
})
State is namespaced by tenantKey, so teams can scope keys when sharing adapter backends. Unit tests cover adapter contracts; validate your backend and lock semantics with integration tests before production use.
#API Methods
#RuntimeControls.create(config?)
Creates a new controls instance. All config is optional.
#controls.run(context, fn)
Runs fn(runtime) through enabled control layers.
Context fields:
toolName(required)runKey,destination,actionargsidempotencyKey,resourceKeytimeoutMs(per-call override)signal(caller cancellation)
const result = await controls.run(
{
toolName: 'shell',
runKey: 'agent-run-42',
destination: 'sandbox.example.com',
action: 'run_tests',
args: { command: 'npm test' },
idempotencyKey: 'test:42:v1',
resourceKey: 'workspace:/repo',
signal: controller.signal,
timeoutMs: 10_000,
},
async ({ signal }) => {
return runShell('npm test', { signal })
}
)
async def execute(runtime):
signal = runtime["signal"]
return await run_shell("npm test", signal=signal)
result = await controls.run(
{
"toolName": "shell",
"runKey": "agent-run-42",
"destination": "sandbox.example.com",
"action": "run_tests",
"args": {"command": "npm test"},
"idempotencyKey": "test:42:v1",
"resourceKey": "workspace:/repo",
"signal": controller.signal,
"timeoutMs": 10_000,
},
execute,
)
#controls.wrap(params)
Creates a reusable guarded function for repeated tool calls.
const guardedShell = controls.wrap({
toolName: 'shell',
runKey: 'agent-run-42',
destination: 'sandbox.example.com',
run: async ([command], { signal }) => {
return runShell(command, { signal })
},
})
await guardedShell('npm test')
await guardedShell('npm run lint')
async def run_shell_guarded(args, runtime):
command = args[0]
signal = runtime["signal"]
return await run_shell(command, signal=signal)
guarded_shell = controls.wrap({
"toolName": "shell",
"runKey": "agent-run-42",
"destination": "sandbox.example.com",
"run": run_shell_guarded,
})
await guarded_shell("npm test")
await guarded_shell("npm run lint")
Dynamic context via resolver functions:
const guardedShell = controls.wrap({
toolName: 'shell',
run: async ([command], { signal }) => runShell(command, { signal }),
resolveRunKey: (command) => `run-${command}`,
resolveDestination: () => 'sandbox.example.com',
resolveAction: (command) => (command.startsWith('rm') ? 'delete' : 'execute'),
resolveIdempotencyKey: (command) => `shell:${command}`,
resolveResourceKey: () => 'workspace:/repo',
})
async def run_shell_dynamic(args, runtime):
command = args[0]
signal = runtime["signal"]
return await run_shell(command, signal=signal)
guarded_shell = controls.wrap({
"toolName": "shell",
"run": run_shell_dynamic,
"resolveRunKey": lambda command: f"run-{command}",
"resolveDestination": lambda command: "sandbox.example.com",
"resolveAction": lambda command: "delete" if str(command).startswith("rm") else "execute",
"resolveIdempotencyKey": lambda command: f"shell:{command}",
"resolveResourceKey": lambda command: "workspace:/repo",
})
#controls.reset(runKey=None)
Resets budget counters for the selected run key.
await controls.reset('agent-run-42')
await controls.reset("agent-run-42")
#Full Configuration Reference
| Config | Default | Description |
|---|---|---|
tenantKey | "default" | Namespace for all state keys |
timeoutMs | 60000 | Per-call timeout (0 disables) |
maxToolCalls | unset | Per-runKey call budget |
retry.maxAttempts | 4 | Total attempts including first |
retry.initialDelayMs | 250 | Backoff base delay |
retry.maxDelayMs | 10000 | Backoff cap |
retry.backoffFactor | 2 | Exponential multiplier |
retry.jitterRatio | 0.2 | Delay jitter (0..1) |
retryClassifier | unset | Custom retry decision callback |
loopBreaker.enabled | true | Enables repeated-pattern detection |
loopBreaker.warningThreshold | 5 | Emits loop_warning |
loopBreaker.quarantineThreshold | 8 | Emits loop_quarantine, blocks for quarantineMs |
loopBreaker.stopThreshold | 12 | Emits loop_stop, blocks for stopCooldownMs |
loopBreaker.quarantineMs | 15000 | Quarantine duration |
loopBreaker.stopCooldownMs | 120000 | Stop duration |
loopBreaker.maxFingerprints | 200 | Max fingerprints retained |
circuitBreaker.enabled | true | Enables dependency circuit protection |
circuitBreaker.windowMs | 30000 | Sliding failure window |
circuitBreaker.minRequests | 20 | Requests before open decision |
circuitBreaker.failureRateThreshold | 0.6 | Open when failure rate exceeds this |
circuitBreaker.cooldownMs | 60000 | Open duration |
policy.enabled | true | Enables rule enforcement |
policy.mode | "enforce" | "dryRun" emits simulation events only |
policy.rules | [] | Array of allow/deny/require_approval rules |
policy.approvalHandler | unset | Required for require_approval rules |
verifiers.beforeCall | unset | Pre-execution verifier |
verifiers.afterSuccess | unset | Post-success verifier |
verifiers.afterError | unset | Post-error verifier |
idempotency.enabled | true | Enables replay by idempotencyKey |
idempotency.ttlMs | unset | Replay record expiration |
idempotency.includeErrors | false | Replay final errors when enabled |
idempotency.namespaceByRunKey | true | Scope replay by run key |
concurrency.enabled | false | Enables locking by resourceKey |
concurrency.leaseMs | 30000 | Lock lease duration |
concurrency.waitMode | "reject" | "reject" immediately or "wait" |
concurrency.waitTimeoutMs | 5000 | Wait timeout |
concurrency.pollIntervalMs | 50 | Poll interval in wait mode |
overrides.tools | unset | Per-tool config overrides |
overrides.destinations | unset | Per-destination config overrides |
state.* | in-memory | State adapters (budget, circuit, loop, lock, idempotency) |
onEvent | unset | Synchronous event callback |
eventSinks | [] | Async event fan-out sinks |
onEventSinkFailure | unset | Sink failure callback |
#FAQ
#Do I need a Buildfunctions API token?
No. Runtime Controls works standalone without any API token.
#What should I wrap first?
Start with side-effecting calls:
Repository writes (git push, branch operations)
External ticket or issue creation
Sandbox and test execution
PR or issue comments
#What should be denied by default?
Start by denying destructive actions (delete*) and requiring approval for external writes.
#Can I use this without agents?
Yes. Runtime Controls wrap functions — API calls, database queries, file operations, shell commands. Validate behavior in your own integration paths.