Protect an agent
External effects
A Stripe charge, a sent email, a created SaaS resource. These can't be restored by writing state back, because there is no before-state to capture — undo is a different call.
Declare the compensation
Two ways, depending on whether the reverse needs logic.
1. A declarative file — no code
Point GMONK_COMPENSATION_FILE at it, or drop a
gmonk.compensations.json in the working directory:
[{ "tool": "stripe.createCharge",
"capturesFromResult": ["id", "amount"],
"compensateWith": "stripe.refund",
"args": { "charge": "$id", "amount": "$amount" },
"idempotencyKeyFrom": "id" }] 2. Code, via @gmonk/sdk
import { registerCompensation, registerCompensationExecutor } from "@gmonk/sdk";
// 1. describe how to compensate the effect of a tool
registerCompensation({
tool: "stripe.createCharge",
capturesFromResult: ["charge_id"], // pulled off the result at capture time
compensate: ({ charge_id }) => ({
tool: "stripe.refund",
args: { charge: charge_id },
idempotencyKey: `gmonk-refund-${charge_id}`, // guards a double-refund on retry
}),
});
// 2. register the executor that performs the actual outbound call
registerCompensationExecutor("stripe.refund", async (desc) => {
const refund = await stripe.refunds.create(desc.args, {
idempotencyKey: desc.idempotencyKey,
});
return { ok: true, detail: `refunded ${desc.args.charge}`, result: { refund_id: refund.id } };
});
Point gmonk at it with GMONK_CONFIG=/path/to/gmonk.config.mjs.
Compensation and executor are two separate things
Performing the call needs an executor — registered in code, or a human running the exact call gmonk shows. That gives three states:
| What you've declared | What gmonk can do |
|---|---|
| Nothing | Flag-only. The effect is recorded and surfaced for a human — never silently undone, never silently dropped. |
| A compensation, no executor | A known, described, reviewable reverse: gmonk shows precisely what to call, with an idempotency key so a retried reverse can't double-refund. |
| A compensation and an executor | Applied automatically, behind --approve-compensations. |
This is the same fail toward flagged rule the rest of gmonk follows: anything it can't reverse with certainty becomes a human's decision. See what can be reversed for where external effects sit against everything else.