@dev.sail.money/sailor 1.0.0-42 → 1.1.0-44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/AGENTS.md +5 -3
  2. package/README.md +29 -20
  3. package/docs/PERMISSION_MODEL.md +1 -1
  4. package/examples/README.md +24 -0
  5. package/package.json +1 -1
  6. package/packages/cli/README.md +0 -1
  7. package/packages/cli/dist/index.cjs +146 -185
  8. package/packages/cli/dist/server.cjs +2 -1
  9. package/packages/sdk/README.md +2 -2
  10. package/packages/sdk/dist/intelligence.d.ts +1 -1
  11. package/packages/sdk/dist/intelligence.js +1 -1
  12. package/packages/ui/dist/assets/{add-i2P8A2gs.js → add-BHh1DUhq.js} +1 -1
  13. package/packages/ui/dist/assets/{all-wallets-BVgI7zL8.js → all-wallets-BksVbtLO.js} +1 -1
  14. package/packages/ui/dist/assets/{app-store-BewBVZmc.js → app-store-DAXz4Z64.js} +1 -1
  15. package/packages/ui/dist/assets/{apple-C7MUnEQz.js → apple-D1qi9OLG.js} +1 -1
  16. package/packages/ui/dist/assets/{arrow-bottom-DfToMb64.js → arrow-bottom-BiqLduSV.js} +1 -1
  17. package/packages/ui/dist/assets/{arrow-bottom-circle-DJb-rBBb.js → arrow-bottom-circle-DjOKHyE8.js} +1 -1
  18. package/packages/ui/dist/assets/{arrow-left-DsWmN2OG.js → arrow-left-Cv3GiY8T.js} +1 -1
  19. package/packages/ui/dist/assets/{arrow-right-DJUX6dvg.js → arrow-right-cU9gH-83.js} +1 -1
  20. package/packages/ui/dist/assets/{arrow-top-bK9-SH4h.js → arrow-top-CwdOsDUA.js} +1 -1
  21. package/packages/ui/dist/assets/{bank-LlHF24Ta.js → bank-BvkUuR5o.js} +1 -1
  22. package/packages/ui/dist/assets/{basic-BgERj65k.js → basic-ULr_cCSL.js} +1 -1
  23. package/packages/ui/dist/assets/{browser-B9v0kcdd.js → browser-ofrFja4K.js} +1 -1
  24. package/packages/ui/dist/assets/{card-QhDXGe3I.js → card-CD4ERbnK.js} +1 -1
  25. package/packages/ui/dist/assets/{ccip-CP4Hg6QU.js → ccip-C7eZiz3a.js} +1 -1
  26. package/packages/ui/dist/assets/{checkmark-ScbLFePj.js → checkmark-6onG3n_x.js} +1 -1
  27. package/packages/ui/dist/assets/{checkmark-bold-SxqQsHPF.js → checkmark-bold-NcCoTwYz.js} +1 -1
  28. package/packages/ui/dist/assets/{chevron-bottom-HOzCXggY.js → chevron-bottom-BbtzkXrF.js} +1 -1
  29. package/packages/ui/dist/assets/{chevron-left-DmtXEk14.js → chevron-left-BupFb2YQ.js} +1 -1
  30. package/packages/ui/dist/assets/{chevron-right-DJCWUGen.js → chevron-right-B8ORkqXW.js} +1 -1
  31. package/packages/ui/dist/assets/{chevron-top-Bj9XyjOS.js → chevron-top-3bPA7cI0.js} +1 -1
  32. package/packages/ui/dist/assets/{chrome-store-C0Rq-0OK.js → chrome-store-TyiIOIxo.js} +1 -1
  33. package/packages/ui/dist/assets/{clock-DJiHF6By.js → clock-XdWZm05K.js} +1 -1
  34. package/packages/ui/dist/assets/{close-B58KpBOm.js → close-O2N8lyoj.js} +1 -1
  35. package/packages/ui/dist/assets/{coinPlaceholder-QrD1nktf.js → coinPlaceholder-DmXeJurd.js} +1 -1
  36. package/packages/ui/dist/assets/{compass-DkdmCS4M.js → compass-BNxtPTdy.js} +1 -1
  37. package/packages/ui/dist/assets/{copy-BKkOeIGO.js → copy-CcKL-ay_.js} +1 -1
  38. package/packages/ui/dist/assets/{core-BJ5Wn_0H.js → core-CA8vptSL.js} +3 -3
  39. package/packages/ui/dist/assets/cursor-DOh7MR7A.js +3 -0
  40. package/packages/ui/dist/assets/{cursor-transparent-CwUDRud2.js → cursor-transparent-OF-vM6pK.js} +1 -1
  41. package/packages/ui/dist/assets/{desktop-CKk_cTjE.js → desktop-BfhpeGBs.js} +1 -1
  42. package/packages/ui/dist/assets/{disconnect-BR4tm4ry.js → disconnect-C82unXY9.js} +1 -1
  43. package/packages/ui/dist/assets/{discord-CimCnw-R.js → discord-CXKsMGcp.js} +1 -1
  44. package/packages/ui/dist/assets/{etherscan-DKSSzuOS.js → etherscan-CNJ1W4Ky.js} +1 -1
  45. package/packages/ui/dist/assets/{events-HAbmebGY.js → events-Bqdt3zhf.js} +1 -1
  46. package/packages/ui/dist/assets/{exclamation-triangle-CnI_W0DF.js → exclamation-triangle-CopyPOzr.js} +1 -1
  47. package/packages/ui/dist/assets/{extension-BSpH53zE.js → extension-DXl-l9r3.js} +1 -1
  48. package/packages/ui/dist/assets/{external-link-D2QNdCHI.js → external-link-DbaKKqZS.js} +1 -1
  49. package/packages/ui/dist/assets/{facebook-CgSGAlNU.js → facebook-uQnRLN_c.js} +1 -1
  50. package/packages/ui/dist/assets/{fallback-CUotDuSc.js → fallback-qvxUxnWI.js} +1 -1
  51. package/packages/ui/dist/assets/{farcaster-SPALE2oQ.js → farcaster-CjxFnq3Q.js} +1 -1
  52. package/packages/ui/dist/assets/{filters-DjDEAYKk.js → filters-ONZDIBOt.js} +1 -1
  53. package/packages/ui/dist/assets/{github-CP9cMjmv.js → github-CV4aFvqa.js} +1 -1
  54. package/packages/ui/dist/assets/{google-DHxowSYF.js → google-BzKMlcn7.js} +1 -1
  55. package/packages/ui/dist/assets/{help-circle-Y1PsVJnm.js → help-circle-JrQdZVju.js} +1 -1
  56. package/packages/ui/dist/assets/{id-Du_4Z0EW.js → id-DkP1sjPL.js} +1 -1
  57. package/packages/ui/dist/assets/{image-tsFuvzhF.js → image-DnevZUmm.js} +1 -1
  58. package/packages/ui/dist/assets/index-BFj8874c.js +1789 -0
  59. package/packages/ui/dist/assets/{index-CNBJUjiM.js → index-CZVBCY_B.js} +1 -1
  60. package/packages/ui/dist/assets/{index-tPkIpVzb.js → index-Cf1OFgoJ.js} +3 -3
  61. package/packages/ui/dist/assets/{index-0OXGjc-u.js → index-DBk2Tmio.js} +1 -1
  62. package/packages/ui/dist/assets/index-ZMTNJl7U.css +1 -0
  63. package/packages/ui/dist/assets/{index-7RnuTfdS.js → index-aTtdADh3.js} +1 -1
  64. package/packages/ui/dist/assets/{index-DExn8x4G.js → index-dsFClx06.js} +1 -1
  65. package/packages/ui/dist/assets/{index.es-DIo7ubqt.js → index.es-BeGshwm4.js} +4 -4
  66. package/packages/ui/dist/assets/{info-DwysfTrK.js → info-CL9fTXa-.js} +1 -1
  67. package/packages/ui/dist/assets/{info-circle-BgqfZd9m.js → info-circle-1g_YMvEJ.js} +1 -1
  68. package/packages/ui/dist/assets/{lightbulb-Cy1dZk-v.js → lightbulb-BCz1ZXej.js} +1 -1
  69. package/packages/ui/dist/assets/{mail-q8uF2fJv.js → mail-C34-jLtr.js} +1 -1
  70. package/packages/ui/dist/assets/{metamask-sdk-CC2GhoTH.js → metamask-sdk-CSM1cHvw.js} +1 -1
  71. package/packages/ui/dist/assets/{mobile-BM3dHloL.js → mobile-C6LNNWIU.js} +1 -1
  72. package/packages/ui/dist/assets/{more-DtxChT3n.js → more-Mq--l-Tg.js} +1 -1
  73. package/packages/ui/dist/assets/{network-placeholder-CkS7N2-s.js → network-placeholder-D8dAk4sm.js} +1 -1
  74. package/packages/ui/dist/assets/{nftPlaceholder-BP2BELBx.js → nftPlaceholder-DelmkBov.js} +1 -1
  75. package/packages/ui/dist/assets/{off-CGlNgOvT.js → off-7H_l5MJ-.js} +1 -1
  76. package/packages/ui/dist/assets/{parseSignature-B-9InYad.js → parseSignature-DYe1tJDq.js} +1 -1
  77. package/packages/ui/dist/assets/{play-store-j4WI9tEY.js → play-store-Czh3YKh7.js} +1 -1
  78. package/packages/ui/dist/assets/{plus-DoBLB2r6.js → plus-ComRxU33.js} +1 -1
  79. package/packages/ui/dist/assets/{qr-code-CxMPa6dI.js → qr-code-CuqCcyVv.js} +1 -1
  80. package/packages/ui/dist/assets/{recycle-horizontal-tKM6wEFt.js → recycle-horizontal-DFKQpQAP.js} +1 -1
  81. package/packages/ui/dist/assets/{refresh-D3gJv-wD.js → refresh-CTJiyWAT.js} +1 -1
  82. package/packages/ui/dist/assets/{reown-logo-CB8aduTF.js → reown-logo-C6YIY-fD.js} +1 -1
  83. package/packages/ui/dist/assets/{search-CEFrSvpQ.js → search-BiwxRFHX.js} +1 -1
  84. package/packages/ui/dist/assets/{secp256k1-BiXu1g8S.js → secp256k1-CPGf_4B1.js} +1 -1
  85. package/packages/ui/dist/assets/{send-DpKQgtRb.js → send-D03N9lRH.js} +1 -1
  86. package/packages/ui/dist/assets/{swapHorizontal-a9Ybf18t.js → swapHorizontal-B5x5kCS-.js} +1 -1
  87. package/packages/ui/dist/assets/{swapHorizontalBold-DP452gU2.js → swapHorizontalBold-DCMf8a5a.js} +1 -1
  88. package/packages/ui/dist/assets/{swapHorizontalMedium-eG99M64A.js → swapHorizontalMedium-BYlOGVkE.js} +1 -1
  89. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BAc_to7T.js → swapHorizontalRoundedBold-CZwHe_mN.js} +1 -1
  90. package/packages/ui/dist/assets/{swapVertical-BU1HNAb2.js → swapVertical-ytho09QV.js} +1 -1
  91. package/packages/ui/dist/assets/{telegram-DFyhBidh.js → telegram-FqqxzOzC.js} +1 -1
  92. package/packages/ui/dist/assets/{three-dots-BdnfIeUB.js → three-dots-zaLgHURe.js} +1 -1
  93. package/packages/ui/dist/assets/{twitch-DdurBvf3.js → twitch-CxKccKXq.js} +1 -1
  94. package/packages/ui/dist/assets/{twitterIcon-JS1Kh388.js → twitterIcon-CxT39J_l.js} +1 -1
  95. package/packages/ui/dist/assets/{verify-C-ZvR7T4.js → verify-DxwXI19y.js} +1 -1
  96. package/packages/ui/dist/assets/{verify-filled-Dzgdgepq.js → verify-filled-BTHJmMkV.js} +1 -1
  97. package/packages/ui/dist/assets/{w3m-modal-B_CKEhcT.js → w3m-modal-BGEooFHU.js} +1 -1
  98. package/packages/ui/dist/assets/{wallet-C4JYrLNC.js → wallet-N29oXhAV.js} +1 -1
  99. package/packages/ui/dist/assets/{wallet-placeholder-DURyjfcE.js → wallet-placeholder-DBI8jWka.js} +1 -1
  100. package/packages/ui/dist/assets/{walletconnect-C5I2F5B-.js → walletconnect-BT1fqf0b.js} +1 -1
  101. package/packages/ui/dist/assets/{warning-circle-Blhki0Aq.js → warning-circle-f1tnN6R2.js} +1 -1
  102. package/packages/ui/dist/assets/{x-NdGVznpk.js → x-NK3JDZtO.js} +1 -1
  103. package/packages/ui/dist/index.html +2 -2
  104. package/scripts/check-docs.mjs +51 -4
  105. package/scripts/check-init.mjs +12 -0
  106. package/templates/default/.agents/skills/sail-ci/SKILL.md +66 -0
  107. package/templates/default/.agents/skills/sail-extend/SKILL.md +74 -0
  108. package/templates/default/.agents/skills/sail-mandates/SKILL.md +93 -0
  109. package/templates/default/.agents/skills/sail-mandates/references/approvals.md +42 -0
  110. package/templates/default/.agents/skills/sail-mandates/references/calls-schema.md +42 -0
  111. package/templates/default/.agents/skills/sail-mandates/references/constructor-args.md +45 -0
  112. package/templates/default/.agents/skills/sail-mandates/references/examples-index.md +31 -0
  113. package/templates/default/.agents/skills/sail-mandates/references/simulate-calls.md +58 -0
  114. package/templates/default/.agents/skills/sail-onboarding/SKILL.md +73 -0
  115. package/templates/default/.agents/skills/sail-project-info/SKILL.md +30 -0
  116. package/templates/default/.agents/skills/sail-servers/SKILL.md +43 -0
  117. package/templates/default/.agents/skills/sail-transactions/SKILL.md +63 -0
  118. package/templates/default/AGENTS.md +37 -126
  119. package/templates/default/test/BoundedCallPermission.t.sol +73 -0
  120. package/packages/ui/dist/assets/cursor-Dk5BeeUC.js +0 -3
  121. package/packages/ui/dist/assets/index-BODuSfdj.css +0 -1
  122. package/packages/ui/dist/assets/index-Bj9jEvxf.js +0 -1775
  123. /package/{templates → examples}/custom-mandate/.sail/contracts/interfaces/IPermission.sol +0 -0
  124. /package/{templates → examples}/custom-mandate/README.md +0 -0
  125. /package/{templates → examples}/custom-mandate/foundry.toml +0 -0
  126. /package/{templates → examples}/custom-mandate/mandates/BoundedCallPermission.sol +0 -0
  127. /package/{templates → examples}/custom-mandate/mandates/README.md +0 -0
  128. /package/{templates → examples}/custom-mandate/mandates/SailCalldata.sol +0 -0
  129. /package/{templates → examples}/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +0 -0
  130. /package/{templates → examples}/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +0 -0
  131. /package/{templates → examples}/lifi-permissions/README.md +0 -0
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: sail-onboarding
3
+ description: Walks the agent through setting up a new Sailor project or resuming a partially set-up one — SMA deployment, agent wallet creation, address prediction, and multi-chain deployment. Use when the project has no SMA yet, when .sail/account.json is missing or incomplete, when the user says "start" or "continue", or when deploying the SMA to an additional chain.
4
+ ---
5
+
6
+ # Sail onboarding
7
+
8
+ ## Running the CLI
9
+
10
+ The project does not install `sailor` as a dependency, so invoke it with **`npx sailor <command>`** unless it is installed globally (`npm i -g @sail.money/sailor`, then `sailor` works bare). Every `sailor …` command in these skills assumes one of those. Confirm the toolchain up front and pin a recent version — `npx sailor@latest --version` — because an old cached `npx` build can be missing newer commands (e.g. `mandate simulate`); if a documented command reports "unknown command", you are on a stale version, not hitting a missing feature.
11
+
12
+ Stage machine keyed off `.sail/`. Read the state, enter at the right stage, never re-run completed stages.
13
+
14
+ ## Determine where the user is
15
+
16
+ | `.sail/` state | Stage |
17
+ |---|---|
18
+ | `config.json` has `chainId: null` | Stage 0 — chain not chosen; ask which chain, write it to `config.json` |
19
+ | No `account.json` | Stage 1 — SMA not deployed |
20
+ | `account.json` exists, `state/mandates.json` empty or absent | SMA live, no permissions — hand off to **sail-mandates** |
21
+ | `account.json` + tracked mandates | Fully onboarded — hand off to **sail-transactions** / running |
22
+
23
+ Supported chains: Ethereum (1), Base (8453), Arbitrum (42161), Unichain (130), Base Sepolia (84532), Eth Sepolia (11155111). `sailor chains --json` lists them with kernel addresses.
24
+
25
+ ## Stage 1 — Deploy the SMA and create the agent wallet
26
+
27
+ In the browser. Run `sailor ui start`, open the printed URL, connect the owner wallet, choose the network, deploy the SMA, then create the agent wallet — a separate signing key the agent uses to submit transactions. **Both wallets need gas, and the split is not what it looks like:** the owner *signs* (SMA deployment, mandate authorization) but the **agent wallet submits and pays for every on-chain transaction** — including `mandate deploy` and `mandate attach` during setup, not just dispatches once running. Fund the agent wallet before Stage 3 or attach fails with `gas required exceeds allowance`. The owner wallet needs gas only for transactions it submits directly in the browser (the SMA deployment). The owner key never leaves the browser.
28
+
29
+ Headless alternative (the agent drives, the owner only signs in the browser):
30
+
31
+ ```bash
32
+ sailor keys generate # create the agent wallet (interactive: role + passphrase)
33
+ sailor station start --json & # signing daemon — BLOCKS; run in background
34
+ sailor owner connect --json # BLOCKS up to 300s waiting for a wallet to connect in the browser
35
+ sailor scan --json # discover the owner's Safes and state
36
+ sailor onboard --new-sma --json # deploy SMA — BLOCKS waiting for the owner's browser signature
37
+ ```
38
+
39
+ `onboard --new-sma` pushes a `create-sma` signing request to the browser, waits for the owner to approve (default timeout 10 minutes), then persists the SMA to `.sail/account.json`. Tell the user: "approve the request in the signing station in your browser."
40
+
41
+ ## Deterministic address (salt)
42
+
43
+ Every SMA deployment uses a CREATE2 salt (default `0`). The kernel binds the salt to the owner, the agent (manager) wallet, and the fee policy — so **create the agent wallet first, then predict**:
44
+
45
+ ```bash
46
+ sailor account predict --owner <owner> --manager <agent-wallet> --json
47
+ ```
48
+
49
+ The salt is saved to `.sail/account.json` (`saltNonce`) automatically on deploy. All supported chains share the same protocol addresses via CREATE2, so the same owner, manager, and salt produce the same SMA address on every chain. `predict` reads no keys and spends no gas. Use `--salt <n>` for a non-default salt, `--chain <id>` for one chain only.
50
+
51
+ If `predict` errors with "depends on the agent (manager) wallet", the agent wallet does not exist yet — generate it first.
52
+
53
+ ## Multi-chain deployment
54
+
55
+ Once the SMA is live on one chain, deploy it at the same address on another supported chain:
56
+
57
+ ```bash
58
+ sailor account predict --json # confirm the address matches first
59
+ sailor account deploy-chain --chain 42161 --json # BLOCKS waiting for the owner's browser signature
60
+ ```
61
+
62
+ The owner approves in the browser (switch the wallet to the target chain before signing); no new salt or agent wallet is needed. The command is idempotent — if the SMA already has code at the predicted address on the target chain it records the chain and exits cleanly. Deployed chains accumulate in `account.json` `deployedChains`.
63
+
64
+ If `deploy-chain` refuses with an address mismatch, the SMA was deployed against the old per-chain contracts (pre-CREATE2) and cannot be reproduced cross-chain — the fix it prints is to deploy a fresh SMA with `sailor onboard --new-sma`.
65
+
66
+ ## Gas requirements
67
+
68
+ - Owner wallet: SMA deployment, mandate signing (EIP-712), any additional-chain deployment.
69
+ - Agent wallet: `mandate deploy`, `mandate attach`, `mandate revoke`, and every dispatch submission once the agent runs, plus permission registration fees on fee-charging chains.
70
+
71
+ `sailor doctor` — read-only preflight: kernel dispatch model, permission health, RPC reachability, gas balances in both wallets. Do not proceed to Stage 3 with a failing doctor.
72
+
73
+ During setup, always ask before anything that costs gas.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: sail-project-info
3
+ description: Read-only commands and state files that answer questions about the project, account, mandate, chains, keys, or environment. Use when the user asks "what's the state of…", "is X set up", "which chains…", "what permissions are registered", or before any operation that needs current state.
4
+ ---
5
+
6
+ # Sail project info
7
+
8
+ Every command here is read-only and supports `--json` for machine reading. Prefer `--json`; parse, don't scrape. None of them block on the browser.
9
+
10
+ | Command | Reports | Backed by |
11
+ |---|---|---|
12
+ | `sailor status` | One-screen setup progress: keys present, account, signed mandate, session, agent run state | Local: `.sail/account.json`, `mandate.json`, `session.json`, `keys/`, agent pid file |
13
+ | `sailor doctor --json` | Preflight: kernel dispatch model, permission health (per-permission evaluate probes), RPC reachability, gas balances of owner + agent wallets | On-chain reads via the configured RPC; `--account <address>` overrides the SMA |
14
+ | `sailor capabilities --json` | Feasibility map: resolved chain, kernel model, available mandate templates, strategy primitives | `deployments.ts` registry + on-chain typehash detection |
15
+ | `sailor chains --json` | Supported chains and their SailKernel addresses | Static registry; add `--verify` to `eth_getCode` each kernel (one RPC call per configured chain) |
16
+ | `sailor scan --json` | Owner's Safes, which are Sail SMAs, their managers/permissions/sessions, local keys — persisted to `.sail/state/context.json` | Safe Transaction Service + kernel reads; `--owner <address>` overrides the saved owner |
17
+ | `sailor owner show --json` | The saved project owner address | `.sail/state/owner.json` |
18
+ | `sailor keys show` | Address of each stored key (decrypts to derive the address; prompts for passphrase unless `SAIL_PASSPHRASE` is set) | `.sail/keys/*.json` |
19
+ | `sailor mandate list` | Permission contracts deployed from this project, with attachment history | `.sail/state/mandates.json` |
20
+ | `sailor mandate templates --json` | How to author a permission + any community-deployed standalone template addresses on this chain (unaudited, informational) | `deployments.ts` `standaloneTemplates` |
21
+
22
+ ## Which source answers what
23
+
24
+ - "Is the SMA deployed?" — `account.json` exists and has `safe`; `doctor` confirms on-chain.
25
+ - "What can the agent do right now?" — on-chain `getPermissions()` is the truth; `mandate sign` reconciles against it. `state/mandates.json` is an append-only historical record — a permission revoked on-chain still appears there.
26
+ - "Is the RPC working / is there gas?" — `sailor doctor --json`.
27
+ - "Same address on another chain?" — `account.json` `deployedChains`, verified by `sailor account predict`.
28
+ - "Is anything running?" — `.sail/runtime/ui.json` (dashboard), `.sail/runtime/server.json` (signing station), agent pid via `sailor status`.
29
+
30
+ Run `sailor doctor` before any operation that spends gas — it is the cheapest way to catch a dead RPC, an unfunded wallet, or a kernel mismatch first.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: sail-servers
3
+ description: Start, stop, and health-check the two local servers — the Sailor dashboard and the signing station. Use when launching the UI, when a signing request needs a browser, when a port or pid question comes up, or when a command appears stuck waiting for a signature.
4
+ ---
5
+
6
+ # Sail servers
7
+
8
+ Two distinct local servers. Both are per-project, both write state under `.sail/runtime/`, and both start idempotently (starting twice reports "already running" and exits 0).
9
+
10
+ ## Dashboard — `sailor ui`
11
+
12
+ ```bash
13
+ sailor ui start # spawns a detached Express server, prints the URL, returns immediately
14
+ sailor ui status # ● running http://localhost:<port> (pid N)
15
+ sailor ui stop # SIGTERM via the pid file
16
+ ```
17
+
18
+ - Port: deterministic per project — `3333 + (hash(projectPath) % 667)`, i.e. somewhere in 3333–3999, bumped to the next free port if taken. **Do not assume 3333** — always use the URL the command prints or read `.sail/runtime/ui.json` (`{ pid, port, startedAt }`).
19
+ - The server serves the pre-built React app (`SERVE_DIST=1`) and a local `/api` that reads `.sail/` state (`SAIL_DIR` env).
20
+ - `ui start` does not block — no `&` needed.
21
+
22
+ ## Signing station — `sailor station`
23
+
24
+ ```bash
25
+ sailor station start --json # BLOCKS — run in the background
26
+ sailor station status --json # running / stopped, url, pid
27
+ sailor station stop --json # SIGTERM, verified against the recorded URL first
28
+ ```
29
+
30
+ - Port: defaults to **3141**, bumped to the next free port if taken. The actual URL is in `.sail/runtime/server.json` (`{ url, wsUrl, port, pid, startedAt, requestSecret }`).
31
+ - Health/discovery endpoint: `GET http://localhost:<port>/config` returns `{ url, wsUrl, port, pid, pendingCount }` — `pendingCount` is the number of signing requests waiting for the owner. All other endpoints require a per-startup secret; do not poll them.
32
+ - `station start` **blocks** (the listening socket keeps the process alive) — run it in the background. It is idempotent: if a reachable daemon exists it reports already-running and exits 0.
33
+ - The URL to give the user is the **dashboard** station route printed by the command: `http://localhost:<ui-port>/#/station`.
34
+
35
+ ## How they relate
36
+
37
+ Signing-flow commands (`mandate deploy/attach/deploy-clone/revoke`, `onboard`, `account deploy-chain`, `account rotate-signer`, `owner connect`) push requests to a running station daemon if one exists, otherwise they spin up an ephemeral in-process signing server for the duration of the command. Starting a persistent station first means the owner connects their wallet once and approves a whole sequence of requests in the same browser tab — do this before any multi-step signing flow.
38
+
39
+ ## Troubleshooting
40
+
41
+ - Command stuck "waiting"? It is blocked on a browser signature — check `GET /config` `pendingCount`, and tell the user to open the station URL and approve. Signing requests time out after 10 minutes.
42
+ - Stale pid file (process died): `ui status` / `station stop` clean it up automatically.
43
+ - `sailor ui start` errors about a missing server bundle or UI dist: the package build is incomplete — re-run the sailor build.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: sail-transactions
3
+ description: How dispatches and EVM transactions work in a Sailor project — the selective dispatch model, signing, batching, permission resolution, running the agent, and which CLI commands block on a browser signature. Use when building or debugging dispatches, writing agent tick code, running the agent, or reasoning about why a transaction was denied or reverted.
4
+ ---
5
+
6
+ # Sail transactions
7
+
8
+ ## The dispatch model
9
+
10
+ The deployed kernels run the **selective** model: every dispatch names one registered permission, and the kernel calls that permission's `evaluate()` (or `evaluateBatch()` for batches) on-chain before executing. A permission returning false, reverting, or exceeding its gas cap is a denial — fail-closed.
11
+
12
+ Never hardcode the model. `detectKernelCapabilities` (SDK) reads the on-chain `DISPATCH_TYPEHASH()` and is the only reliable source; the static label in `deployments.ts` is an offline fallback.
13
+
14
+ ## Signing
15
+
16
+ Never hand-roll the EIP-712 dispatch struct. Use `buildDispatchSignature` from the SDK — it reads the on-chain typehash and builds the correct typed data for the detected model.
17
+
18
+ ## Writing agent code
19
+
20
+ The runner (`sailor run`) calls `agent.tick(ctx)` from `src/agent.ts` and submits each returned `Dispatch`:
21
+
22
+ - A dispatch is `{ calls: [{ target, value, data }, …] }`. One call = single dispatch; multiple calls = batch dispatch (single on-chain tx, all-or-nothing).
23
+ - You do not name permissions: the runner probes each registered permission in registration order (off-chain `evaluate()` / `previewBatch` via `eth_call`) and routes to the first that accepts. Set `dispatch.permission` only as an explicit override to skip the probe.
24
+ - **Approve + action — two non-mixable models** (details: `../sail-mandates/references/approvals.md`):
25
+ - **Per-call (default):** two separate single-call dispatches, each gated by its own `IPermission`. Emit the approve only when on-chain allowance is insufficient (the `examples/dca/` pattern).
26
+ - **Atomic batch (advanced):** one `Dispatch` with `calls: [approveCall, actionCall]` authorized by a single `IBatchPermission`. A normal `IPermission` cannot authorize a batch (`PermissionNotBatchAware`). Do not mix the models.
27
+ - `ctx` provides `read.balance/allowance/decimals`, `publicClient` (arbitrary reads), `log()`, and a persistent `data` slot.
28
+ - Return `[]` to skip a tick — no gas spent.
29
+
30
+ ## Running
31
+
32
+ ```bash
33
+ sailor run --once # single tick — confirm it works before automating
34
+ sailor run # continuous; interval SAILOR_INTERVAL seconds (default 60)
35
+ sailor run --chain <id> # override CHAIN_ID for this run
36
+ ```
37
+
38
+ `run` needs `.sail/account.json`, `.sail/mandate.json`, a manager key, `CHAIN_ID` and an RPC URL (`.sail/.env.local`). Set `SAIL_PASSPHRASE` in `.env.local` to unlock the agent wallet non-interactively. Neither form blocks on a browser — the signed mandate is the authorization.
39
+
40
+ ## Where results land
41
+
42
+ Everything appends to `.sail/activity.jsonl` (one JSON per line): `tick_start`/`tick_end`, `dispatch_approved`, `dispatch_executed` (with `txHash`), `dispatch_reverted` (with `txHash`, `gasUsed`), `dispatch_denied` (with `reason`, e.g. `no_permission_match` plus the rejected selector), `error`, and owner-side `owner_signed`/`owner_rejected` events. On stderr, reverts surface as `reverted: <txHash> (gas used: N)`; denials as `skipped: no registered permission authorizes call to <target> (selector 0x…)`. A failed dispatch never stops the loop.
43
+
44
+ ## Commands that BLOCK on a browser signature
45
+
46
+ These open a signing channel, push a request, and wait (default timeout 10 minutes) for the owner to approve in the browser. When one is pending, tell the user: "approve the request in the signing station in your browser" and give them the station URL the command printed.
47
+
48
+ | Command | What the owner signs |
49
+ |---|---|
50
+ | `sailor onboard --new-sma` | `create-sma` transaction (owner pays gas) |
51
+ | `sailor account deploy-chain --chain <id>` | `create-sma` transaction on the target chain |
52
+ | `sailor account rotate-signer` | Delegate rotation + mandate re-approvals |
53
+ | `sailor mandate deploy` | `deploy-mandate` contract-creation transaction (owner pays gas) |
54
+ | `sailor mandate attach` | `RegisterPermission` EIP-712 (off-chain signature; agent submits and pays gas) |
55
+ | `sailor mandate deploy-clone` | `RegisterPermission` EIP-712 for the predicted clone address |
56
+ | `sailor mandate revoke` | `RevokePermissions` EIP-712 (agent submits and pays gas) |
57
+ | `sailor owner connect` | Nothing — blocks up to 300s waiting for a wallet to connect |
58
+
59
+ All take `--json` for machine-readable output; in `--json` mode the blocking commands emit a `waiting_for_signature` status with the URL before blocking.
60
+
61
+ ## Session control
62
+
63
+ `sailor session pause` instantly revokes dispatch rights without touching custody; `sailor session resume` restores them.
@@ -1,3 +1,7 @@
1
+ This guide is for agents operating a scaffolded Sailor project. (Contributors to the Sailor codebase: see AGENTS.md at the monorepo root.)
2
+
3
+ ---
4
+
1
5
  Sail Protocol is infrastructure for onchain Separately Managed Accounts run by AI agents. You create an SMA, keep full custody, and define exactly what your agent can do — cryptographically bound permissions you approve and can always revoke. The agent executes within those bounds on every transaction. It cannot exceed them.
