Approvals
Pause an agent until a human approves a risky action.
Approvals turn an autonomous agent into one that asks for confirmation at risky moments. The framework persists the request, suspends the relevant code path, and resumes when a human (or another system) resolves it.
Declarative routing — approvalRules
Most users want approvals tied to specific tools, not scattered through agent code. Declare the rules once in policy and the loop pauses for approval before dispatch:
const fabric = await init({
policy: {
toolPolicy: {
approvalRules: [
{ pattern: 'submit_*', audience: 'reviewer', reason: 'Review before submission of ${name}' },
{ pattern: 'delete_*', audience: 'project-admin', ttlSeconds: 3600 },
{ pattern: 'finalize_*', audience: 'compliance-team' },
],
},
},
});audience is an opaque string id. fabric-harness never decides who an audience maps to — your host application's identity layer (UI, SSO, RBAC) does that mapping. Rules also work on commandPolicy.approvalRules for bash invocations.
When the agent calls a matching tool, fabric-harness emits approval_requested with the audience id, the templated reason, and the TTL. The host UI renders the request to the right humans; on approval_granted the loop continues; on denial or TTL it throws. This is sugar over the imperative session.approval.request() API — which stays available as the escape hatch for ad-hoc cases.
Webhook subscriptions
Agents that should wake on inbound events (event bus, queue, external SaaS webhook) use defineWebhookSubscription:
import { agent, defineWebhookSubscription } from '@fabric-harness/sdk';
export default agent({
name: 'data-validator',
triggers: { webhook: true },
subscriptions: [
defineWebhookSubscription<{ recordId: string }>({
id: 'on-record-created',
events: ['record.created'],
handler: async ({ payload, idempotencyKey, headers }) => {
// ...invoke whatever your host application exposes.
},
}),
],
async run() { /* ... */ },
});fh server exposes each subscription at POST /agents/<agent>/subscriptions/<id>. Set FABRIC_HARNESS_WEBHOOK_SECRET to require an X-Fabric-Signature: sha256=<hmac> header on every delivery. The server dedupes by Idempotency-Key for at-least-once event buses, and emits a webhook_received event into the session log for audit.
fh subscriptions [agent] lists registered subscriptions across the workspace.
Request an approval
session.approval.request() returns true when the approver allows the action.
It throws a FabricError (APPROVAL_DENIED or APPROVAL_REQUIRED) on denial
or timeout — wrap in try/catch when you want to handle rejection gracefully.
import { FabricError } from '@fabric-harness/sdk';
try {
await session.approval.request({
reason: 'About to push changes to GitHub',
subject: 'git push origin main',
risk: 'high',
timeoutMs: 24 * 60 * 60 * 1000, // 24h
});
await session.shell('git push origin main');
} catch (error) {
if (error instanceof FabricError && error.code === 'APPROVAL_DENIED') {
throw new Error(`Push blocked: ${error.message}`);
}
throw error;
}The available fields are:
| Field | Type | Description |
|---|---|---|
reason | string | Human-readable reason shown to the approver. |
subject | string? | Short subject line for UIs. Defaults to 'custom'. |
risk | 'low' | 'medium' | 'high'? | Drives escalation policy. |
timeoutMs | number? | Override the session's default approval timeout. |
onApproval | ApprovalCallback? | Per-call approval handler. Falls back to session/agent onApproval. |
Resolve from the CLI
fh approvals <session-id> --pending
fh approve <session-id> <approval-id> --actor preetham
# or
fh reject <session-id> <approval-id> --actor preetham --reason "Wrong branch"Durable waits
On the Temporal worker target, the approval wait becomes a workflow signal — the agent can wait minutes, hours, or days without holding any process resources. On the inline runtime, the wait is process-local.
To make agents safe on either runtime, declare autonomy fallbacks:
await init({
autonomy: {
onApprovalUnavailable: 'fail', // or 'assume-rejected' | 'assume-approved'
},
});Logged identities
Approvals record both the agent identity and the human actor. Combined with the two-identity actor schema (Entra Agent ID + on-behalf-of user), the audit trail answers "which agent acted, on whose behalf, who approved?"