Human Oversight
How human reviewers approve, reject, and manage agent actions
Every autonomous system needs a circuit breaker. Delta provides a structured human-in-the-loop layer where the engine pauses, informs, and waits for a human decision before allowing sensitive actions to proceed or recovering from failures the agent cannot resolve alone.
How it works
The engine never assumes perfect autonomy. When an agent reaches a governance boundary it cannot cross alone (an action that requires approval, a budget violation, a persistent API failure), the engine pauses the task, records the reason, and waits. The task stays blocked until a human reviews the situation and decides what to do next.
// Subscribe to approval requests as they happen
delta.events.on("approval-requested", ({ taskId, action, approvalId, reason }) => {
console.log(`Task ${taskId}: ${action} needs sign-off (${reason})`);
// Forward to Slack, email, dashboard
});
// List pending approvals for a task
const { pendingApprovals } = await delta.inspect(taskId);
// Approve and resume
await delta.approve(approvalId);
await delta.resume(taskId);
// Reject with optional reason
await delta.reject(approvalId, "Use the standard refund flow instead");Approval gates
An action that declares requiresApproval: true blocks at the governance gateway until a human approves it. The task stays paused with a persistent ApprovalRequest that the engine re-checks on every resume attempt.
const shipRefund = delta.action({
name: "issue-refund",
description: "Issue a refund to a customer",
requiresApproval: true,
fn: async (input, ctx) => { /* ... */ },
});Trust-based waiver
Actions that need human oversight by default can waive it once the agent has earned enough trust. requiresApproval: { untilTrust: 0.7 } means the action requires approval only until the task's trust score reaches 0.7. After that it auto-approves with an auditable record.
const action = delta.action({
name: "cancel-subscription",
description: "Cancel a customer subscription",
requiresApproval: { untilTrust: 0.8 },
fn: async (input, ctx) => { /* ... */ },
});A rejection is always final. Even with a trust waiver, a human-rejected action stays blocked. The waiver only governs the initial approval gate, not overrides.
Approval lifecycle
- The engine creates an
ApprovalRequestinpendingstatus - The task transitions to
blocked(persisted aspaused) - The engine emits
approval-requested. Subscribe viadelta.events.on() - A human calls
delta.approve()ordelta.reject() - The engine emits
approval-resolvedwith the decision - After approval, call
delta.resume(taskId)to continue
Escalations
The engine escalates to a human when it hits a boundary it cannot resolve alone. Escalation is not an error. It is a designed recovery path.
When escalation happens, the engine:
- Saves an escalation record with the trigger and reason
- Pauses the task so no further work runs
- Emits
escalation-raisedfor real-time awareness
Escalation triggers
- Reasoner failure: the model API fails after all retries are exhausted. The problem is likely transient, so the task pauses rather than fails
- Budget violation: an action would exceed the remaining budget. The engine blocks before executing
- Risk threshold: the task's assessed risk rises above the configured boundary
- Bayesian surprise: observed outcomes deviate sharply from expected. This indicates novel or potentially unsafe conditions
- Supervision policy: a workflow phase declares
escalateas its supervision strategy for unrecoverable action failures
// React to escalations in real time
delta.events.on("escalation-raised", ({ taskId, trigger, reason }) => {
console.log(`Task ${taskId} escalated (${trigger}): ${reason}`);
notifyOnCallEngineer(taskId, reason);
});Engine guidance
Before escalating, the engine warns the agent through advisory guidance lines injected into its reasoning context. On each turn, the engine checks governance signals (risk level, trust score, budget consumption, and Bayesian surprise) and computes warning-band text the model sees before it proposes its next action.
Guidance works preventively: the agent sees "Risk rising above threshold" or "Budget at 80 percent" and can change course before the engine blocks the action. This reduces unnecessary escalations without reducing safety.
Guidance is on by default. Set guidance: false in engine configuration to disable it entirely.
Pause and resume
The engine pauses a task automatically on escalations and approval blocks. You can also pause a running task manually:
await delta.pause(taskId);
// Later, resume from the latest checkpoint
const result = await delta.resume(taskId);
if (result.isOk) {
console.log(`Resumed: ${result.value.status}`);
}Resume re-enters the governance loop from the last checkpoint. Completed work is never re-executed. If the task is still blocked on a pending approval, it blocks again at the same gate.
Concurrent resume safety
Two callers cannot both resume the same task. The engine uses a compare-and-swap transition so exactly one resume wins. The other gets a clear error. This prevents duplicate execution when multiple reviewers respond to the same escalation.
Rejection routing
When a human rejects an action, the engine feeds the rejection reason back to the model. The agent sees why its proposal was denied and can route around the blocked gate: choose a different approach, complete the task differently, or report what it could not do.
await delta.reject(approvalId, "Use the standard refund flow. The customer is not eligible for manual override");The model receives this feedback on its next turn (free loop) or the workflow fails with the reason (deterministic path). A rejected action is never re-requested. The engine permanently blocks that specific approval.
Supervision and HITL
Workflow phases declare supervision policies that determine what happens when an action fails. Strategies range from fully automated to human-in-the-loop:
- Retry: restart from the failed action (automated)
- Restart: restart the phase from the beginning (automated)
- Resume: continue from the latest checkpoint after human approval
- Escalate: hand off to a human when automated recovery is inappropriate
- Abort subtree: fail the current branch while keeping the workflow alive
- Abort entire tree: fail the entire task and its descendants
The escalate strategy is the designed HITL path for workflows: when automated recovery is exhausted or inappropriate, the engine pauses and waits for a human to assess the situation.
Events reference
HITL events are always-on. They fire regardless of any diagnostics configuration because a silently dropped human-oversight notification is a safety failure.
| Event | When |
|---|---|
approval-requested | An action requiring human approval needs sign-off |
approval-resolved | delta.approve() or delta.reject() resolves it |
escalation-raised | Reasoner failure, budget violation, or risk threshold exceeded |
task-blocked | Task is waiting on human action |
End-to-end flow
Agent proposes sensitive action
↓
Engine checks requiresApproval gate
↓
No approval on record → creates ApprovalRequest
↓
Task pauses, emits "approval-requested"
↓
─────────────────────────────────────────
Human reviews (via dashboard, Slack, etc)
─────────────────────────────────────────
↓
Approve → delta.approve(id) | Reject → delta.reject(id, reason)
↓ ↓
Engine emits "approval-resolved" |
↓ ↓
delta.resume(taskId) Model sees rejection reason
↓ ↓
Agent continues Agent routes around block