2
6
 
3
7
  I'm Sailor, the operator that sets this up. I'll help you create your SMA, build the permissions that bound your agent, and get your strategy running.
@@ -32,140 +36,47 @@ During **setup**, always ask before anything that costs gas. Once the **mandate
32
36
 
33
37
  When the user says start (or any first message), present the welcome above in full — definition, stage list, handoff line — before doing anything else. Do not launch the UI yet. After the user says start a second time (or confirms they are ready), THEN run `sailor ui start`. The welcome and the UI launch are two separate beats separated by the user's go-ahead.
34
38
 
35
- Determine the user's progress by reading `.sail/` — do not ask; read it.
36
-
37
39
  If the user's first message is an npm install command, run it, then present the welcome immediately after it completes — do not wait for another message.
38
40
 
39
- ## Stage 1Deploy your SMA and create your agent wallet
40
-
41
- In the browser. Run `sailor ui start`, open the printed URL, connect your owner wallet, choose your network, and deploy your SMA. Then create your agent wallet — a separate signing key I use to submit transactions on your behalf. You need gas in both: the owner wallet to deploy and sign the mandate; the agent wallet to submit transactions once the agent is running. The owner key never leaves the browser.
42
-
43
- **Deterministic address (salt):** every SMA deployment uses a CREATE2 salt. The CLI defaults to salt `0`, giving you a predictable address you can verify before spending gas: `sailor account predict --owner <your-wallet> --manager <agent-wallet>`. The kernel binds the salt to your owner wallet, agent (manager) wallet, and fee policy — create your agent wallet first, then predict. The salt is saved in `.sail/account.json` automatically. All supported chains (Ethereum, Base, Arbitrum, Unichain, plus testnets) share the same protocol addresses via CREATE2, so the same owner, manager, and salt produce the **same SMA address on every chain**.
44
-
45
- **Multi-chain deployment:** once your SMA is live on one chain, deploy it at the same address on any other supported chain with `sailor account deploy-chain --chain <id>` (e.g. `--chain 42161` for Arbitrum). The owner approves the deployment in the browser; no new salt or agent wallet needed. Run `sailor account predict` first to confirm the address matches.
46
-
47
- ## Stage 2 — Define your strategy
48
-
49
- Tell me what you want your agent to do. I'll ask the right questions, establish the on-chain bounds with you (tokens, amounts, slippage, venues), and set up your RPC endpoint once you've chosen your chain. Blank slate — you define the strategy.
50
-
51
- For a worked end-to-end example (DCA / Uniswap V3 / Base), consult `examples/dca/` — reference only; not your strategy.
52
-
53
- ## Stage 3 — Build, test, and sign your mandate
54
-
55
- I'll write the permission contracts that bound your agent, prove in plain English what each one permits and blocks against sample calls, deploy them, and walk you through signing to authorize. Author, verify, sign — one step.
56
-
57
- Permission contracts live in `mandates/`. The user authors, reviews, and owns them. For examples by protocol and chain, see `examples/permissions/`.
58
-
59
- **Prerequisite — Foundry:** `forge build` requires the Foundry toolchain. If `forge` is not found, install it:
60
- ```bash
61
- curl -L https://foundry.paradigm.xyz | bash # then restart shell
62
- foundryup
63
- ```
64
-
65
- **Approve coverage — mandatory:** ERC-20 `approve()` calls are NOT covered by supply or deposit permission contracts. If your strategy approves a token before supplying, you MUST deploy a separate bounded-approve permission that covers that specific `(token, spender, maxAmount)` combination, and authorize it alongside the supply permission. An agent that calls `approve()` without a matching permission will be rejected by the kernel.
66
-
67
- **Batching:** if a strategy tick needs to approve before supplying, build both calls into a single dispatch array — `[approveCall, supplyCall]` — not two separate ticks. Splitting them wastes a tick and the approval sits exposed until the next run.
68
-
69
- ```bash
70
- forge build
71
- sailor mandate deploy --contract <Name> --sma <SMA> # deploy only — do NOT --attach yet
72
- ```
73
-
74
- **Constructor args:** quoting rules differ by shell.
41
+ ## Project stateread `.sail/`, never ask
75
42
 
