From 77ed9c972594303e16803a528f43d72bf226ce6c Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Mon, 3 Aug 2026 08:15:40 -0700 Subject: [PATCH 1/3] Fix: declare dev's four hoisted runtime dependencies dev/src imports @opentelemetry/api, @opentelemetry/sdk-trace-base, lodash-es and @google-cloud/vertexai, but dev/package.json declares none of them. They resolve in-repo only because core/package.json declares them and npm hoists them to the workspace root, so 'npm install @google/adk-devtools' outside this repo gets none of them. dev/build.js passes packages:'external' to esbuild, so every one of these specifiers survives unbundled into the published dist/esm and dist/cjs and is resolved against the consumer's tree. Each range is copied character for character from core/package.json so npm keeps collapsing both workspaces onto one physical copy. @opentelemetry/api stays an exact pin: two copies of the OTel API in one process each carry their own global tracer registry, and spans recorded against one are invisible to the other. @types/lodash-es is a devDependency because lodash-es ships no type declarations and dev's 'tsc --emitDeclarationOnly' build step needs them, but cloneDeep's types never reach dev's public .d.ts. This mirrors how core pairs the two. --- dev/package.json | 5 +++ dev/test/package_manifest_test.ts | 55 +++++++++++++++++++++++++++++++ package-lock.json | 5 +++ 3 files changed, 65 insertions(+) create mode 100644 dev/test/package_manifest_test.ts diff --git a/dev/package.json b/dev/package.json index 6117df13a..e743206d4 100644 --- a/dev/package.json +++ b/dev/package.json @@ -42,6 +42,7 @@ "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^4.17.21", + "@types/lodash-es": "^4.17.12", "@types/node": "^20.12.7", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", @@ -55,12 +56,15 @@ }, "dependencies": { "@clack/prompts": "^0.11.0", + "@google-cloud/vertexai": "^1.12.0", "@google/adk": "^1.5.0", "@mikro-orm/mariadb": "^6.6.6", "@mikro-orm/mssql": "^6.6.6", "@mikro-orm/mysql": "^6.6.6", "@mikro-orm/postgresql": "^6.6.6", "@mikro-orm/sqlite": "^6.6.6", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", "camelcase-keys": "^6.2.2", "commander": "^14.0.0", "cors": "^2.8.5", @@ -70,6 +74,7 @@ "express": "^4.21.2", "fast-glob": "^3.3.3", "js-yaml": "^4.1.1", + "lodash-es": "^4.18.1", "ts-graphviz": "^1.0.1", "winston": "^3.19.0", "zod": "^4.2.1" diff --git a/dev/test/package_manifest_test.ts b/dev/test/package_manifest_test.ts new file mode 100644 index 000000000..4c09b3f05 --- /dev/null +++ b/dev/test/package_manifest_test.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {readFileSync} from 'node:fs'; +import {fileURLToPath} from 'node:url'; +import {describe, expect, it} from 'vitest'; + +interface Manifest { + dependencies?: Record; + devDependencies?: Record; +} + +function readManifest(relativePath: string): Manifest { + return JSON.parse( + readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), 'utf8'), + ) as Manifest; +} + +const coreManifest = readManifest('../../core/package.json'); +const devManifest = readManifest('../package.json'); + +/** + * Packages dev/src imports at runtime that core also depends on. Both + * workspaces must request the identical range so npm resolves a single copy: + * two copies of @opentelemetry/api in one process each carry their own global + * tracer registry, and spans recorded against one are invisible to the other. + */ +const SHARED_RUNTIME_DEPENDENCIES = [ + '@google-cloud/vertexai', + '@opentelemetry/api', + '@opentelemetry/sdk-trace-base', + 'lodash-es', +]; + +describe('dev package manifest', () => { + it.each(SHARED_RUNTIME_DEPENDENCIES)( + 'declares %s as a runtime dependency matching core', + (name) => { + expect(coreManifest.dependencies?.[name]).toBeDefined(); + expect(devManifest.dependencies?.[name]).toBe( + coreManifest.dependencies?.[name], + ); + }, + ); + + it('declares @types/lodash-es so lodash-es typechecks in a standalone build', () => { + expect(coreManifest.devDependencies?.['@types/lodash-es']).toBeDefined(); + expect(devManifest.devDependencies?.['@types/lodash-es']).toBe( + coreManifest.devDependencies?.['@types/lodash-es'], + ); + }); +}); diff --git a/package-lock.json b/package-lock.json index e29a4a190..3c71f403b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,12 +95,15 @@ "license": "Apache-2.0", "dependencies": { "@clack/prompts": "^0.11.0", + "@google-cloud/vertexai": "^1.12.0", "@google/adk": "^1.5.0", "@mikro-orm/mariadb": "^6.6.6", "@mikro-orm/mssql": "^6.6.6", "@mikro-orm/mysql": "^6.6.6", "@mikro-orm/postgresql": "^6.6.6", "@mikro-orm/sqlite": "^6.6.6", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", "camelcase-keys": "^6.2.2", "commander": "^14.0.0", "cors": "^2.8.5", @@ -110,6 +113,7 @@ "express": "^4.21.2", "fast-glob": "^3.3.3", "js-yaml": "^4.1.1", + "lodash-es": "^4.18.1", "ts-graphviz": "^1.0.1", "winston": "^3.19.0", "zod": "^4.2.1" @@ -120,6 +124,7 @@ "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^4.17.21", + "@types/lodash-es": "^4.17.12", "@types/node": "^20.12.7", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", From b0f1394ea32ca04f9abb5f27c2913bf21222c8cf Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Mon, 3 Aug 2026 08:15:47 -0700 Subject: [PATCH 2/3] Fix: import Client from the @google-cloud/vertexai package root @google-cloud/vertexai@1.12.0 re-exports Client from its root (build/src/index.d.ts), so the deep build-output path reached into implementation detail with no semver guarantee. Both specifiers resolve to the same file in the same package instance, so class identity is unchanged. ReasoningEngine stays on its deep path: it is declared in build/src/genai/types/common.d.ts and re-exported only by build/src/genai/types.d.ts, so the package root does not expose it. Vitest intercepts by specifier, so the deep-path vi.mock in cli_deploy_agent_engine_test.ts would have silently stopped applying and let the suite construct the real Client; the mock is retargeted to match. Its factory body is unchanged. --- dev/src/cli/deploy/cli_deploy_agent_engine.ts | 2 +- dev/test/cli/cli_deploy_agent_engine_test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/src/cli/deploy/cli_deploy_agent_engine.ts b/dev/src/cli/deploy/cli_deploy_agent_engine.ts index 434ed04d1..ae96f9f90 100644 --- a/dev/src/cli/deploy/cli_deploy_agent_engine.ts +++ b/dev/src/cli/deploy/cli_deploy_agent_engine.ts @@ -6,7 +6,7 @@ import * as fs from 'node:fs/promises'; import * as path from 'node:path'; -import {Client} from '@google-cloud/vertexai/build/src/genai/client.js'; +import {Client} from '@google-cloud/vertexai'; import {ReasoningEngine as VertexReasoningEngine} from '@google-cloud/vertexai/build/src/genai/types.js'; import {AgentLoader} from '../../utils/agent_loader.js'; diff --git a/dev/test/cli/cli_deploy_agent_engine_test.ts b/dev/test/cli/cli_deploy_agent_engine_test.ts index f20519350..06b68847e 100644 --- a/dev/test/cli/cli_deploy_agent_engine_test.ts +++ b/dev/test/cli/cli_deploy_agent_engine_test.ts @@ -141,7 +141,7 @@ const mockCreateInternal = vi.fn(); const mockUpdateInternal = vi.fn(); const mockGetAgentOperationInternal = vi.fn(); -vi.mock('@google-cloud/vertexai/build/src/genai/client.js', () => ({ +vi.mock('@google-cloud/vertexai', () => ({ Client: class { agentEnginesInternal = { createInternal: mockCreateInternal, From 37aaee66b46370a4b8d7dedc98b53f9881bfe900 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Mon, 3 Aug 2026 08:37:09 -0700 Subject: [PATCH 3/3] Fix: declare @google/genai in the dev workspace too dev/src imports @google/genai at six sites and it was left undeclared, resolving only by hoisting from core -- the same bug class this branch fixes for the other four packages, so leaving it out shipped a fix that did not actually make dev standalone-installable. It is not type-only: createUserContent is called at dev/src/server/adk_api_client.ts:153 and survives into the published output as import_genai.createUserContent, and AdkApiClient is exported from dev/src/index.ts, so a standalone install hit ERR_MODULE_NOT_FOUND on a public entry point. The range matches core/package.json:48 so npm keeps both workspaces on one copy; npm ls confirms @google/genai@2.9.0 deduped for core and dev. The nested 1.52.0 under @google-cloud/vertexai is that package's own pin and is untouched. Added to SHARED_RUNTIME_DEPENDENCIES so the guard covers it: the list is hand-maintained, so an omission there is invisible. --- dev/package.json | 1 + dev/test/package_manifest_test.ts | 1 + package-lock.json | 1 + 3 files changed, 3 insertions(+) diff --git a/dev/package.json b/dev/package.json index e743206d4..ae7dbba5d 100644 --- a/dev/package.json +++ b/dev/package.json @@ -58,6 +58,7 @@ "@clack/prompts": "^0.11.0", "@google-cloud/vertexai": "^1.12.0", "@google/adk": "^1.5.0", + "@google/genai": "^2.9.0", "@mikro-orm/mariadb": "^6.6.6", "@mikro-orm/mssql": "^6.6.6", "@mikro-orm/mysql": "^6.6.6", diff --git a/dev/test/package_manifest_test.ts b/dev/test/package_manifest_test.ts index 4c09b3f05..5eb49bf02 100644 --- a/dev/test/package_manifest_test.ts +++ b/dev/test/package_manifest_test.ts @@ -30,6 +30,7 @@ const devManifest = readManifest('../package.json'); */ const SHARED_RUNTIME_DEPENDENCIES = [ '@google-cloud/vertexai', + '@google/genai', '@opentelemetry/api', '@opentelemetry/sdk-trace-base', 'lodash-es', diff --git a/package-lock.json b/package-lock.json index 3c71f403b..a4c73ad00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,6 +97,7 @@ "@clack/prompts": "^0.11.0", "@google-cloud/vertexai": "^1.12.0", "@google/adk": "^1.5.0", + "@google/genai": "^2.9.0", "@mikro-orm/mariadb": "^6.6.6", "@mikro-orm/mssql": "^6.6.6", "@mikro-orm/mysql": "^6.6.6",