Skip to content

Fix: declare @google/genai in the build_setup integration fixture manifests - #329

Open
AmaadMartin wants to merge 1 commit into
mainfrom
fix/build-setup-fixtures-declare-genai
Open

Fix: declare @google/genai in the build_setup integration fixture manifests#329
AmaadMartin wants to merge 1 commit into
mainfrom
fix/build-setup-fixtures-declare-genai

Conversation

@AmaadMartin

@AmaadMartin AmaadMartin commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

  1. Link to an existing issue (if applicable):

No existing issue.

  1. Or, if no issue exists, describe the change:

Problem: All six tests/integration/build_setup fixtures import @google/genai in their agent entry point, but none of them declare it in their own package.json:

Fixture Import site
ts_esm/agent.ts:15 import {createModelContent, GenerateContentResponse} from '@google/genai';
ts_esm_native_addon/agent.ts:14 import {...} from '@google/genai';
ts_commonjs_native_addon/agent.ts:14 import {...} from '@google/genai';
ts_commonjs/agent.ts:17 require('@google/genai')
js_esm/agent.js:13 import {...} from '@google/genai';
js_commonjs/agent.js:13 require('@google/genai')

Each fixture is a self-contained npm project that build_setup_test.ts installs (:29) and builds (:34) for real — it is a miniature user project. Resolution of @google/genai currently succeeds only as a side effect of npm hoisting a transitive copy, or of the fixture directory happening to live under a repo root that has its own node_modules. Neither is something a real user copying ts_esm would get.

Two concrete consequences:

  1. The resolved major is decided by a hoist race, today. core/package.json:48 declares "@google/genai": "^2.9.0", but core also declares "@google-cloud/vertexai": "^1.12.0", and @google-cloud/vertexai@1.12.0 declares "@google/genai": "^1.45.0". Two incompatible ranges compete for the single hoisted slot at the fixture's node_modules root — the exact slot the fixture's own import resolves from. The committed root lockfile shows that collision resolving against core: package-lock.json:1608 puts 1.52.0 at the top level, while core's 2.9.0 is pushed down to core/node_modules/@google/genai (package-lock.json:91). Verified on disk after a root install: root copy 1.52.0, core/node_modules copy 2.9.0.
  2. Silent breakage on an upstream change. If core drops @google/genai or bumps its major, the fixtures break with TS2307 / ERR_MODULE_NOT_FOUND even though nothing in the fixtures changed — and, worse, they can silently keep compiling against a copy resolved from outside the fixture (demonstrated below).

Solution: Declare "@google/genai": "^2.9.0" in each of the six fixture manifests. That is the whole change — six one-line additions to dependencies, 10 insertions total, no executable code touched.

Why this solution:

  • npm always places a project's own direct dependency at the root of that project's node_modules, so the declaration deterministically pins which copy the fixture's import resolves from, instead of leaving it to a hoist race.
  • The range is read from the single source of truth, core/package.json:48 ("@google/genai": "^2.9.0") — not invented, not pinned exactly, not *. It was ^2.9.0 at implementation time, so there is no discrepancy to report.
  • The key is inserted after "@google/adk-devtools" (and before "onnxruntime-node" where present), preserving the alphabetical ordering already used in all six files.

Deliberately not done, to keep the diff minimal:

  • No change to agent.ts / agent.js — the imports are correct as written.
  • No change to core/package.json, dev/package.json, or the root package.json; the root-manifest and dev gaps are separately tracked.
  • No tests/**/package-lock.json committed — .gitignore:8 ignores it by design, and each fixture install is torn down in afterAll (build_setup_test.ts:111-122).
  • No overrides block (separately tracked; there are currently none anywhere in the repo).
  • No change to build_setup_test.ts (see the note on the pre-existing hook timeout below).
  • No change to the root package-lock.json. Running the root npm install locally produced one incidental hunk (a "dev": true flag flip on adm-zip); it was reverted, since this change adds no workspace dependency.

Scope note (6 fixtures, not 4). The originating task claimed the two js_* fixtures do not import @google/genai and need no change. That is false against this checkout — js_commonjs/agent.js:13 and js_esm/agent.js:13 both import it (see the table above). Both have the identical defect and the identical one-line fix, so they are included here rather than left as a knowingly-open defect requiring a second review cycle for two lines.

