# Getting Started

Define a capability and run it through the Kaji executor.

Kaji is TypeScript-only in v0 and is not yet published to a package
registry. This tutorial defines one capability and executes it with the
in-memory execution store, the same shape as `examples/refund` in the
repository.

## Install

From within the repository (or a project that depends on the `@irogane/kaji`
workspace package):

```bash
bun install
```

Kaji does not require or declare a schema library as a dependency. `input`
only needs a `parse(input: unknown)` method that returns validated input or
throws — this example uses Zod, but any library with that shape works.

```bash
bun add zod
```

## First run

<Steps>
  <Step>
    ### Define and execute a capability

    ```ts
    import { capability, createKaji, memoryStore } from "@irogane/kaji";
    import { z } from "zod";

    const echo = capability({
      name: "echo",
      input: z.object({ message: z.string() }),
      authorize: async () => true,
      execute: async (input) => ({ message: input.message }),
    });

    const kaji = createKaji({ store: memoryStore() });

    const result = await kaji.execute(echo, {
      input: { message: "Hello, Kaji." },
      principalId: "local-user",
      idempotencyKey: "echo-1",
    });

    console.log(result);
    ```

    Run it with `bun run kaji.ts` (or `npx tsx kaji.ts`). It prints a
    `succeeded` result carrying `{ message: "Hello, Kaji." }` and execution
    evidence (`executionId`, `capability`, `principalId`, `idempotencyKey`,
    `inputFingerprint`).

  </Step>

  <Step>
    ### Add approval for sensitive input

    `approval` marks which requests need a decision before `execute` runs.
    Supply `approve` on the executor to decide them:

    ```ts
    const refund = capability({
      name: "payments.refund",
      input: z.object({ paymentId: z.string(), amount: z.number() }),
      authorize: async ({ principalId, input }) => canRefund(principalId, input.paymentId),
      approval: ({ input }) => input.amount >= 500,
      execute: async (input, context) => refundPayment(input, context),
    });

    const kaji = createKaji({
      store: memoryStore(),
      approve: async (request) => requestHumanApproval(request),
    });
    ```

    An absent approver, an invalid decision shape, a rejected decision, or an
    approver error settles the execution as `rejected` instead of running
    `execute`.

  </Step>

  <Step>
    ### Handle every outcome

    `execute()` resolves with an explicit `status` instead of throwing for
    expected outcomes: `succeeded`, `denied`, `rejected`, `failed`,
    `cancelled`, or `unknown`. `unknown` means a side effect may have
    committed but Kaji cannot prove it — do not blindly retry it.

    ```ts
    if (result.status === "succeeded") {
      console.log(result.result);
    } else if (result.status === "unknown") {
      // do not retry automatically; the side effect may have already run
    }
    ```

  </Step>
</Steps>

## Further Reading

- [Capabilities](/docs/concepts/capability) for the full `capability()`
  contract
- [Execution](/docs/concepts/executor) for the exact order Kaji runs
  validation, authorization, approval, and execution
- [Idempotency](/docs/concepts/idempotency) for how
  retries and duplicates resolve
- `docs/api.md` in the repository for the complete frozen contract, including
  the export boundary and known-failure semantics
- `examples/refund` in the repository for a runnable end-to-end proof