diff --git a/evaluations/icp-cli.json b/evaluations/icp-cli.json index e8656284..aba1b715 100644 --- a/evaluations/icp-cli.json +++ b/evaluations/icp-cli.json @@ -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" @@ -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" ] }, diff --git a/evaluations/vetkeys.json b/evaluations/vetkeys.json index 35090729..8a0259fb 100644 --- a/evaluations/vetkeys.json +++ b/evaluations/vetkeys.json @@ -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": { diff --git a/evaluations/wallet-integration.json b/evaluations/wallet-integration.json index 429bb131..4c2d0333 100644 --- a/evaluations/wallet-integration.json +++ b/evaluations/wallet-integration.json @@ -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", @@ -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": [ diff --git a/skills/encrypted-maps/SKILL.md b/skills/encrypted-maps/SKILL.md index 64de4d33..82ca0345 100644 --- a/skills/encrypted-maps/SKILL.md +++ b/skills/encrypted-maps/SKILL.md @@ -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 diff --git a/skills/icp-cli/SKILL.md b/skills/icp-cli/SKILL.md index aefc2559..1e414623 100644 --- a/skills/icp-cli/SKILL.md +++ b/skills/icp-cli/SKILL.md @@ -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:`) 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. diff --git a/skills/icp-cli/references/binding-generation.md b/skills/icp-cli/references/binding-generation.md index 438138b8..b1a839a4 100644 --- a/skills/icp-cli/references/binding-generation.md +++ b/skills/icp-cli/references/binding-generation.md @@ -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) @@ -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`. diff --git a/skills/icp-cli/references/dfx-migration.md b/skills/icp-cli/references/dfx-migration.md index cc01b383..eb962ce2 100644 --- a/skills/icp-cli/references/dfx-migration.md +++ b/skills/icp-cli/references/dfx-migration.md @@ -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 diff --git a/skills/vetkeys/SKILL.md b/skills/vetkeys/SKILL.md index 53e6ea8e..2400895b 100644 --- a/skills/vetkeys/SKILL.md +++ b/skills/vetkeys/SKILL.md @@ -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 @@ -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. diff --git a/skills/wallet-integration/SKILL.md b/skills/wallet-integration/SKILL.md index 33a26f83..6671324a 100644 --- a/skills/wallet-integration/SKILL.md +++ b/skills/wallet-integration/SKILL.md @@ -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 @@ -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