Collision check. Before implementing, all 232 open PRs on the fork were scanned for file overlap (gh pr list plus a GraphQL query over every open PR's file list). No open PR touches any tests/integration/build_setup/*/package.json. Six PRs touch build_setup_test.ts (#75, #106, #117, #129, #218, #257) — this PR does not touch that file, so there is no overlap and no need to stack. The nearby @google/genai dedupe / manifest PRs (#226, #228, #244, #274) and the fixture-install PRs (#276, #299) were diffed individually and are all confined to the root/workspace manifests or to other fixture trees; none of them declares @google/genai in a build_setup fixture. This PR therefore branches from main.

Testing Plan

Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.

Unit Tests:
[ ] I have added or updated unit tests for my change.
[x] All unit tests pass locally.

No unit test was added, deliberately. This change adds zero lines of executable code — it is six one-line additions to JSON manifests. There is no statement, branch, or function for a unit test to cover, and the coverage config (vitest.config.ts, coverage.include) only instruments core/src/**, dev/src/**, integrations/src/**, so coverage is unaffected in either direction. A test that read these JSON files back and asserted the literal string just written would be a tautology against the diff: it would pass regardless of whether dependency resolution is actually correct. The meaningful verification is the existing integration suite plus the falsification experiment below.

Existing integration suite — targeted run, all six fixtures, no changes to the test file:

npx vitest run --project integration tests/integration/build_setup/build_setup_test.ts

Result: Test Files 1 passed (1) / Tests 20 passed | 4 skipped (24). The 4 skips are the suite's own it.skipIf, which excludes the two *_native_addon fixtures from the test:db / test:devtools cases. This exercises, per fixture: a real npm install, tsc for the four ts_*, npm run start against the mock LLM, npm run test:db / npm run test:devtools, and npx @google/adk-devtools --version.

One caveat on how that run was invoked, stated plainly. With the committed config the suite fails before reaching any assertion:

Error: Hook timed out in 10000ms.
 ❯ tests/integration/build_setup/build_setup_test.ts:28:5

beforeAll (:28) runs a real npm install and passes no explicit timeout, so it inherits vitest's 10s default. This is pre-existing and unrelated to this change — verified by reverting all six manifests to main and re-running the identical command, which fails identically (Hook timed out in 10000ms, 24 skipped). It is already tracked by open PRs #117 and #257. Rather than modify build_setup_test.ts (out of scope, and it would collide with those PRs), the run above was performed through a local, uncommitted vitest config identical to the integration project but with hookTimeout: 900_000. Nothing in the repo was changed to obtain the green run.

Repo-wide checks on the exact pushed commit: npm run build → exit 0; npm run lint (eslint "**/*.ts") → exit 0; npm run format:checkAll matched files use Prettier code style!.

Manual End-to-End (E2E) Tests:

Two experiments prove the change is load-bearing rather than cosmetic. Both were run on ts_esm; reproduce from the repo root after npm install && npm run build.

Proof 1 — the declaration takes effect.

cd tests/integration/build_setup/ts_esm
rm -rf node_modules dist package-lock.json && npm install
npm ls @google/genai

Before (fixture undeclared) — the fixture is not a dependent; the copy at its node_modules root is whatever won the hoist:

ts_esm_build_setup@1.0.0 /…/tests/integration/build_setup/ts_esm
└─┬ @google/adk@1.4.0
  ├─┬ @google-cloud/vertexai@1.12.0
  │ └── @google/genai@1.52.0
  └── @google/genai@2.15.0

After — the fixture itself is a direct dependent, and the root copy is pinned to a 2.x:

ts_esm_build_setup@1.0.0 /…/tests/integration/build_setup/ts_esm
├─┬ @google/adk@1.4.0
│ ├─┬ @google-cloud/vertexai@1.12.0
│ │ └── @google/genai@1.52.0
│ └── @google/genai@2.15.0 deduped
└── @google/genai@2.15.0

Proof 2 — mutation / falsification: the fixture survives core dropping the dependency. Mutation applied: temporarily delete both @google/genai and @google-cloud/vertexai from core/package.json dependencies (these are the only two providers of @google/genai in the fixture tree), then reinstall the fixture. core/package.json was restored afterwards and is not part of this diff.

Sub-case A — with the repo-root node_modules still present. Both variants build, but they resolve to different files. tsc --traceResolution in the unfixed fixture:

Module name '@google/genai' was successfully resolved to
  '/…/adk-js/node_modules/@google/genai/dist/node/node.d.ts'
  with Package ID '@google/genai/dist/node/node.d.ts@1.52.0…'

It escaped the fixture entirely and compiled against 1.52.0 — a major core does not use — from a directory that exists only because the fixture happens to sit inside this repo. The fixed fixture, same conditions:

Module name '@google/genai' was successfully resolved to
  '/…/build_setup/ts_esm/node_modules/@google/genai/dist/node/node.d.ts'
  with Package ID '@google/genai/dist/node/node.d.ts@2.15.0…'

Fixture-local, correct major. This ancestor-node_modules fallback is the finding that most justifies the change, and it also means the plain "does it build" check is not sufficient to detect the defect — hence sub-case B.

Sub-case B — with the repo-root copy also moved aside, i.e. modelling a genuine user project. Unfixed fixture:

agent.ts(15,59): error TS2307: Cannot find module '@google/genai' or its corresponding type declarations.

npm run build exit code 1. Fixed fixture, identical conditions: tsc reports no errors, Build complete, exit code 0, fixture-local @google/genai at 2.15.0.

So the change flips a hard TS2307 build failure into a pass, and in the softer case flips a silent wrong-major resolution into a correct fixture-local one. All fixture node_modules, dist, and package-lock.json were removed afterwards; git status is clean apart from the six intended manifests.

Checklist

[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[ ] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.

Notes on the two unticked boxes:

  • Comments: the diff is six "@google/genai": "^2.9.0" lines in JSON manifests, which admits no comments and needs none; the rationale is in this description instead.
  • Unit tests: no unit test was added, for the reason given under Testing Plan. The "tests that prove my fix is effective" box is ticked on the strength of the existing integration suite plus the Proof 2 falsification experiment, which shows the unfixed fixture failing with TS2307 and the fixed one passing.

No type-checker or linter suppressions were added by this change (@ts-expect-error, @ts-ignore, eslint-disable, as any, as never, coverage-ignore): the diff contains no code. Verified with a grep over git diff main.

CI

All checks pass on this PR: run-tests on ubuntu-latest, macos-latest and windows-latest, plus check-license and auto-assign. Overall run conclusion success.

Disclosure — the Windows leg was flaky and needed re-runs (2 fail, then pass), and that is worth explaining rather than hiding. Both failures were the same pre-existing 10s beforeAll timeout described above (build_setup_test.ts:28, Hook timed out in 10000ms), affecting 1 fixture on the first run and 3 on the second. Evidence that this is a pre-existing flake and not a regression introduced here:

  1. It reproduces on a PR that touches no fixture at all. PR Fix: mirror geminiInitParams in the e2e credential guard so a project-only environment skips instead of failing #316 (an e2e credential-guard change) fails on windows-latest with the byte-identical error at the same line, on ts_esm_native_addon. Sampling recent Windows legs (Fix: require TEST_API_SERVER_PORT in the A2A multi-hop test agent #314, Feat: Port the typed-errors module (errors/) from adk-python #317, Feat: Port the environment abstraction (BaseEnvironment, ExecutionResult, LocalEnvironment) from adk-python #319, Feat: Workflow node model — BaseNode, FunctionNode, JoinNode, NodeContext (Part 1/3) #320, Feat: fail the build on phantom dependencies in the published src trees (import/no-extraneous-dependencies) #323, Fix: raise vitest coverage thresholds to the measured worst-leg floor #324, Fix: point ts:check at a program that type-checks sources, not build output #326) shows the suite passing but taking 124–166s for six fixtures — the per-fixture beforeAll sits close to its 10s budget on Windows, so it tips over at random.
  2. This change provably does not make the install slower or bigger. Installing ts_esm with and without the declaration produces an identical tree: added 606 packages both times, 593 installed package directories, 423M on disk, and exactly 2 copies of @google/genai (2.15.0 at the fixture root, 1.52.0 nested under @google-cloud/vertexai) in both cases. The copy npm previously hoisted by accident is the same copy it now places deliberately, so nothing extra is fetched or written. Wall-clock was 88.4s undeclared vs 71.4s declared — i.e. dominated by network noise, and if anything faster with the fix.
  3. The ubuntu and macOS legs passed on the first attempt.

The proper fix is an explicit timeout on that hook, which is already the subject of open PRs #117 and #257. Duplicating it here would create a competing implementation, so build_setup_test.ts is deliberately left untouched.

…ifests

All six tests/integration/build_setup fixtures import @google/genai in their
agent entry point, but none declared it. Each fixture is a self-contained npm
project that build_setup_test.ts installs and builds for real, so resolution
succeeded only as a side effect of npm hoisting a transitive copy.

That copy is contested: core declares ^2.9.0 while @google-cloud/vertexai@1.12.0
declares ^1.45.0, and nothing pins which one lands at the fixture node_modules
root -- the exact slot the fixture's own import resolves from. Declaring the
dependency makes npm place a 2.x there deterministically, and makes the fixtures
survive core dropping or bumping the dependency.

The range mirrors core/package.json ("@google/genai": "^2.9.0").
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant