AI agents with human approval for risky tool calls
An agent in ShiftLabs is instructions, a model and tools. A tool is one of your workflows, or a function in the agent's own code. Mark a tool approval: "always" and every call to it waits for a person, who sees the exact arguments before anything runs.
Define an agent in code
// shiftlabs.config.ts
import { agent, file, project, workflowTool } from "shiftlabs";
export default project({
workflows: { "stripe-refund": "./workflows/stripe-refund.ts" },
agents: {
"invoice-assistant": agent({
title: "Invoice assistant",
instructions: file("./agents/invoice-assistant.md"),
model: "@cf/zai-org/glm-4.7-flash",
tools: [
workflowTool("stripe-refund", {
description: "Refund an invoice.",
approval: "always",
}),
],
budget: { conversationUsd: 0.5, monthlyUsd: 20 },
}),
},
});
npx shiftlabs deploy releases the agent with its workflows. Each deploy adds a version. See Define an agent.
How a call is held
- The model calls a tool marked
approval: "always". Nothing runs. - The call becomes a request in the workspace's approvals. The agent tells the person it is waiting.
- An approver answers in the dashboard, in Slack or with the API. An approval runs the exact arguments the model asked for.
- The agent hears the answer and replies.
| Answer | What happens |
|---|---|
| Approved | The workflow runs with the approved arguments. The agent reads its output or error. |
| Denied | The workflow doesn't run. The agent reads the approver's note. |
| Expired | Nobody answered within 72 hours. The workflow doesn't run. |
Approvers can be named per tool: up to 20 of role:admin, role:manager and members' emails. A tool can need 1 to 5 approvals. See Who approves.
Tools are your workflows
A workflow tool runs one of your workflows. The model's arguments, checked against the tool's parameters, become the run's payload. The run has durable steps, retries and its own record, like any run. A code tool runs a function in the agent's own code, with no network.
Tool results are data
A tool result can hold text anyone wrote, such as an email or a ticket. The model gets each result marked as data, never instructions. That makes prompt injection less likely, but no model is proof against it. Give every tool that changes something approval: "always", and give the agent only the tools it needs.
Credentials stay out of the model
A connection adds credentials at the gateway to requests a linked workflow sends to named hosts. The workflow's code and the model never hold the token.
Budgets and limits
conversationUsdcaps one conversation, $0.50 by default and $5 at most.monthlyUsdcaps all of an agent's conversations in a UTC month.
| Free | Pro | |
|---|---|---|
| Agents | 3, on small models | 25, on small and larger models |
| Agent messages | 1,000 per 24 h | 250,000 per 24 h |
| Model allowance | 50,000 tokens per 24 h, $1 a month | 5,000,000 tokens per 24 h, $10 a month |
| Tool calls per model turn | 10 | 50 |
The built-in helper
Every workspace with agents on has a helper. It answers questions about the platform, reads your workspace with your access, and drafts workflows and agents. Its tools only read. See The helper.
Start from a template
Five templates each deploy an agent and the workflows it calls. Every write to another service waits for an approver, except the Slack post to your own channel. Each runs on a small model, so every template works on Free.
github-triage: reads an issue, suggests labels and a reply, and posts them after approval.support-triage: sorts a support message, drafts a reply and posts the triage to Slack.github-actions-runbook: runs a runbook in your GitHub Actions after an approver says yes.