76
- Bash / Git Bash:
77
- ```bash
78
- sailor mandate deploy --contract <Name> --args '["0xToken","1000000"]' --sma <SMA>
79
- ```
80
-
81
- PowerShell — use escaped inner quotes inside single quotes:
82
- ```powershell
83
- sailor mandate deploy --contract <Name> --args '[\"0xToken\",\"1000000\"]' --sma <SMA>
84
- ```
85
-
86
- Any shell — `--args-file` avoids quoting entirely:
87
- ```json
88
- ["0xToken", "1000000"]
89
- ```
90
- ```bash
91
- sailor mandate deploy --contract <Name> --args-file args.json --sma <SMA>
92
- ```
93
-
94
- Before AUTHORIZING (attaching) the permission, BACK the plain-English claims with an actual on-chain probe. `evaluate()` lives on the deployed contract, so deploy first, then — before the irreversible authorization — generate sample calls from the user's stated strategy (ones the permission MUST accept and ones it MUST reject) and run them through `sailor mandate simulate`. This is an off-chain `eth_call` (no gas, no signing) that reports what the permission's `evaluate()` returns for each call, and flags any target with no contract code (a wrong or wrong-chain address):
95
-
96
- ```bash
97
- # one call inline, or a batch via JSON
98
- sailor mandate simulate --address <PermissionOrName> --sma <SMA> \
99
- --target <addr> --calldata <hex> --expect pass
100
- sailor mandate simulate --address <PermissionOrName> --sma <SMA> --calls calls.json
101
- ```
102
-
103
- `calls.json` is an array of `{ target, calldata, value?, expect: "pass"|"fail", label }`. A mismatch between `expect` and the actual result exits non-zero — do not authorize until every sample matches. Simulate proves what the permission DOES; it does not guarantee it is correct.
104
-
105
- ```bash
106
- sailor mandate attach --address <PermissionOrName> --sma <SMA> # authorize, once simulate is clean
107
- ```
108
-
109
- Registration requires the owner to sign in the browser. If the wrong wallet is connected, the CLI rejects it.
110
-
111
- ## Stage 4 — Run
112
-
113
- Your agent starts executing within its mandate — locally on a schedule or via GitHub Actions. No per-transaction confirmation. The mandate is the authorization.
114
-
115
- ```bash
116
- sailor run # local, continuous
117
- sailor run --once # single tick — confirm it works before automating
118
- ```
119
-
120
- For GitHub Actions:
121
-
122
- 1. Run `sailor keys export-ci` — copies your encrypted agent wallet to `ci-keystore.json` in the project root and adds it to `.gitignore` as an allowed file. The keystore is geth v3 encrypted; the raw private key is never exposed.
123
- 2. Commit the required files. CI needs these non-secret files to be in the repo:
124
- ```bash
125
- npm install # generate package-lock.json if it doesn't exist
126
- git add ci-keystore.json package-lock.json .sail/account.json .sail/config.json .sail/mandate.json
127
- git commit -m "chore: add CI keystore and sail state" && git push
128
- ```
129
- `package-lock.json` is required by `npm ci` (used in the workflow). `.sail/account.json`, `.sail/config.json`, and `.sail/mandate.json` contain only public addresses and flags — no secrets. The `.gitignore` already has `!` exceptions for all of these.
130
- 3. Add two secrets in GitHub (Settings → Secrets → Actions):
131
- - `SAIL_PASSPHRASE` — the passphrase that encrypts your agent wallet
132
- - `RPC_URL` — your RPC endpoint
133
- 4. Install the `gh` CLI — required to manage the workflow from the terminal (trigger runs, check logs, add secrets without opening the browser):
134
- - macOS: `brew install gh`
135
- - Windows: `winget install --id GitHub.cli` or `scoop install gh`
136
- - Linux: see https://github.com/cli/cli/blob/trunk/docs/install_linux.md
137
- Then authenticate with the `workflow` scope:
138
- ```bash
139
- gh auth login --scopes workflow
140
- ```
141
- The `workflow` scope is required — without it, `gh` cannot trigger or inspect Actions runs. Verify with:
142
- ```bash
143
- gh auth status # confirm workflow scope is listed
144
- gh workflow run agent-tick.yml # manual trigger
145
- gh run list --workflow agent-tick.yml # check run history
146
- ```
147
-
148
- The scaffolded workflow at `.github/workflows/agent-tick.yml` picks up `ci-keystore.json`, unlocks it with `SAIL_PASSPHRASE`, and runs on the configured schedule. No private key ever appears in the workflow or in secrets.
149
-
150
- ## Stage 5 — Extend
151
-
152
- I can set up notifications (Telegram, email, or other) for runs and transactions, and build you a custom dashboard tailored to your strategy — a price chart and portfolio view for a trading agent, health-factor and yield for a lending agent.
153
-
154
- These are things the coding assistant builds on request — not Sailor features. Raise them once the agent is live; build on request.
155
-
156
- ## Signing (for custom runners)
157
-
158
- Use `buildDispatchSignature` from `@sail.money/sdk` — it reads the on-chain `DISPATCH_TYPEHASH` and builds the correct typed data. Never hand-roll the EIP-712 struct or hardcode the dispatch model.
43
+ Determine the user's progress by reading `.sail/` — do not ask; read it.
159
44
 
