Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions evaluations/icp-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
"prompt": "How do I generate TypeScript bindings for my backend canister so I can call it from my React frontend?",
"expected_behaviors": [
"Does NOT suggest 'dfx generate' — it does not exist in icp-cli",
"Recommends @icp-sdk/bindgen with a version constraint (>= 0.3.0) or references references/binding-generation.md which contains the version",
"Mentions @icp-sdk/core with a version constraint (>= 5.0.0) or explicitly warns that there is no 0.x/1.x release",
"Recommends @icp-sdk/bindgen with an explicit version constraint, or references references/binding-generation.md which carries it",
"Pins @icp-sdk/core to ^5 — does NOT leave the install unpinned",
"Shows npm install commands for both packages (@icp-sdk/core and @icp-sdk/bindgen)",
"Mentions that the .did file must exist on disk before the frontend builds",
"Shows a Vite plugin setup using icpBindgen from @icp-sdk/bindgen, or references references/binding-generation.md for the full setup"
Expand Down Expand Up @@ -161,8 +161,8 @@
"icp.yaml has Motoko backend with @dfinity/motoko@v5.0.0 (no recipe.configuration block) and a version-pinned asset canister for the frontend",
"mops.toml has a [toolchain] section (concrete moc version) and a [canisters.backend] section with a main field",
"Uses 'mops generate candid backend' (or 'mops generate candid') as the command that produces the committed .did file — NOT the older 'mops build' + 'cp .mops/.build/backend.did' two-step",
"Uses @icp-sdk/bindgen (>= 0.3.0) Vite plugin with a didFile path pointing to the committed .did file",
"Uses @icp-sdk/core (>= 5.0.0) if a version is referenced — does NOT use a 0.x or 1.x version",
"Uses the @icp-sdk/bindgen Vite plugin with a didFile path pointing to the committed .did file",
"Pins @icp-sdk/core to ^5 if a version is referenced",
"Does NOT use dfx commands, dfx.json, .env files, or process.env for canister IDs"
]
},
Expand Down
9 changes: 9 additions & 0 deletions evaluations/vetkeys.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@
"Passes a DerivedPublicKey object as the first argument via `DerivedPublicKey.deserialize(publicKeyBytes)` — NOT raw public-key bytes",
"Passes the message and signature as the remaining arguments"
]
},
{
"name": "Adversarial: vetkeys peer range drifts core to 6",
"prompt": "My dapp already uses @icp-sdk/auth for Internet Identity login. Give me the npm install command to add the vetKeys frontend library. No integration code.",
"expected_behaviors": [
"Installs @icp-sdk/vetkeys — NOT the legacy @dfinity/vetkeys",
"Names @icp-sdk/core explicitly in the install, pinned to ^5",
"Explains that vetKeys accepts more than one @icp-sdk/core major, so an install that omits core resolves the highest one — which @icp-sdk/auth may not accept"
]
}
],
"trigger_evals": {
Expand Down
12 changes: 10 additions & 2 deletions evaluations/wallet-integration.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"skill": "wallet-integration",
"description": "Evaluation cases for the wallet-integration skill. Tests whether agents produce correct ICRC signer protocol code, avoid top-level await, and use the right wallet classes.",

"output_evals": [
{
"name": "No top-level await in wallet code",
Expand Down Expand Up @@ -41,9 +40,18 @@
"Registers ICRC25_REQUEST_PERMISSIONS and ICRC27_ACCOUNTS prompts",
"Registers ICRC21_CALL_CONSENT_MESSAGE and ICRC49_CALL_CANISTER prompts"
]
},
{
"name": "Adversarial: unpinned oisy-wallet-signer resolves core 4",
"prompt": "My dapp already uses @icp-sdk/auth for Internet Identity login. Give me the npm install command to add OISY wallet integration. No integration code.",
"expected_behaviors": [
"Pins @dfinity/oisy-wallet-signer below 6.0.0 (e.g. ^4.1.3)",
"Pins @icp-sdk/core to ^5",
"Does NOT give an unpinned install command for @dfinity/oisy-wallet-signer or @icp-sdk/core",
"Notes that the latest oisy-wallet-signer (6.0.0) peers @icp-sdk/core ^4 and is therefore incompatible with @icp-sdk/auth"
]
}
],

