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.

gmonk never calls a third party on its own. It builds the compensating-call descriptor and hands it to an executor you supply, or shows you precisely what to run.

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:

json
[{ "tool": "stripe.createCharge",
   "capturesFromResult": ["id", "amount"],
   "compensateWith": "stripe.refund",
   "args": { "charge": "$id", "amount": "$amount" },
   "idempotencyKeyFrom": "id" }]

2. Code, via @gmonk/sdk

js
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 declaredWhat 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.