160
- ## What NOT to do
45
+ | File | What it tells you |
46
+ |---|---|
47
+ | `config.json` | Project manifest: name, `chainId` (null until the user picks a chain), contract addresses |
48
+ | `account.json` | Active SMA: safe, owner, manager (agent wallet), chainId, saltNonce, deployedChains |
49
+ | `mandate.json` | The signed mandate the runner executes against (absent = not signed yet) |
50
+ | `keys/` | Encrypted geth-v3 keystores (agent wallet, mandate signer) — never read or print contents |
51
+ | `state/mandates.json` | Append-only record of every permission deployed/attached from this project |
52
+ | `runtime/` | Live process state: `ui.json` (dashboard pid/port), `server.json` (signing station url/pid) |
53
+ | `activity.jsonl` | Unified activity log — agent dispatches and owner signing decisions, one JSON per line |
54
+ | `.env.local` | RPC_URL / CHAIN_ID / per-chain RPC vars / SAIL_PASSPHRASE — never commit or print |
55
+
56
+ ## Skills
57
+
58
+ Detailed procedures live in skills. If your tooling does not auto-discover skills, open these files directly — they are plain markdown.
59
+
60
+ | Skill | Load when | Path |
61
+ |---|---|---|
62
+ | sail-onboarding | New project setup, or resuming a partially set-up project | `.agents/skills/sail-onboarding/SKILL.md` |
63
+ | sail-project-info | Any question about project, account, mandate, chain, or environment state | `.agents/skills/sail-project-info/SKILL.md` |
64
+ | sail-servers | Starting, stopping, or health-checking the dashboard or signing station | `.agents/skills/sail-servers/SKILL.md` |
65
+ | sail-transactions | Building dispatches or any EVM transaction for the agent | `.agents/skills/sail-transactions/SKILL.md` |
66
+ | sail-mandates | Designing, authoring, testing, deploying, or authorizing permission contracts | `.agents/skills/sail-mandates/SKILL.md` |
67
+ | sail-ci | Automating the agent on a schedule via GitHub Actions | `.agents/skills/sail-ci/SKILL.md` |
68
+ | sail-extend | Notifications or a custom dashboard, once the agent is live | `.agents/skills/sail-extend/SKILL.md` |
69
+
70
+ ## Invariants — apply to every turn
161
71
 
