Skip to content
Kavachकवच

Agent Modes: Dry runs

● Intermediate3 min

kavach run --dry-run runs a program without making any capability call, and answers each one with a made-up value.

terminal
kavach run --dry-run refunds.kav
type PayError = Declined | Denied(reason: String)

type Receipt {
    id: String
}

capability Payments {
    fn refund(order: Int, amount: Int) -> Receipt ! PayError
}

type Bank: Payments {
    fn refund(self, order: Int, amount: Int) -> Receipt ! PayError {
        print("the bank moves real money here")
        Receipt(id: "r{order}")
    }
}

agent Refunder {
    can Payments.refund, Console

    limit Payments.refund {
        amount <= 500
    }

    on refund(order: Int, amount: Int) ! PayError {
        const receipt = try Payments.refund(order, amount)
        print("order {order}: receipt {receipt.id}")
    }
}

fn main() {
    const bot = Refunder.start(Payments: Bank())
    bot.refund(1, 400) catch e => print("refused: {e}")
    bot.refund(2, 900) catch e => print("refused: {e}")
}

The program ran and printed:

kavach run dry-refund.kav

order 1: receipt ofry refused: Denied(reason: "limit broken: amount <= 500")[dry run] Payments.refund(order: 1, amount: 400) -> Receipt(id: "ofry ")[dry run] Payments.refund(order: 2, amount: 900) refused: limit broken: amount <= 500
dry-refund.kav: Kavach code, then what running it prints
  • The bank's code never ran: no the bank moves real money here line.
  • The refund got a made-up Receipt, generated from its type. The same seed gives the same values every run.
  • Limits and approvals are still checked: the 900 refund fails as it would for real, and its [dry run] line says refused and why.
  • ask is answered the same way, so no model is called and nothing costs.

Each [dry run] line goes to the error output, apart from what the program prints. These pages show them after the program's own lines.