"trigger_evals": {
"description": "Queries to test whether the skill activates correctly.",
"should_trigger": [
Expand Down
6 changes: 4 additions & 2 deletions skills/encrypted-maps/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ Use the **`vetkeys` skill** instead when you need lower-level primitives: identi

| Layer | Rust | Motoko | Frontend |
|-------|------|--------|----------|
| Package | `ic-vetkeys` **0.9** | `ic-vetkeys` **0.6** (moc ≥ 1.13.0, core ≥ 2.6.1) | `@icp-sdk/vetkeys` **0.5** |
| Package | `ic-vetkeys` **0.9** | `ic-vetkeys` **0.6** (moc ≥ 1.13.0, core ≥ 2.6.1) | `@icp-sdk/vetkeys` **`^0.7`** |
| Backend | `export_encrypted_maps_canister!` macro | `EncryptedMapsCanister` mixin | `@icp-sdk/vetkeys/encrypted_maps` |

> Use `@icp-sdk/vetkeys` (≥0.5), not the legacy `@dfinity/vetkeys` (frozen at 0.4). Frontend agent/identity come from `@icp-sdk/core`, not `@dfinity/agent`.
> Use `@icp-sdk/vetkeys` **`^0.7`**, not the legacy `@dfinity/vetkeys` (deprecated on npm, frozen at 0.4). Frontend agent/identity come from `@icp-sdk/core`, not `@dfinity/agent`.

> **Name `@icp-sdk/core` in the install, pinned to `^5`.** vetKeys peers core as `^5 || ^6`, so installing it alone resolves core to `6.x`, while `@icp-sdk/auth` requires `^5`. Encrypted-storage apps authenticate users, so always install both: `npm i '@icp-sdk/vetkeys@^0.7' '@icp-sdk/core@^5'`.

## Concepts

Expand Down
2 changes: 1 addition & 1 deletion skills/icp-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm

11. **Expecting `output_env_file` or `.env` with canister IDs.** dfx writes canister IDs to a `.env` file (`CANISTER_ID_BACKEND=...`) via `output_env_file`. icp-cli does not generate `.env` files. Instead, it injects canister IDs as environment variables (`PUBLIC_CANISTER_ID:<name>`) directly into canisters during `icp deploy`. Frontends read these from the `ic_env` cookie set by the frontend canister (static-site or the legacy asset canister). Remove `output_env_file` from your config and any code that reads `CANISTER_ID_*` from `.env` — frontends use the `ic_env` cookie, and canister code reads the same variables at runtime (see Canister Environment Variables below and Pitfall 22).

12. **Expecting `dfx generate` for TypeScript bindings.** icp-cli does not have a `dfx generate` equivalent. Use `@icp-sdk/bindgen` (>= 0.3.0) with `@icp-sdk/core` (>= 5.0.0 — there is no 0.x or 1.x release) to generate TypeScript bindings from `.did` files at build time. Use `outDir: "./src/bindings"` so imports are clean (e.g., `./bindings/backend`). The `.did` file must exist on disk — either commit it to the repo, or generate it with `icp build` first (recipes auto-generate it when `candid` is not specified). See `references/binding-generation.md` for the full Vite plugin setup.
12. **Expecting `dfx generate` for TypeScript bindings.** icp-cli does not have a `dfx generate` equivalent. Use `@icp-sdk/bindgen` (>= 0.4.0) with `@icp-sdk/core` pinned to `^5` (do **not** take `latest` — `6.x` is published but `@icp-sdk/auth`/`@icp-sdk/signer` still peer on `^5`, so core 6 fails with `ERESOLVE`) to generate TypeScript bindings from `.did` files at build time. Use `outDir: "./src/bindings"` so imports are clean (e.g., `./bindings/backend`). The `.did` file must exist on disk — either commit it to the repo, or generate it with `icp build` first (recipes auto-generate it when `candid` is not specified). See `references/binding-generation.md` for the full Vite plugin setup.

13. **Passing `{ agent }` to `createActor` from `@icp-sdk/bindgen`.** The old `@dfinity/agent` pattern was `createActor(canisterId, { agent })`. The `@icp-sdk/bindgen` pattern is `createActor(canisterId, { agentOptions: { host, rootKey } })` — the binding creates the agent internally. Passing `{ agent }` to the new API **silently creates an anonymous identity** — no error is thrown, but calls return empty data or access denied. See `references/binding-generation.md` for the correct pattern.

Expand Down
10 changes: 5 additions & 5 deletions skills/icp-cli/references/binding-generation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Binding Generation

icp-cli does not have a built-in `dfx generate` command. Use `@icp-sdk/bindgen` (>= 0.3.0) to generate TypeScript bindings from `.did` files. It depends on `@icp-sdk/core` (>= 5.0.0).
icp-cli does not have a built-in `dfx generate` command. Use `@icp-sdk/bindgen` (>= 0.4.0) to generate TypeScript bindings from `.did` files. bindgen itself has no runtime dependency on `@icp-sdk/core` — the code it *generates* imports from `@icp-sdk/core` (`^5`).

## Vite plugin (recommended)

Expand Down Expand Up @@ -82,11 +82,11 @@ if (result !== null) { name = result; }

Install both packages in the frontend project (note the minimum versions):
```bash
npm install @icp-sdk/core@^5.0.0
npm install -D @icp-sdk/bindgen@^0.3.0
npm install '@icp-sdk/core@^5'
npm install -D '@icp-sdk/bindgen@^0.4.0'
```

**Important:** `@icp-sdk/core` starts at version 5.x — there is no 0.x or 1.x release. Do not guess a lower version.
**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** `6.x` is published, but `@icp-sdk/auth` and `@icp-sdk/signer` peer on `@icp-sdk/core@^5`, so core `6` alongside them fails with `ERESOLVE`. Do not reach for `--legacy-peer-deps` to get past it: that installs two copies of core in one tree, which degrades silently instead of failing. Every IC skill pins `@icp-sdk/core@^5` for this reason.

- The `.did` file must exist on disk before the frontend builds. The recommended workflow: generate the `.did` file once (see SKILL.md pitfall #16), commit it to the repo, and specify `candid:` in the recipe config. If `candid` is omitted, the recipe auto-generates the `.did` into the build cache at a non-deterministic path that bindgen cannot reference — so always commit the `.did` and set `candid:` when using bindgen.
- `@icp-sdk/bindgen` (>= 0.3.0) generates code that depends on `@icp-sdk/core` (>= 5.0.0). Projects using `@dfinity/agent` must upgrade to `@icp-sdk/core` + `@icp-sdk/bindgen`. This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on `@dfinity/agent`.
- `@icp-sdk/bindgen` (>= 0.4.0) generates code that depends on `@icp-sdk/core` (`^5`). Projects using `@dfinity/agent` must upgrade to `@icp-sdk/core` + `@icp-sdk/bindgen`. This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on `@dfinity/agent`.
2 changes: 1 addition & 1 deletion skills/icp-cli/references/dfx-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ createActor(canisterEnv?.["PUBLIC_CANISTER_ID:backend"], {

Steps:
1. `npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environment`
2. `npm install @icp-sdk/core@^5.0.0 @icp-sdk/bindgen@^0.3.0`
2. `npm install '@icp-sdk/core@^5' '@icp-sdk/bindgen@^0.4.0'` — pin `^5`; core `6.x` is published but `@icp-sdk/auth`/`@icp-sdk/signer` still peer on `^5`
3. Delete `src/declarations/` (dfx-generated bindings)
4. Add `**/src/bindings/` to `.gitignore`
5. Commit the `.did` file(s) used by bindgen
Expand Down
14 changes: 11 additions & 3 deletions skills/vetkeys/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@ Build on the maintained libraries — do not hand-roll the cryptography or the C

| Layer | Rust | Motoko | Frontend |
|-------|------|--------|----------|
| Package | `ic-vetkeys` **0.9** ([crates.io](https://crates.io/crates/ic-vetkeys)) | `ic-vetkeys` **0.6** ([mops](https://mops.one/ic-vetkeys)) | `@icp-sdk/vetkeys` **0.5** ([npm](https://www.npmjs.com/package/@icp-sdk/vetkeys)) |
| Package | `ic-vetkeys` **0.9** ([crates.io](https://crates.io/crates/ic-vetkeys)) | `ic-vetkeys` **0.6** ([mops](https://mops.one/ic-vetkeys)) | `@icp-sdk/vetkeys` **`^0.7`** ([npm](https://www.npmjs.com/package/@icp-sdk/vetkeys)) |
| Management API | `ic-cdk-management-canister`, `ic_vetkeys::management_canister` | `mo:ic-vetkeys/ManagementCanister` | — |
| Low-level primitives | crate root (`ic_vetkeys::…`) | — (**not available**, see below) | package root (`@icp-sdk/vetkeys`) |

> **`@dfinity/vetkeys` is legacy** (frozen at 0.4.0). The package was renamed to `@icp-sdk/vetkeys` at 0.5.0. Frontend agent/identity types come from `@icp-sdk/core` (`@icp-sdk/core/agent`, `@icp-sdk/core/principal`), **not** `@dfinity/agent`/`@dfinity/principal`.

Also required: Rust `ic-cdk = "0.20"` + `ic-cdk-management-canister = "0.1"` (and `ic-dummy-getrandom-for-wasm` for IBE); Motoko `ic-vetkeys` 0.6 needs `moc ≥ 1.13.0` / `core ≥ 2.6.1`; frontend also `@icp-sdk/core ^5.4`.
> **Name `@icp-sdk/core` in the install, pinned to `^5`.** vetKeys peers core as `^5 || ^6`, so installing it alone resolves core to `6.x`, while `@icp-sdk/auth` and `@icp-sdk/signer` require `^5`. Pin both so the project stays on one major:
>
> ```bash
> npm i '@icp-sdk/vetkeys@^0.7' '@icp-sdk/core@^5'
> ```
>
> Use `0.7.0` or later. `0.5.0`/`0.6.0` declared core as a plain dependency, so they could install a second copy of it alongside auth instead of failing. The API is unchanged across all three.

Also required: Rust `ic-cdk = "0.20"` + `ic-cdk-management-canister = "0.1"` (and `ic-dummy-getrandom-for-wasm` for IBE); Motoko `ic-vetkeys` 0.6 needs `moc ≥ 1.13.0` / `core ≥ 2.6.1`; frontend `@icp-sdk/vetkeys@^0.7` with `@icp-sdk/core@^5` installed explicitly as its peer (see the pin note above).

## Which skill / which feature

Expand Down Expand Up @@ -198,7 +206,7 @@ A vetKey can be turned into **verifiable randomness**: a Rust canister calls `ic

## Pitfalls

1. **Wrong package / imports.** Use `@icp-sdk/vetkeys` (≥0.5), not `@dfinity/vetkeys` (frozen at 0.4). Import agent/identity from `@icp-sdk/core` (`@icp-sdk/core/agent`, `@icp-sdk/core/principal`), and build the agent with `await HttpAgent.create({ identity, host, rootKey })` — the client classes take a ready `HttpAgent`, not options. Get `rootKey` from `safeGetCanisterEnv()` (`@icp-sdk/core/agent/canister-env`); never call `fetchRootKey()` in shipped code (see the `icp-cli` skill).
1. **Wrong package / imports.** Use `@icp-sdk/vetkeys` `^0.7` and install `@icp-sdk/core@^5` alongside it — vetKeys peers `^5 || ^6`, so an unpinned install lands on core 6 while auth needs 5 (see the pin note under the version table). Do not use `@dfinity/vetkeys` (deprecated on npm, frozen at 0.4). Import agent/identity from `@icp-sdk/core` (`@icp-sdk/core/agent`, `@icp-sdk/core/principal`), and build the agent with `await HttpAgent.create({ identity, host, rootKey })` — the client classes take a ready `HttpAgent`, not options. Get `rootKey` from `safeGetCanisterEnv()` (`@icp-sdk/core/agent/canister-env`); never call `fetchRootKey()` in shipped code (see the `icp-cli` skill).

2. **`toDerivedKeyMaterial()` does not exist.** For symmetric encryption: `const dkm = await vetKey.asDerivedKeyMaterial()`, then `await dkm.encryptMessage(msg, domainSep, associatedData)` / `await dkm.decryptMessage(ct, domainSep, associatedData)` (all async). Never use the raw decrypted vetKey bytes directly as an AES key.

Expand Down
13 changes: 10 additions & 3 deletions skills/wallet-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,19 @@ This skill covers integration using `@dfinity/oisy-wallet-signer`. Other integra

## Prerequisites

- `@dfinity/oisy-wallet-signer` (>= 4.1.0)
- Peer dependencies: `@dfinity/utils` (>= 4.2.0), `@dfinity/zod-schemas` (>= 3.2.0), `@icp-sdk/canisters` (>= 3.5.0), `@icp-sdk/core` (>= 5.0.0), `zod`
- `@dfinity/oisy-wallet-signer` — pin to `^4.1.3`. **Do not install `6.0.0`** (the current `latest`); see pitfall 11.
- Peer dependencies of `4.1.3`: `@dfinity/utils` (`^4.2.1`), `@dfinity/zod-schemas` (`^3`), `@icp-sdk/canisters` (`^3.2`), `@icp-sdk/core` (`^5`), `zod` (`^4`)
- A non-anonymous identity on the signer side (e.g. `Ed25519KeyIdentity`)

Pin both the signer and `@icp-sdk/core`, or npm resolves a tree two majors below the rest of the IC skills:

```bash
npm i @dfinity/oisy-wallet-signer @dfinity/utils @dfinity/zod-schemas @icp-sdk/canisters @icp-sdk/core zod
npm i '@dfinity/oisy-wallet-signer@^4.1.3' @dfinity/utils @dfinity/zod-schemas \
@icp-sdk/canisters '@icp-sdk/core@^5' zod
```

The pin that matters here is `@dfinity/oisy-wallet-signer`: its `latest` (`6.0.0`) peers `@icp-sdk/core@^4` and drags the whole tree down a major. Pinned below `6.0.0`, this stack resolves on the 5.x line that `@icp-sdk/auth` and `@icp-sdk/canisters` also require, so wallet integration and Internet Identity login coexist. `@icp-sdk/core` is named explicitly for the same reason every IC skill names it — see the `icp-cli` skill for the repo-wide rationale.

## How It Works

### End-to-End Lifecycle
Expand Down Expand Up @@ -107,6 +112,8 @@ npm i @dfinity/oisy-wallet-signer @dfinity/utils @dfinity/zod-schemas @icp-sdk/c

10. **Auto-triggering signing on connect.** Never fire a canister call immediately after `connect()`. Let the user initiate the action. The signer is designed for intentional, user-driven operations.

11. **Installing `@dfinity/oisy-wallet-signer` unpinned.** The `latest` tag is `6.0.0`, whose peer dependencies moved *backwards*: `@icp-sdk/core` from `^5` to `^4`, `@dfinity/utils` to `~4.0.3`, `@icp-sdk/canisters` to `~3.1.0`. An unpinned `npm i` therefore silently resolves `@icp-sdk/core@4.2.3` and downgrades the rest — two majors below every other IC skill, and unusable alongside `@icp-sdk/auth` (peer `@icp-sdk/core@^5`), which fails with `ERESOLVE`. Pin `^4.1.3`, the last release that peers `@icp-sdk/core@^5`.

## Implementation

### Import Map
Expand Down
Loading