162
72
  - Do not present the welcome and immediately launch the UI — wait for the second "start"
163
- - Do not describe, mention, or present any code in `src/` or `examples/` as the user's strategy — treat Stage 2 as a blank slate; ask what they want
73
+ - Do not describe, mention, or present any code in `src/` or `examples/` as the user's strategy — treat strategy definition as a blank slate; ask what they want
164
74
  - Do not ask a running agent to confirm individual dispatches within its mandate
165
75
  - Do not put an owner key in the terminal — owner signing is browser-only
166
- - Do not hand-roll dispatch EIP-712 signatures — use `buildDispatchSignature`
167
- - Do not hardcode the dispatch model — detect it on-chain
76
+ - Do not hand-roll dispatch EIP-712 signatures — use `buildDispatchSignature` from the SDK
77
+ - Do not hardcode the dispatch model — detect it on-chain with `detectKernelCapabilities`
168
78
  - Do not present example permissions as audited or as a supported menu
169
79
  - Do not commit `SAIL_PASSPHRASE` or private keys
170
- - Do not write a supply or deposit permission without also deploying a bounded-approve permission for each token the agent will approve approve calls have no mandate coverage otherwise
80
+ - ERC-20 `approve()` calls are NOT covered by supply, swap, or deposit permissions every approve the strategy makes needs explicit coverage. Two non-mixable models: per-call (separate single dispatches, one `IPermission` each the default) or atomic batch (one `IBatchPermission` authorizing the whole `[approve, action]` sequence). A normal `IPermission` cannot authorize a batch. Details: `.agents/skills/sail-mandates/references/approvals.md`
81
+ - Never authorize (attach) a permission before `forge test` and `sailor mandate simulate` both pass against samples derived from the user's strategy
171
82
  - Do not pass `--args` inline JSON from PowerShell — use `--args-file` instead
