From 2c58ba87dcd5c5fe1a77291198f7004a86afe5d7 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 26 Aug 2026 15:34:11 +0200 Subject: [PATCH 1/5] fix: pin @icp-sdk/core to ^5 across skills and pin oisy-wallet-signer below 6.0.0 skills/wallet-integration installed @icp-sdk/core unpinned, which resolves to 4.2.3 today -- two majors below every other skill, and unusable alongside @icp-sdk/auth (peer @icp-sdk/core@^5). Cause: @dfinity/oisy-wallet-signer 6.0.0 regressed its peers (core ^5 -> ^4, utils -> ~4.0.3, canisters -> ~3.1.0), and the unpinned install now takes it. - wallet-integration: pin oisy ^4.1.3 and core ^5; Prerequisites now list the peers 4.1.3 actually declares; new pitfall 11 documents the 6.0.0 regression - binding-generation.md: replace the incorrect "core starts at 5.x, there is no 0.x or 1.x release" claim with the real version line and why 6.x must not be taken; correct "bindgen depends on core" (it has no core dependency -- the code it generates imports core); bindgen floor -> ^0.4.0 - dfx-migration.md, icp-cli/SKILL.md pitfall 12: pin ^5 with the ERESOLVE reason Verified: the wallet-integration install line now resolves with @icp-sdk/auth present, one copy of @icp-sdk/core at 5.4.0, no ERESOLVE. Closes #364 --- evaluations/icp-cli.json | 8 ++++---- evaluations/wallet-integration.json | 12 ++++++++++-- skills/icp-cli/SKILL.md | 2 +- skills/icp-cli/references/binding-generation.md | 10 +++++----- skills/icp-cli/references/dfx-migration.md | 2 +- skills/wallet-integration/SKILL.md | 13 ++++++++++--- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/evaluations/icp-cli.json b/evaluations/icp-cli.json index e8656284..12c10d65 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 a version constraint (>= 0.4.0) or references references/binding-generation.md which contains the version", + "Pins @icp-sdk/core to ^5 (equivalently >= 5 and < 6) — does NOT recommend 6.x and 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 @icp-sdk/bindgen (>= 0.4.0) 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 6.x, and does NOT use a 0.x or 1.x version", "Does NOT use dfx commands, dfx.json, .env files, or process.env for canister IDs" ] }, 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/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..69877f4f 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`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core — which breaks `instanceof` checks on `Principal` and `HttpAgent` across package boundaries. Every IC skill pins `^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/wallet-integration/SKILL.md b/skills/wallet-integration/SKILL.md index 33a26f83..97ff231c 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 ``` +`@icp-sdk/core` is pinned to `^5` across every IC skill: `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/vetkeys` all still require `^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published but nothing else supports it yet. + ## 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 From 4d4e03b9aad005357968ddc7a9c4d968decedd8c Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 26 Aug 2026 15:50:09 +0200 Subject: [PATCH 2/5] fix: pin @icp-sdk/vetkeys to ^0.5 and clarify the core 5.x anchor @icp-sdk/vetkeys 0.6.0 shipped and depends on @icp-sdk/core@^6.1.0, while @icp-sdk/auth, @icp-sdk/signer and @icp-sdk/canisters still peer on ^5. Because vetKeys declares core as a dependency rather than a peer, npm raises no ERESOLVE -- it silently installs two copies of @icp-sdk/core. The vetKeys clients take a caller-supplied HttpAgent, so an agent built from core 5 is handed to code typed against core 6 and Principal/HttpAgent identity no longer matches across that boundary. Both skills previously said "@icp-sdk/vetkeys (>=0.5)", which permits 0.6.0. - vetkeys, encrypted-maps: pin ^0.5, document why 0.6.0 must not be installed - wallet-integration: the claim that vetkeys "still requires ^5" is now false; rewritten, and notes that oisy 6.0.0 and vetkeys 0.6.0 leave the 5.x line in opposite directions - binding-generation.md, wallet-integration: "pins ^5" -> "anchors core to the 5.x line", addressing the review (vetkeys/SKILL.md uses ^5.4) - new adversarial eval: vetkeys 0.6.0 duplicates @icp-sdk/core Verified: the full skill surface (auth + vetkeys + wallet + canisters) resolves with exactly one @icp-sdk/core at 5.4.0. --- evaluations/vetkeys.json | 10 ++++++++++ skills/encrypted-maps/SKILL.md | 6 ++++-- skills/icp-cli/references/binding-generation.md | 2 +- skills/vetkeys/SKILL.md | 8 +++++--- skills/wallet-integration/SKILL.md | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/evaluations/vetkeys.json b/evaluations/vetkeys.json index 35090729..9502d5dc 100644 --- a/evaluations/vetkeys.json +++ b/evaluations/vetkeys.json @@ -46,6 +46,16 @@ "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 0.6.0 duplicates @icp-sdk/core", + "prompt": "My dapp 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": [ + "Pins @icp-sdk/vetkeys to ^0.5 — does NOT install 0.6.0 or leave the version unpinned", + "Explains that @icp-sdk/vetkeys 0.6.0 requires @icp-sdk/core ^6 while @icp-sdk/auth requires ^5", + "Warns that the result is duplicated/mismatched @icp-sdk/core copies rather than a clean install failure", + "Installs @icp-sdk/vetkeys — NOT the legacy @dfinity/vetkeys" + ] } ], "trigger_evals": { diff --git a/skills/encrypted-maps/SKILL.md b/skills/encrypted-maps/SKILL.md index 64de4d33..adbb8772 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.5`** — **not `0.6`** | | 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` pinned to **`^0.5`**, not the legacy `@dfinity/vetkeys` (frozen at 0.4). Frontend agent/identity come from `@icp-sdk/core`, not `@dfinity/agent`. + +> **Do not install `@icp-sdk/vetkeys@0.6.0`** even though it is `latest`. It depends on `@icp-sdk/core@^6.1.0`, while `@icp-sdk/auth`/`@icp-sdk/signer`/`@icp-sdk/canisters` still peer on `@icp-sdk/core@^5`. Because core is a dependency rather than a peer there, npm installs **two** copies of `@icp-sdk/core` with no error, and the `HttpAgent`/`Principal` you pass into the EncryptedMaps client comes from the wrong one. Install `'@icp-sdk/vetkeys@^0.5'`. Encrypted-storage apps authenticate users, so this always applies. ## Concepts diff --git a/skills/icp-cli/references/binding-generation.md b/skills/icp-cli/references/binding-generation.md index 69877f4f..2e8e7376 100644 --- a/skills/icp-cli/references/binding-generation.md +++ b/skills/icp-cli/references/binding-generation.md @@ -86,7 +86,7 @@ npm install '@icp-sdk/core@^5' npm install -D '@icp-sdk/bindgen@^0.4.0' ``` -**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core — which breaks `instanceof` checks on `Principal` and `HttpAgent` across package boundaries. Every IC skill pins `^5` for this reason. +**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core — which breaks `instanceof` checks on `Principal` and `HttpAgent` across package boundaries. Every IC skill anchors core to the **5.x line** for this reason (written `^5` here, `^5.4` where a later minimum is needed). - 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.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/vetkeys/SKILL.md b/skills/vetkeys/SKILL.md index 53e6ea8e..d109c9d0 100644 --- a/skills/vetkeys/SKILL.md +++ b/skills/vetkeys/SKILL.md @@ -16,13 +16,15 @@ 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.5`** ([npm](https://www.npmjs.com/package/@icp-sdk/vetkeys)) — **not `0.6`**, see below | | 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`. +> **Pin `@icp-sdk/vetkeys` to `^0.5` — do not install `0.6.0`.** `0.6.0` is the current `latest`, and it depends on `@icp-sdk/core@^6.1.0`, whereas `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all still peer on `@icp-sdk/core@^5`. vetKeys declares core as a **dependency**, not a peer, so npm does not raise `ERESOLVE` — it silently installs **two** copies of `@icp-sdk/core` (6.1.0 nested for vetKeys, 5.4.0 for auth). The vetKeys clients take a caller-supplied agent (`constructor(agent: HttpAgent, canisterId: string)`), so an `HttpAgent` built from core 5 is passed into code typed against core 6, and `Principal`/`HttpAgent` class identity no longer matches across that boundary. Install `'@icp-sdk/vetkeys@^0.5'` explicitly. The pin can be dropped only for an app that uses no other `@icp-sdk` package — which excludes anything with Internet Identity login. + +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.5` with `@icp-sdk/core@^5.4` (the 5.x line — see the pin note above). ## Which skill / which feature @@ -198,7 +200,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` pinned to `^0.5` (**not** `0.6.0`, which pulls `@icp-sdk/core@^6` alongside auth's `^5` — see the pin note under the version table), 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). 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 97ff231c..b1d33308 100644 --- a/skills/wallet-integration/SKILL.md +++ b/skills/wallet-integration/SKILL.md @@ -63,7 +63,7 @@ npm i '@dfinity/oisy-wallet-signer@^4.1.3' @dfinity/utils @dfinity/zod-schemas \ @icp-sdk/canisters '@icp-sdk/core@^5' zod ``` -`@icp-sdk/core` is pinned to `^5` across every IC skill: `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/vetkeys` all still require `^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published but nothing else supports it yet. +Every IC skill anchors `@icp-sdk/core` to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all peer on `@icp-sdk/core@^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published, but the only package that has moved to it is `@icp-sdk/vetkeys@0.6.0` — which is why the `vetkeys` and `encrypted-maps` skills pin `@icp-sdk/vetkeys@^0.5`. Two packages' `latest` tags leave the 5.x line in opposite directions: oisy `6.0.0` falls back to core `^4`, vetKeys `0.6.0` jumps to core `^6`. Pin both. ## How It Works From 101e48087597331a331f47a8989593d5fc11ba78 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 26 Aug 2026 21:20:52 +0200 Subject: [PATCH 3/5] fix: adopt @icp-sdk/vetkeys 0.7.0 and correct the duplicate-core consequence 0.7.0 moves @icp-sdk/core from a dependency to a peer spanning both majors (^5.0.0 || ^6.0.0), so vetKeys no longer forces a core version and coexists with @icp-sdk/auth on a single core. Verified: vetkeys 0.7 + auth resolves to one @icp-sdk/core at 5.4.0, fully deduped. The explicit core pin now matters more, not less: because core is a peer, `npm i @icp-sdk/vetkeys` alone resolves it to 6.1.0 (highest in range), while auth/signer/oisy still require ^5. - vetkeys, encrypted-maps: version tables and prose 0.5 -> 0.7; pin notes rewritten around the peer range instead of the duplication that 0.6.0 caused - wallet-integration: drop the claim that vetkeys requires ^5 (it accepts both) - binding-generation.md: correct the duplicate-core consequence. The previous text claimed it breaks instanceof on Principal/HttpAgent, which overstates it -- core is duck-typed (candid tests _isPrincipal, Principal.isPrincipal has a structural fallback). The real effects are narrower and quieter: the effectiveTarget instanceof Principal shim in pollForResponse stops matching, and error instanceof AgentError fails so reject errors lose enrichment. - eval case rewritten: the 0.6.0 duplication no longer exists; it now tests that core is named explicitly at ^5 against the ^5 || ^6 peer range No code-sample changes: 0.5.0 -> 0.7.0 is API-identical (public typings diff is empty, 69 declarations either side). --- evaluations/vetkeys.json | 11 +++++------ skills/encrypted-maps/SKILL.md | 6 +++--- skills/icp-cli/references/binding-generation.md | 2 +- skills/vetkeys/SKILL.md | 14 ++++++++++---- skills/wallet-integration/SKILL.md | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/evaluations/vetkeys.json b/evaluations/vetkeys.json index 9502d5dc..db68b7b2 100644 --- a/evaluations/vetkeys.json +++ b/evaluations/vetkeys.json @@ -48,13 +48,12 @@ ] }, { - "name": "Adversarial: vetkeys 0.6.0 duplicates @icp-sdk/core", - "prompt": "My dapp uses @icp-sdk/auth for Internet Identity login. Give me the npm install command to add the vetKeys frontend library. No integration code.", + "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": [ - "Pins @icp-sdk/vetkeys to ^0.5 — does NOT install 0.6.0 or leave the version unpinned", - "Explains that @icp-sdk/vetkeys 0.6.0 requires @icp-sdk/core ^6 while @icp-sdk/auth requires ^5", - "Warns that the result is duplicated/mismatched @icp-sdk/core copies rather than a clean install failure", - "Installs @icp-sdk/vetkeys — NOT the legacy @dfinity/vetkeys" + "Installs @icp-sdk/vetkeys — NOT the legacy @dfinity/vetkeys", + "Names @icp-sdk/core explicitly in the install, pinned to ^5, rather than letting it resolve implicitly", + "Explains that @icp-sdk/vetkeys peers @icp-sdk/core as ^5 || ^6, so an install that omits core resolves it to 6.x while @icp-sdk/auth still requires ^5" ] } ], diff --git a/skills/encrypted-maps/SKILL.md b/skills/encrypted-maps/SKILL.md index adbb8772..e846b3c9 100644 --- a/skills/encrypted-maps/SKILL.md +++ b/skills/encrypted-maps/SKILL.md @@ -16,12 +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`** — **not `0.6`** | +| 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` pinned to **`^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`. -> **Do not install `@icp-sdk/vetkeys@0.6.0`** even though it is `latest`. It depends on `@icp-sdk/core@^6.1.0`, while `@icp-sdk/auth`/`@icp-sdk/signer`/`@icp-sdk/canisters` still peer on `@icp-sdk/core@^5`. Because core is a dependency rather than a peer there, npm installs **two** copies of `@icp-sdk/core` with no error, and the `HttpAgent`/`Principal` you pass into the EncryptedMaps client comes from the wrong one. Install `'@icp-sdk/vetkeys@^0.5'`. Encrypted-storage apps authenticate users, so this always applies. +> **Install `@icp-sdk/core` explicitly at `^5`.** vetKeys `0.7.0` peers core as `^5.0.0 || ^6.0.0`, so `npm i @icp-sdk/vetkeys` alone resolves core to `6.1.0` — while `@icp-sdk/auth` still requires `^5`. Encrypted-storage apps authenticate users, so always install both together: `npm i '@icp-sdk/vetkeys@^0.7' '@icp-sdk/core@^5'`. (Avoid `0.5.0`/`0.6.0`, which declared core as a regular dependency; `0.6.0` beside auth silently produced two copies of core.) ## Concepts diff --git a/skills/icp-cli/references/binding-generation.md b/skills/icp-cli/references/binding-generation.md index 2e8e7376..d73eb1ab 100644 --- a/skills/icp-cli/references/binding-generation.md +++ b/skills/icp-cli/references/binding-generation.md @@ -86,7 +86,7 @@ npm install '@icp-sdk/core@^5' npm install -D '@icp-sdk/bindgen@^0.4.0' ``` -**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core — which breaks `instanceof` checks on `Principal` and `HttpAgent` across package boundaries. Every IC skill anchors core to the **5.x line** for this reason (written `^5` here, `^5.4` where a later minimum is needed). +**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core in one tree. Most of core is duck-typed, so values still cross (candid tests `_isPrincipal`, and `Principal.isPrincipal` has a structural fallback) — the breakage is narrower and quieter: the `effectiveTarget instanceof Principal` compatibility shim in `pollForResponse` stops matching, and `error instanceof AgentError` fails so reject errors lose their enrichment. Silent degradation, not a clean failure. Every IC skill anchors core to the **5.x line** for this reason (written `^5` here, `^5.4` where a later minimum is needed). - 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.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/vetkeys/SKILL.md b/skills/vetkeys/SKILL.md index d109c9d0..bf33c933 100644 --- a/skills/vetkeys/SKILL.md +++ b/skills/vetkeys/SKILL.md @@ -16,15 +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)) — **not `0.6`**, see below | +| 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`. -> **Pin `@icp-sdk/vetkeys` to `^0.5` — do not install `0.6.0`.** `0.6.0` is the current `latest`, and it depends on `@icp-sdk/core@^6.1.0`, whereas `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all still peer on `@icp-sdk/core@^5`. vetKeys declares core as a **dependency**, not a peer, so npm does not raise `ERESOLVE` — it silently installs **two** copies of `@icp-sdk/core` (6.1.0 nested for vetKeys, 5.4.0 for auth). The vetKeys clients take a caller-supplied agent (`constructor(agent: HttpAgent, canisterId: string)`), so an `HttpAgent` built from core 5 is passed into code typed against core 6, and `Principal`/`HttpAgent` class identity no longer matches across that boundary. Install `'@icp-sdk/vetkeys@^0.5'` explicitly. The pin can be dropped only for an app that uses no other `@icp-sdk` package — which excludes anything with Internet Identity login. +> **Install `@icp-sdk/core` explicitly, pinned to `^5`.** From `0.7.0`, vetKeys declares core as a **peer** dependency spanning both majors (`^5.0.0 || ^6.0.0`), so it no longer forces a core version — but that also means `npm i @icp-sdk/vetkeys` on its own resolves core to **6.1.0**, the highest in the range. `@icp-sdk/auth`, `@icp-sdk/signer` and `@dfinity/oisy-wallet-signer` still require `^5`, so name core explicitly and keep the whole project on one major: +> +> ```bash +> npm i '@icp-sdk/vetkeys@^0.7' '@icp-sdk/core@^5' +> ``` +> +> Avoid `0.5.0` and `0.6.0`: both declared core as a regular *dependency* rather than a peer, and `0.6.0` pinned it to `^6.1.0` — installing it beside `@icp-sdk/auth` silently produced two copies of `@icp-sdk/core` instead of an `ERESOLVE`. `0.7.0` is API-identical to `0.5.0`; only packaging changed. -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.5` with `@icp-sdk/core@^5.4` (the 5.x line — see the pin note above). +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.4` installed explicitly as its peer (the 5.x line — see the pin note above). ## Which skill / which feature @@ -200,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` pinned to `^0.5` (**not** `0.6.0`, which pulls `@icp-sdk/core@^6` alongside auth's `^5` — see the pin note under the version table), 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 b1d33308..074a21a2 100644 --- a/skills/wallet-integration/SKILL.md +++ b/skills/wallet-integration/SKILL.md @@ -63,7 +63,7 @@ npm i '@dfinity/oisy-wallet-signer@^4.1.3' @dfinity/utils @dfinity/zod-schemas \ @icp-sdk/canisters '@icp-sdk/core@^5' zod ``` -Every IC skill anchors `@icp-sdk/core` to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all peer on `@icp-sdk/core@^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published, but the only package that has moved to it is `@icp-sdk/vetkeys@0.6.0` — which is why the `vetkeys` and `encrypted-maps` skills pin `@icp-sdk/vetkeys@^0.5`. Two packages' `latest` tags leave the 5.x line in opposite directions: oisy `6.0.0` falls back to core `^4`, vetKeys `0.6.0` jumps to core `^6`. Pin both. +Every IC skill anchors `@icp-sdk/core` to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all peer on `@icp-sdk/core@^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published, but nothing in this stack requires it — `@icp-sdk/vetkeys` `0.7.0` accepts either major (`^5 || ^6`), which means an install that does not name core explicitly drifts to `6.x`. Name `@icp-sdk/core@^5` in the install command, and pin `@dfinity/oisy-wallet-signer` below `6.0.0` (its `latest` peers core `^4`). ## How It Works From 87339f53351d93642968efd79828aa1d96bd2182 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 26 Aug 2026 21:28:15 +0200 Subject: [PATCH 4/5] refactor: trim version archaeology from the pin guidance; make evals behavioural Two corrections after review feedback. 1. wallet-integration referenced @icp-sdk/vetkeys, which is both out of scope for that skill and false there: with oisy pinned below 6.0.0, the wallet stack resolves @icp-sdk/core to 5.4.0 even unpinned, because oisy 4.1.3 and @icp-sdk/canisters both peer ^5. The drift-to-6 only happens when vetkeys is in the project. The paragraph now names only the pin that matters here (oisy) and defers the repo-wide rationale to the icp-cli skill. 2. The pin notes carried package archaeology that changes nothing an agent types -- the core version history, the 0.5.0/0.6.0 packaging story, and the duck-typing internals behind the duplicate-core failure. Removed; what remains is the command, the reason, and the failure mode. Evals: version literals removed from every expected behaviour except the ^5 pin itself, which is the guidance. A case asserting a point-in-time snapshot of the npm ecosystem goes stale on every upstream release -- this one was rewritten three times in a day. Behavioural assertions ("names core explicitly rather than letting npm resolve it") survive; detecting that the pin itself has gone stale is what the version-coherence guard in #366 is for. --- evaluations/icp-cli.json | 8 ++++---- evaluations/vetkeys.json | 4 ++-- skills/encrypted-maps/SKILL.md | 2 +- skills/icp-cli/references/binding-generation.md | 2 +- skills/vetkeys/SKILL.md | 4 ++-- skills/wallet-integration/SKILL.md | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/evaluations/icp-cli.json b/evaluations/icp-cli.json index 12c10d65..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.4.0) or references references/binding-generation.md which contains the version", - "Pins @icp-sdk/core to ^5 (equivalently >= 5 and < 6) — does NOT recommend 6.x and does NOT leave the install unpinned", + "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.4.0) 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 6.x, and 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 db68b7b2..8a0259fb 100644 --- a/evaluations/vetkeys.json +++ b/evaluations/vetkeys.json @@ -52,8 +52,8 @@ "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, rather than letting it resolve implicitly", - "Explains that @icp-sdk/vetkeys peers @icp-sdk/core as ^5 || ^6, so an install that omits core resolves it to 6.x while @icp-sdk/auth still requires ^5" + "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" ] } ], diff --git a/skills/encrypted-maps/SKILL.md b/skills/encrypted-maps/SKILL.md index e846b3c9..82ca0345 100644 --- a/skills/encrypted-maps/SKILL.md +++ b/skills/encrypted-maps/SKILL.md @@ -21,7 +21,7 @@ Use the **`vetkeys` skill** instead when you need lower-level primitives: identi > 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`. -> **Install `@icp-sdk/core` explicitly at `^5`.** vetKeys `0.7.0` peers core as `^5.0.0 || ^6.0.0`, so `npm i @icp-sdk/vetkeys` alone resolves core to `6.1.0` — while `@icp-sdk/auth` still requires `^5`. Encrypted-storage apps authenticate users, so always install both together: `npm i '@icp-sdk/vetkeys@^0.7' '@icp-sdk/core@^5'`. (Avoid `0.5.0`/`0.6.0`, which declared core as a regular dependency; `0.6.0` beside auth silently produced two copies of core.) +> **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/references/binding-generation.md b/skills/icp-cli/references/binding-generation.md index d73eb1ab..27e87251 100644 --- a/skills/icp-cli/references/binding-generation.md +++ b/skills/icp-cli/references/binding-generation.md @@ -86,7 +86,7 @@ npm install '@icp-sdk/core@^5' npm install -D '@icp-sdk/bindgen@^0.4.0' ``` -**Important — pin `@icp-sdk/core` to `^5`; do not take `latest`.** The published line is `1.0.0-beta.x` → `4.0.0`–`4.2.3` → `5.x` → `6.x`, so neither "the lowest number you can guess" nor "whatever `latest` gives you" is correct. `6.x` exists, but `@icp-sdk/auth` and `@icp-sdk/signer` still declare a peer dependency on `@icp-sdk/core@^5`, so installing core `6` alongside them fails with `ERESOLVE`, and forcing it with `--legacy-peer-deps` installs two copies of core in one tree. Most of core is duck-typed, so values still cross (candid tests `_isPrincipal`, and `Principal.isPrincipal` has a structural fallback) — the breakage is narrower and quieter: the `effectiveTarget instanceof Principal` compatibility shim in `pollForResponse` stops matching, and `error instanceof AgentError` fails so reject errors lose their enrichment. Silent degradation, not a clean failure. Every IC skill anchors core to the **5.x line** for this reason (written `^5` here, `^5.4` where a later minimum is needed). +**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 anchors core to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). - 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.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/vetkeys/SKILL.md b/skills/vetkeys/SKILL.md index bf33c933..b5602113 100644 --- a/skills/vetkeys/SKILL.md +++ b/skills/vetkeys/SKILL.md @@ -22,13 +22,13 @@ Build on the maintained libraries — do not hand-roll the cryptography or the C > **`@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`. -> **Install `@icp-sdk/core` explicitly, pinned to `^5`.** From `0.7.0`, vetKeys declares core as a **peer** dependency spanning both majors (`^5.0.0 || ^6.0.0`), so it no longer forces a core version — but that also means `npm i @icp-sdk/vetkeys` on its own resolves core to **6.1.0**, the highest in the range. `@icp-sdk/auth`, `@icp-sdk/signer` and `@dfinity/oisy-wallet-signer` still require `^5`, so name core explicitly and keep the whole project on one major: +> **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' > ``` > -> Avoid `0.5.0` and `0.6.0`: both declared core as a regular *dependency* rather than a peer, and `0.6.0` pinned it to `^6.1.0` — installing it beside `@icp-sdk/auth` silently produced two copies of `@icp-sdk/core` instead of an `ERESOLVE`. `0.7.0` is API-identical to `0.5.0`; only packaging changed. +> 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.4` installed explicitly as its peer (the 5.x line — see the pin note above). diff --git a/skills/wallet-integration/SKILL.md b/skills/wallet-integration/SKILL.md index 074a21a2..6671324a 100644 --- a/skills/wallet-integration/SKILL.md +++ b/skills/wallet-integration/SKILL.md @@ -63,7 +63,7 @@ npm i '@dfinity/oisy-wallet-signer@^4.1.3' @dfinity/utils @dfinity/zod-schemas \ @icp-sdk/canisters '@icp-sdk/core@^5' zod ``` -Every IC skill anchors `@icp-sdk/core` to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). `@icp-sdk/auth`, `@icp-sdk/signer` and `@icp-sdk/canisters` all peer on `@icp-sdk/core@^5`, so an app combining wallet integration with Internet Identity login resolves cleanly. Core `6.x` is published, but nothing in this stack requires it — `@icp-sdk/vetkeys` `0.7.0` accepts either major (`^5 || ^6`), which means an install that does not name core explicitly drifts to `6.x`. Name `@icp-sdk/core@^5` in the install command, and pin `@dfinity/oisy-wallet-signer` below `6.0.0` (its `latest` peers core `^4`). +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 From e8dc66ef27cdc2b3d1a491daa3b13a14a90f670f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 26 Aug 2026 21:49:54 +0200 Subject: [PATCH 5/5] fix: unify the core pin on ^5; the ^5.4 floor is stale Review flagged that vetkeys/SKILL.md instructs `@icp-sdk/core@^5` in the install snippet but `^5.4` in the compatibility line. The suggested fix was to pick either value, but they are not equivalent: ^5.4 has no requirement behind it any more. It entered the skill because @icp-sdk/vetkeys 0.5.0 depended on "@icp-sdk/core": "^5.4.0". 0.7.0 peers ^5.0.0 || ^6.0.0, so the floor dropped to 5.0.0, and both core APIs the skill uses (safeGetCanisterEnv, rootKey) are present in core 5.0.0. Raising the snippet to ^5.4 would have propagated a constraint no package asks for. That was also the only exception in the repo, so the earlier hedge in binding-generation.md ("the 5.x line, written ^5 here, ^5.4 where a later minimum is needed") is no longer needed -- every skill now pins ^5 flat. --- skills/icp-cli/references/binding-generation.md | 2 +- skills/vetkeys/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/icp-cli/references/binding-generation.md b/skills/icp-cli/references/binding-generation.md index 27e87251..b1a839a4 100644 --- a/skills/icp-cli/references/binding-generation.md +++ b/skills/icp-cli/references/binding-generation.md @@ -86,7 +86,7 @@ npm install '@icp-sdk/core@^5' npm install -D '@icp-sdk/bindgen@^0.4.0' ``` -**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 anchors core to the **5.x line** (written `^5` here, `^5.4` where a later minimum is needed). +**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.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/vetkeys/SKILL.md b/skills/vetkeys/SKILL.md index b5602113..2400895b 100644 --- a/skills/vetkeys/SKILL.md +++ b/skills/vetkeys/SKILL.md @@ -30,7 +30,7 @@ Build on the maintained libraries — do not hand-roll the cryptography or the C > > 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.4` installed explicitly as its peer (the 5.x line — see the pin note above). +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