@@ -0,0 +1,73 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ import {Context} from "@sail/interfaces/IPermission.sol";
5
+ import {BoundedCallPermission} from "../mandates/BoundedCallPermission.sol";
6
+
7
+ /// Foundry tests for the scaffolded example permission. Runs with `forge test`
8
+ /// and needs no external libraries: a failed `require` reverts, and a reverting
9
+ /// test function is a test failure. (Add forge-std with `forge install
10
+ /// foundry-rs/forge-std` if you want richer assertions and cheatcodes.)
11
+ ///
12
+ /// When you author a permission in mandates/, copy this file and derive the
13
+ /// cases from the user's strategy: every call the agent must be able to make
14
+ /// (evaluate returns true) and every bound it must not cross (returns false).
15
+ /// `forge test` must pass BEFORE `sailor mandate deploy`, and simulate must
16
+ /// pass before `sailor mandate attach`.
17
+ contract BoundedCallPermissionTest {
18
+ address internal constant ROUTER = 0x1111111111111111111111111111111111111111;
19
+ address internal constant UNKNOWN = 0x2222222222222222222222222222222222222222;
20
+ bytes4 internal constant ALLOWED_SELECTOR = bytes4(keccak256("exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))"));
21
+ bytes4 internal constant OTHER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
22
+
23
+ BoundedCallPermission internal permission;
24
+
25
+ function setUp() public {
26
+ address[] memory targets = new address[](1);
27
+ targets[0] = ROUTER;
28
+ bytes4[] memory selectors = new bytes4[](1);
29
+ selectors[0] = ALLOWED_SELECTOR;
30
+ permission = new BoundedCallPermission(targets, selectors, 0);
31
+ }
32
+
33
+ function _ctx(address target, bytes4 selector, uint256 value) internal view returns (Context memory) {
34
+ return Context({
35
+ account: address(0xACC0), // the Safe
36
+ manager: address(0xA9E7), // the agent wallet
37
+ submitter: address(0xA9E7),
38
+ target: target,
39
+ selector: selector,
40
+ value: value,
41
+ blockTimestamp: block.timestamp,
42
+ blockNumber: block.number
43
+ });
44
+ }
45
+
46
+ function test_AllowsBoundedCall() public view {
47
+ require(
48
+ permission.evaluate("", _ctx(ROUTER, ALLOWED_SELECTOR, 0)),
49
+ "must allow allowlisted target + selector with no ETH"
50
+ );
51
+ }
52
+
53
+ function test_RejectsUnknownTarget() public view {
54
+ require(
55
+ !permission.evaluate("", _ctx(UNKNOWN, ALLOWED_SELECTOR, 0)),
56
+ "must reject a target outside the allowlist"
57
+ );
58
+ }
59
+
60
+ function test_RejectsUnknownSelector() public view {
61
+ require(
62
+ !permission.evaluate("", _ctx(ROUTER, OTHER_SELECTOR, 0)),
63
+ "must reject a selector outside the allowlist"
64
+ );
65
+ }
66
+
67
+ function test_RejectsEthValueAboveMax() public view {
68
+ require(
69
+ !permission.evaluate("", _ctx(ROUTER, ALLOWED_SELECTOR, 1)),
70
+ "must reject ETH value above MAX_VALUE"
71
+ );
72
+ }
73
+ }
@@ -1,3 +0,0 @@
1
- import{F as o}from"./core-BJ5Wn_0H.js";import"./index-Bj9jEvxf.js";import"./events-HAbmebGY.js";import"./index.es-DIo7ubqt.js";import"./fallback-CUotDuSc.js";const l=o` <svg fill="none" viewBox="0 0 13 4">
2
- <path fill="currentColor" d="M.5 0h12L8.9 3.13a3.76 3.76 0 0 1-4.8 0L.5 0Z" />
3
- </svg>`;export{l as cursorSvg};