Repository navigation
feat(AIC-3416): say the real reason when a bundler breaks an optional import [3/4] - #78
Merged
tonytrinh3 merged 5 commits intoOct 8, 2026
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Contributor
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
Contributor
|
@cursor review |
This was referenced Sep 22, 2026
tonytrinh3
commented
Sep 23, 2026
tonytrinh3
added a commit
that referenced
this pull request
Sep 23, 2026
…ckages (1/4) (#76) ## Summary This PR fixes nothing. It turns the customer's bug into a test that fails on purpose, and writes down the workaround customers can use until the real fix (PR2) ships. Closes [AIC-3414](https://launchdarkly.atlassian.net/browse/AIC-3414). Parent: [AIC-3370](https://launchdarkly.atlassian.net/browse/AIC-3370). PR 1 of 4 — no module-format change yet. ### The bug, in plain terms Our `@launchdarkly/ai-*` packages ship ESM-only: `"exports"` has an `import` entry and no `require` entry. A customer Lambda that emits CommonJS and bundles with Webpack + `webpack-node-externals` leaves our packages external, and Webpack rewrites `await import('@launchdarkly/ai-node')` into `require('@launchdarkly/ai-node')`. Node finds no `require` entry and throws: ```text Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /var/task/node_modules/@launchdarkly/ai-node/package.json ``` That happens before any of our code runs, so no AI config is evaluated and nothing reaches AI Insights. An app with a hardcoded fallback prompt keeps returning plausible answers — which is why this went unnoticed. ### Why a test, when we already have a ticket Our monorepo physically cannot reproduce this: internally the packages are symlinked, so `require` resolves fine. The bug only exists in the *published* package shape. So the test builds what npm would ship and consumes it like a customer would — `yarn build` → `npm pack` → install the tarballs into an empty project → Webpack-bundle a CommonJS Lambda handler → run it. It bundles that handler twice, differing only by one line of Webpack config (`nodeExternals({ allowlist: [/^@launchdarkly\/ai-/] })`), and asserts both outcomes: ```text left external → must fail with ERR_PACKAGE_PATH_NOT_EXPORTED / No "exports" main defined allowlisted → must load config, initClient, openaiMessages, NodeTracerProvider ``` The failing case is a real assertion, not a skip or `it.fails`. PR2 flips it from "must fail" to "must pass" — that flip is the proof the fix works on real published artifacts, and the reason this PR lands first. The handler only reports which symbols resolved: no SDK key, no LaunchDarkly call, no model spend. ### Which AIC-3370 asks this PR answers It answers asks 2, 3, and 4: - **Ask 2 — an official Lambda recipe naming the exact allowlist.** `packages/ai-node/README.md` gives the Serverless + Webpack config with `allowlist: [/^@launchdarkly\/ai-/]`, and CI asserts that recipe works. - **Ask 3 — a smoke test that packages and invokes a real CommonJS Lambda, because a source-level import test cannot catch this.** The harness installs `npm pack` tarballs into a separate project, so Node reads the published `"exports"` map. PR4 (#79) runs the same shape against the published npm versions in a real Lambda after release. - **Ask 4 — a documented support matrix.** The root README covers CommonJS source, CommonJS bundle output, native ESM, and Node 22, and marks which rows CI proves. It names Webpack only; esbuild and Rollup rows are not covered yet. Asks 1 (a `require` condition) and 5 (an initialization diagnostic) are PR2 (#77) and PR3 (#78). ### Also in this PR - `yarn test:integration` plus a dedicated Node 22 CI job matching the reported Lambda runtime; existing unit-test jobs untouched. - Docs customers can use today: a Lambda + Serverless + Webpack recipe in `packages/ai-node/README.md`, and a module-format support matrix in the root README saying what works, what doesn't, and which rows CI proves. - `biome.json` excludes `**/.staging` and `**/dist-*` so generated Webpack output isn't linted. ### Testing `yarn test:integration` on Node 22.21.1 and Node 24 (2 passed), plus `yarn workspace @launchdarkly/ai-node test`, `yarn code:check`, `yarn lint:pkg`. The harness shells out to `yarn build` and `npm pack`, so it's much slower than the unit suites — hence its own script and CI job. Start review at `integration-tests/commonjs-webpack/README.md`, then `harness.ts`, then the two assertions in `commonjs-webpack.test.ts`. No UI or staging preview applies. [AIC-3414]: https://launchdarkly.atlassian.net/browse/AIC-3414?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-3370]: https://launchdarkly.atlassian.net/browse/AIC-3370?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Link to Devin session: https://app.devin.ai/sessions/2798b5e37086465cb5a3af7c906a217b Open in Devin Desktop: https://app.devin.ai/desktop/session/2798b5e37086465cb5a3af7c906a217b?variant=devin Requested by: @tonytrinh3
tonytrinh3
marked this pull request as ready for review
September 23, 2026 15:35
…3/AIC-3416-esm-externalization-diagnostics
tonytrinh3
added a commit
that referenced
this pull request
Sep 23, 2026
…CommonJS apps can load it (2/4) (#77) ## Summary **Plain version:** The other LaunchDarkly JavaScript SDKs already ship in two module formats. This AI SDK did not. It shipped ESM only, so old CommonJS projects crashed when they loaded it. This PR gives the AI SDK the same two-format build, so those projects work. **What it means for the code (junior eng):** The other JavaScript SDKs live in `launchdarkly/js-core`. They build with `tsup` and publish both an `import` condition and a `require` condition. The AI SDK was the outlier with no `require` condition. Now every `@launchdarkly/ai-*` package builds with `tsup` and emits `dist/index.js` (ESM) and `dist/index.cjs` (CommonJS). So `require('@launchdarkly/ai-node')` works. A Webpack bundle that keeps the packages external also works. Node no longer throws `ERR_PACKAGE_PATH_NOT_EXPORTED`. Closes [AIC-3415](https://launchdarkly.atlassian.net/browse/AIC-3415). This fixes the failure in [AIC-3370](https://launchdarkly.atlassian.net/browse/AIC-3370). ### Which AIC-3370 asks this PR answers **Plain version:** This PR answers ask 1 in full. It also adds a few rows to the support table in the root README, which is part of ask 4. The other asks belong to other PRs. **What it means for the code (junior eng):** Ask 1 wants a CommonJS-compatible conditional export. The evidence sits in this diff. - Nine packages now build both formats: `client`, `ai-node`, `ai-otel`, `claude-agents`, `claude-messages`, `openai-agents`, `openai-messages`, `langchain-agents`, and `langchain-messages`. - Each `exports` map gains a `require` branch: ```json ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } ``` - In `integration-tests/commonjs-webpack/commonjs-webpack.test.ts`, the `externalized` case flips from an expected failure to an expected success. The suite also adds a `require()` consumer and an `import` consumer that run from packed tarballs on Node 22 and Node 24. Ask 4 wants a support matrix. PR #76 owns that matrix. This PR only updates its rows in the root README, because five load paths now work instead of three. It adds no new bundler and no new row. Asks 2 and 4 belong to PR #76. Ask 3 belongs to PR #76 plus PR #79. Ask 5 belongs to PR #78. ### Changes - This PR adopts the build that js-core already uses. 11 js-core packages build with `tsup`, and 23 of its 32 packages publish a `require` condition. The AI SDK published none, so it was the outlier. - Nine packages move from `tsc` to `tsup-node`. They share one config at `tsup.config.base.ts`. - Each `exports` map gets a `require` condition. It points to `dist/index.cjs` and `dist/index.d.cts`. The fields `main`, `module`, and `types` stay aligned for old tools. - The `externalized` test case from PR1 now expects success. The suite also adds `require()` and `import` consumers, tarball checks, and manifest checks. - The CJS output keeps optional dependencies behind a runtime `import()`. So users still choose to install OpenTelemetry, LangChain providers, and `@launchdarkly/node-server-sdk`. - The integration job in CI now runs on Node 22 and Node 24. <details> <summary>Implementation details</summary> ### The build Each package holds one line of config: ```ts export { default } from '../../tsup.config.base'; ``` The shared config is small: ```ts // tsup.config.base.ts export default defineConfig({ entry: { index: 'src/index.ts' }, format: ['esm', 'cjs'], splitting: false, sourcemap: true, clean: true, dts: true, }); ``` The scripts `prepare`, `build`, and `build:watch` call `tsup-node`. ### Consistency with js-core This PR adds no new pattern. It copies the current js-core build. - The config in `packages/sdk/server-ai/tsup.config.ts` matches the config here field for field: the same entry, the same two formats, `splitting: false`, sourcemaps, clean output, and declarations. - The manifest shape matches too. The field `main` points to the `.cjs` file. The field `module` points to the `.js` file. The `exports` map holds a `require` branch and an `import` branch, and each branch names its own declaration file. - Two cosmetic differences remain. js-core lists `require` before `import`. Our legacy fields omit the leading `./`. Node matches conditions by name and not by order, so neither difference changes behavior. ### Bundler coverage CI proves one bundler: Webpack 5 with `webpack-node-externals`. That tool produced the incident, so it comes first. A `require` condition is the standard mechanism, so esbuild, Rollup, and Vite should now work too. I did not test them, so the README does not claim them. ### Optional dependencies esbuild keeps `await import('...')` of an external package as a real dynamic import in the CJS file. A static `require` would force users to install every optional package. The test suite reads each `dist/index.cjs`. It asserts the file contains `import("@launchdarkly/node-server-sdk")`. It asserts the file does not contain `require("@launchdarkly/node-server-sdk")`. ### TypeScript project layout The declaration build reads files outside the old glob `src/*.ts`. That broke the `composite` project build with error TS6307. Each package `tsconfig.json` drops `composite`. A new file `tsconfig.ref.json` holds `composite: true` and `include: ["src/**/*"]`. The root `tsconfig.json` points at those files. The root `build` and `clean` scripts now call workspace scripts. The build runs `@launchdarkly/ai-server` first, because the handler packages read types from its `dist` folder. ### Alternatives that I rejected A second `tsc` pass plus a `dist/package.json` shim adds more moving parts. It also produces no `.d.cts` file. A custom esbuild wrapper repeats what `tsup` already does. js-core already uses `tsup`. ### Scope I changed no source imports and no runtime logic. The package `@launchdarkly/ai-node` stays a pure barrel. I updated the READMEs, because CommonJS is now supported. ### Testing These commands pass: `yarn build`, `yarn typecheck`, `yarn code:check`, `yarn lint:pkg`, and `yarn test`. I ran `npm pack --dry-run` for all nine packages. Each tarball holds `dist/index.js`, `dist/index.cjs`, `dist/index.d.ts`, and `dist/index.d.cts`. The command `yarn test:integration` passes 21 of 21 tests on Node 22.23.2 and on Node 24.16.0. That run covers the `externalized` case, the `allowlisted` case, and the `require` and `import` consumers. </details> Link to Devin session: https://app.devin.ai/sessions/c730a69bf9534f648dd5cb5f1db6a28b Open in Devin Desktop: https://app.devin.ai/desktop/session/c730a69bf9534f648dd5cb5f1db6a28b?variant=devin Requested by: @tonytrinh3 [AIC-3415]: https://launchdarkly.atlassian.net/browse/AIC-3415?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-3370]: https://launchdarkly.atlassian.net/browse/AIC-3370?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **Adds CommonJS publishing across the AI SDK** so `require()`, Webpack externalized bundles, and plain CJS/ESM consumers resolve published packages instead of hitting `ERR_PACKAGE_PATH_NOT_EXPORTED` (AIC-3370). > > All nine publishable workspaces switch from **`tsc` to shared `tsup`** (`tsup.config.base.ts`), emitting **`dist/index.js` + `dist/index.cjs`** and matching **`index.d.ts` / `index.d.cts`**. Each `package.json` gains a **`require` branch** in `exports` and sets **`main`** to the CJS file. Root **`build`/`clean`** run workspace scripts ( **`@launchdarkly/ai-server` first** ); TypeScript **`composite`** moves to **`tsconfig.ref.json`** per package. > > **Integration tests and CI** flip the Webpack **externalized** case to expect success, add **`entrypoints.cjs` / `entrypoints.mjs`**, tarball/manifest checks, and assertions that optional peers stay on **runtime `import()`** in CJS. The CommonJS/Webpack job runs on **Node 22 and 24**. READMEs document dual-format support and updated load-path matrix. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9bb4956. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
Base automatically changed from
tonytrinh3/AIC-3415-dual-esm-cjs-output
to
main
September 23, 2026 16:10
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f90b97a. Configure here.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
andrewklatzke
approved these changes
Sep 30, 2026
tonytrinh3
deleted the
tonytrinh3/AIC-3416-esm-externalization-diagnostics
branch
October 8, 2026 19:28
Merged
XieX
added a commit
that referenced
this pull request
Oct 9, 2026
Brings in #115 (helper usage), #123 (Experimental changelog heading), #78 (bundler import diagnostics) and #119 (node-workspace ranges). lifecycle.ts conflict: keep the branch's init/shutdown structure and add main's usage flush beside each flushAiSdkInfo call (already-initialized, BYOC, and after a successful options-path await), so a failed or abandoned init never flushes and a retry flushes once. shutdown() resets usage with sdk-info, only when a client existed, as main's early return did; held helpers survive a no-client shutdown. shutdownTelemetry() keeps the branch's OTel-globals teardown and gains main's ESM warning reset. Root surface: add main's configInternal, createHandlerInternal, graphInternal, inspectConfigInternal and reportUsage to ROOT_VALUES and regenerate etc/ai-server.api.md. The experimental report is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
andrewklatzke
pushed a commit
that referenced
this pull request
Oct 9, 2026
🤖 I have created a release *beep* *boop* --- <details><summary>@launchdarkly/ai-claude-agents: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-claude-agents-0.3.0...@launchdarkly/ai-claude-agents-0.4.0) (2026-10-09) ### Features * **AIC-3211:** record each graph node as its own tracking event ([#91](#91)) ([2df2923](2df2923)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) ### Bug Fixes * **claude-agents:** forward only an allowlist of query() options from model.parameters ([37bd8a7](37bd8a7)) * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-claude-messages: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-claude-messages-0.3.0...@launchdarkly/ai-claude-messages-0.4.0) (2026-10-09) ### Features * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) * **claude-messages:** forward only Messages API keys from model.parameters ([9a1819e](9a1819e)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * **openai-messages:** only exclude model.parameters keys that would break the handler ([39e342d](39e342d)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **SEC-9507:** authorize message-handler tools from the active variation ([#90](#90)) ([c16fe93](c16fe93)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-langchain-agents: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-langchain-agents-0.3.0...@launchdarkly/ai-langchain-agents-0.4.0) (2026-10-09) ### Features * **AIC-3211:** record each graph node as its own tracking event ([#91](#91)) ([2df2923](2df2923)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** forward only a per-model-class allowlist of constructor fields ([0990a51](0990a51)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-langchain-messages: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-langchain-messages-0.3.0...@launchdarkly/ai-langchain-messages-0.4.0) (2026-10-09) ### Features * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** forward only a per-model-class allowlist of constructor fields ([0990a51](0990a51)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **SEC-9507:** authorize message-handler tools from the active variation ([#90](#90)) ([c16fe93](c16fe93)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-node: 0.3.0</summary> ## [0.3.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-node-0.2.0...@launchdarkly/ai-node-0.3.0) (2026-10-09) ### Experimental * **skills:** add experimental Agent Skills under `@launchdarkly/ai-server/experimental`, re-exported from `@launchdarkly/ai-node/experimental` ([a242659](a242659)) ### Features * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) * **build:** dual ESM/CommonJS output via tsup ([d8e62e4](d8e62e4)) * **client:** `initClient(client, options)` accepts telemetry options for a pre-initialized client ([a242659](a242659)) * **client:** warn about unrecognized `initClient` options instead of ignoring them silently ([a242659](a242659)) ### Bug Fixes * **client:** `shutdownTelemetry()` releases the OpenTelemetry globals the SDK registered, so a later `initClient` exports spans again ([a242659](a242659)) * **client:** a repeat `initClient(client)` returns the client already set instead of replacing it ([a242659](a242659)) * **client:** an `initClient` that fails, or that `shutdown()` interrupts, closes the client it built, and a failure is no longer cached, so the next call retries ([a242659](a242659)) * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-openai-agents: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-openai-agents-0.3.0...@launchdarkly/ai-openai-agents-0.4.0) (2026-10-09) ### Features * **AIC-3210:** support streaming responses from agent graph nodes ([#84](#84)) ([24671e8](24671e8)) * **AIC-3211:** record each graph node as its own tracking event ([#91](#91)) ([2df2923](2df2923)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * **openai-agents:** forward only an allowlist of ModelSettings fields from model.parameters ([398e935](398e935)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **telemetry:** report OpenAI tool arguments as an object, not a JSON string ([#68](#68)) ([d614791](d614791)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-openai-messages: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-openai-messages-0.3.0...@launchdarkly/ai-openai-messages-0.4.0) (2026-10-09) ### Features * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) * **openai-messages:** forward only Responses API keys from model.parameters ([5c51dc9](5c51dc9)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * **openai-messages:** only exclude model.parameters keys that would break the handler ([39e342d](39e342d)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **SEC-9507:** authorize message-handler tools from the active variation ([#90](#90)) ([c16fe93](c16fe93)) * **telemetry:** report OpenAI tool arguments as an object, not a JSON string ([#68](#68)) ([d614791](d614791)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-otel: 0.2.0</summary> ## [0.2.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-otel-0.1.1...@launchdarkly/ai-otel-0.2.0) (2026-10-09) ### Features * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) * **build:** dual ESM/CommonJS output via tsup ([d8e62e4](d8e62e4)) </details> <details><summary>@launchdarkly/ai-server: 0.4.0</summary> ## [0.4.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-server-0.3.0...@launchdarkly/ai-server-0.4.0) (2026-10-09) ### Experimental * **skills:** add experimental Agent Skills under `@launchdarkly/ai-server/experimental`, re-exported from `@launchdarkly/ai-node/experimental` ([a242659](a242659)) ### Features * **AIC-3210:** support streaming responses from agent graph nodes ([#84](#84)) ([24671e8](24671e8)) * **AIC-3211:** record each graph node as its own tracking event ([#91](#91)) ([2df2923](2df2923)) * **AIC-3416:** say the real reason when a bundler breaks an optional import [3/4] ([#78](#78)) ([7ee4d5a](7ee4d5a)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) * **build:** build the AI SDK like the other LaunchDarkly JS SDKs so CommonJS apps can load it (2/4) ([#77](#77)) ([2cda755](2cda755)) * **client:** `initClient(client, options)` accepts telemetry options for a pre-initialized client ([a242659](a242659)) * **client:** restore pickForwardedModelParameters for closed provider request types ([f7f85d0](f7f85d0)) * **client:** warn about unrecognized `initClient` options instead of ignoring them silently ([a242659](a242659)) ### Bug Fixes * **client:** `shutdownTelemetry()` releases the OpenTelemetry globals the SDK registered, so a later `initClient` exports spans again ([a242659](a242659)) * **client:** a repeat `initClient(client)` returns the client already set instead of replacing it ([a242659](a242659)) * **client:** an `initClient` that fails, or that `shutdown()` interrupts, closes the client it built, and a failure is no longer cached, so the next call retries ([a242659](a242659)) * **handlers:** camelize snake_case model.parameters for framework handlers ([75c6287](75c6287)) * **judges:** a judge's own config must not fail the run it grades ([#17](#17)) ([6e85cac](6e85cac)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **vercel-messages:** map model.parameters onto AI SDK call settings ([d2a52ae](d2a52ae)) * **vercel:** tag vercelEvaluate spans with run identity ([#100](#100)) ([d32bede](d32bede)) </details> <details><summary>@launchdarkly/ai-vercel-agents: 0.2.0</summary> ## [0.2.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-vercel-agents-0.1.0...@launchdarkly/ai-vercel-agents-0.2.0) (2026-10-09) ### Features * **AIC-3408:** add Vercel AI SDK messages and agents adapters ([#83](#83)) ([7d08c9c](7d08c9c)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **vercel-agents:** map model.parameters onto ToolLoopAgent call settings ([4241d75](4241d75)) * **vercel:** never forward headers, providerOptions, maxRetries or timeout from model.parameters ([0a616fb](0a616fb)) * **vercel:** send image content as AI SDK file parts ([#99](#99)) ([2856abc](2856abc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> <details><summary>@launchdarkly/ai-vercel-messages: 0.2.0</summary> ## [0.2.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-vercel-messages-0.1.0...@launchdarkly/ai-vercel-messages-0.2.0) (2026-10-09) ### Features * **AIC-3408:** add Vercel AI SDK messages and agents adapters ([#83](#83)) ([7d08c9c](7d08c9c)) * **AIC-3495:** record which public helpers an application calls ([#115](#115)) ([aeb4019](aeb4019)) ### Bug Fixes * **deps:** depend on the @launchdarkly/ai-server release each package needs ([#119](#119)) ([9c2a56b](9c2a56b)) * **langchain:** stop forwarding additionalModelRequestFields to ChatBedrockConverse ([f6268fd](f6268fd)) * pass AI Config model parameters through to every provider handler ([#73](#73)) ([993225f](993225f)) * **vercel-messages:** map model.parameters onto AI SDK call settings ([d2a52ae](d2a52ae)) * **vercel:** never forward headers, providerOptions, maxRetries or timeout from model.parameters ([0a616fb](0a616fb)) * **vercel:** send image content as AI SDK file parts ([#99](#99)) ([2856abc](2856abc)) * **vercel:** tag vercelEvaluate spans with run identity ([#100](#100)) ([d32bede](d32bede)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.3.0 to ^0.4.0 </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **Release Please monorepo cut** that bumps published versions in `.release-please-manifest.json`, each package’s `package.json` / `src/version.ts`, and adds matching **CHANGELOG** sections (including first changelogs for the Vercel packages). Provider packages now depend on **`@launchdarkly/ai-server` ^0.4.0**. > > The documented release for **`@launchdarkly/ai-server` 0.4.0** (and aligned provider bumps) includes **dual ESM + CommonJS builds**, **agent graph node streaming**, **per-node tracking events** and **public-helper usage telemetry**, **`initClient` / `shutdownTelemetry` lifecycle fixes**, and **experimental Agent Skills** under `@launchdarkly/ai-server/experimental`. Cross-cutting fixes in the notes cover **AI Config `model.parameters` forwarding** (allowlists, snake_case → camelCase), **message-handler tool authorization (SEC-9507)**, and **Vercel adapter** parameter mapping and multimodal content handling. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4acc72c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
tonytrinh3
added a commit
that referenced
this pull request
Oct 21, 2026
…79) ## Summary **Plain version:** We publish packages to npm. A customer installs them and runs them on AWS Lambda. This change adds a test that does the same thing right after each release. The test installs the published packages, runs them in a real AWS Lambda, and then deletes that Lambda. If the test fails, the packages are broken for customers, and we know in minutes. **What it means for the code (junior eng):** A canary is a small test against production artifacts. This canary closes [AIC-3370](https://launchdarkly.atlassian.net/browse/AIC-3370). The suite from PR1 packs the workspace with `npm pack`, so it tests the tarball that we plan to ship. This canary tests the artifact that npm actually serves, in the runtime that customers use. Closes [AIC-3417](https://launchdarkly.atlassian.net/browse/AIC-3417). [AIC-3416](https://launchdarkly.atlassian.net/browse/AIC-3416) is merged. The base branch is `main` now. I merged `main` into this branch and resolved the conflicts. - **The AWS account and the OIDC role are not confirmed.** I found no approved AWS account for this repository. I found no GitHub OIDC role for this repository. The `id-token` permission here serves npm trusted publishing only. The job reads `vars.AWS_LAMBDA_CANARY_ROLE_ARN`. GitHub skips the job while that variable is empty. I added no credentials. The README lists the trust policy and the permissions. - `integration-tests/lambda-commonjs/` holds the fixture. It uses Serverless 3, Webpack 5, and `webpack-node-externals` 3. The output is `commonjs2`. The runtime is `nodejs22.x`. The fixture sets no externals allowlist, so Node resolves the LaunchDarkly packages at runtime. That path is the AIC-3370 failure path. - `canary.mjs` has three commands. `wait` polls npm. `build` installs the exact versions and packages the app. `assert` checks the payload from the Lambda. The assert step requires the exact type of each export. - `release-please.yml` gets a `release-canary` job. A new `canary_versions` dispatch input runs the canary on demand. The cleanup step runs under `if: always()`. The step fails the job when it cannot delete the stack or the log group. - The canary loads packages only. It uses no SDK key. It evaluates no flag. It calls no model. - The canary pins `@launchdarkly/ai-server` too. `@launchdarkly/ai-node` depends on the core package with `*`, so npm would otherwise pick any visible core version. A core-only release now also runs the canary. ## Which AIC-3370 asks this PR answers **Plain version:** This PR answers ask 3. It runs the packages that npm serves inside a real Lambda after each release. It does not answer the other four asks. Other PRs in the stack answer those. **What it means for the code (junior eng):** This PR completes ask 3. It installs exact published npm versions. It packages a CommonJS Lambda with Webpack 5 and `webpack-node-externals` 3. It deploys that Lambda on Node 22. It invokes the Lambda after each release. Ask 3 has two halves. #76 covers the packed-tarball half. This PR covers the published-artifact half. The real AWS path is gated on `vars.AWS_LAMBDA_CANARY_ROLE_ARN`. No approved AWS account exists yet. No GitHub OIDC role exists yet. So the deploy path, the invoke path, and the cleanup path are unverified against real AWS. This PR does not answer asks 1, 2, 4, or 5. #77 answers ask 1. #76 answers asks 2 and 4. #78 answers ask 5. <details> <summary>Implementation details</summary> ### Why this test exists The suite in `integration-tests/commonjs-webpack` packs the workspace with `npm pack`. That suite cannot catch a publish-time regression. Examples are a wrong `files` list, a stale `dist` folder, or a bad exports map in the published tag. That suite also does not run on Lambda. The difference is real today. I invoked the generated bundle against the published `0.2.0` artifacts. ``` $ node -e "require('./handler.js').handler()..." ERR_PACKAGE_PATH_NOT_EXPORTED No "exports" main defined in node_modules/@launchdarkly/ai-node/package.json ``` That error is the AIC-3370 bug. The canary passes after we publish the dual output from PR2. ### The driver `canary.mjs` holds all of the logic. It takes one exact version for each package. You pass `--ai-node=0.3.0` and the other flags. You can also set `LD_AI_CANARY_VERSIONS='ai-node=0.3.0,openai-messages=0.3.0,ai-otel=0.2.0'`. | Command | Behavior | | --- | --- | | `wait` | Calls `npm view <pkg>@<version> version` up to 30 times, 10 seconds apart. A new publish is not readable at once. | | `build` | Copies `fixture/` into `.canary/app`. Runs `npm install --save-exact`. Checks the tree with `npm ls`. Writes `versions.json`. Runs `serverless package` when you pass `--package`. | | `assert` | Reads the invoke payload and checks it. | The driver checks exactness two times. It first matches the input against `/^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/`. It then compares the installed tree from `npm ls --depth=0 --json` against the request. The build writes `versions.json` into the app, and the handler returns that file. So the assert step checks the versions that the Lambda loaded. It does not trust the versions that CI asked for. The handler returns this shape. The assert step compares each capability against `EXPECTED_CAPABILITIES`. ```json { "ok": true, "runtime": "v22.x", "versions": { }, "capabilities": { "config": "function", "initClient": "function", "shutdown": "function", "openaiMessages": "function", "aiOtel": "object" } } ``` ### The workflow job ```yaml release-canary: needs: [release-please, release-client, release-ai-node, release-ai-otel, release-openai-messages] if: always() && no failed or cancelled need && vars.AWS_LAMBDA_CANARY_ROLE_ARN != '' && (dispatch needs canary_versions, push needs a released canary package) permissions: { contents: read, id-token: write } env: { CANARY_STAGE: c${{ github.run_id }}-${{ github.run_attempt }} } ``` A push reads the versions from `.release-please-manifest.json`. A manual run reads them from the `canary_versions` input. Each run and attempt gets its own stage, so the function name never collides. The existing publish jobs do not change. `manual-publish` now also requires an empty `canary_versions`. So a canary-only manual run publishes nothing. The cleanup step fails loudly. It ignores only a log group that AWS already removed. ```bash serverless remove --stage "$CANARY_STAGE" || status=1 aws logs delete-log-group --log-group-name "$log_group" # ResourceNotFoundException is the only pass exit "$status" ``` ### Decisions - The config schema of Serverless 3 does not know `nodejs22.x`. The package step prints a warning. `configValidationMode: warn` keeps it a warning. CloudFormation accepts the runtime. The canary reads `process.version` and fails on any runtime that is not Node 22. - `aws-actions/configure-aws-credentials` is pinned to a SHA for v4.3.1. This matches the pinning style of this repository. - `.canary/` is now in `.gitignore` and in the Biome ignore list. Serverless writes generated CloudFormation JSON there, and `code:check` fails on those files. ### Testing - `yarn code:fix`, `yarn code:check`, `yarn lint:pkg`, `yarn build`, `yarn typecheck`, and `yarn test` all pass. - `canary.test.ts` adds 8 tests to `yarn test:integration`. The tests use no network and no AWS account. They cover the happy path, a version that is not exact, a version mismatch, a wrong runtime, an export that did not resolve, an export with the wrong type, a missing export, and an error payload from Lambda. - The local dry run packaged the app against the published artifacts. The archive holds the three external LaunchDarkly packages. ``` node canary.mjs build --ai-node=0.2.0 --openai-messages=0.2.0 --ai-otel=0.1.1 --package --stage=local ``` - I did not verify the deploy path, the invoke path, or the cleanup path against AWS. There is no role to assume yet. ### Reviewer guide 1. Read `integration-tests/lambda-commonjs/README.md` for the AWS trust policy, the permissions, and the owner. 2. Read the `release-canary` job in `.github/workflows/release-please.yml`. Check the `if` condition, the OIDC step, and the cleanup step. 3. Read `canary.mjs` for the version checks and the assert contract. 4. Read `fixture/`. Confirm that you want no externals allowlist in this test. </details> [AIC-3370]: https://launchdarkly.atlassian.net/browse/AIC-3370?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-3417]: https://launchdarkly.atlassian.net/browse/AIC-3417?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [AIC-3416]: https://launchdarkly.atlassian.net/browse/AIC-3416?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Link to Devin session: https://app.devin.ai/sessions/bc2f6d69a1b54d9a8ee7b3f1cdee2f8c Open in Devin Desktop: https://app.devin.ai/desktop/session/bc2f6d69a1b54d9a8ee7b3f1cdee2f8c?variant=devin Requested by: @tonytrinh3 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Adds a **published-artifact release canary** that runs after relevant npm publishes (or on demand) to catch CommonJS Lambda load failures like AIC-3370. > > The new `integration-tests/lambda-commonjs` harness uses `canary.mjs` to poll npm for exact semver versions, stage a Serverless 3 + Webpack 5 + `webpack-node-externals` app with **no externals allowlist**, and validate an invoke payload (Node 22 runtime, export types, installed versions). `canary.test.ts` covers assert/version guards offline; `yarn test:integration` now includes it. Generated `.canary/` output is gitignored and excluded from Biome. > > **`release-please.yml`** gains a `release-canary` job (GitHub OIDC → ephemeral `ld-ai-canary-*` deploy/invoke/cleanup) gated on `AWS_LAMBDA_CANARY_ROLE_ARN` and `AWS_LAMBDA_CANARY_PERMISSIONS_BOUNDARY`, plus optional `canary_versions` on `workflow_dispatch` for canary-only runs. Manual publish is skipped when `canary_versions` is set. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 11acd8d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Read this first: this PR only changes error text. It fixes no load failure. It adds no feature. It does not fix the original AIC-3370 crash, because that crash happened before any SDK code ran. PR #77 fixed that part.
|
|
|
||
||
||
||
||
||
||
||
|
Plain version: The SDK loads a few optional packages while it starts. A bundler can break that load. The old error message said the package is missing. That message was wrong, because the package is installed. The import still fails. The message now states the real cause and the fix.
What it means for the code (junior eng):
packages/client/src/lifecycle.tsloads optional packages withawait import(). A bundler can turn that call intorequire().require()cannot load an ESM-only package. ESM means ECMAScript Modules, theimport/exportmodule format. Node then throwsERR_REQUIRE_ESMor a similar error. Both catch blocks used to report "not installed" for every error. Now a classifier reads the error first.Closes AIC-3416. This PR is part of AIC-3370. This PR is stacked on #77 (AIC-3415).
packages/client/src/import-diagnostics.tssorts a failed import into three groups. The groups are ESM interoperability, a missing package, and an unknown error.ESM package was externalized into CommonJS output. It gives the fixallowlist: [/^@launchdarkly\/ai-/]. It links the Lambda recipe inpackages/ai-node/README.md.cause.shutdownTelemetry()resets that state. The node SDK path still throws.Which AIC-3370 asks this PR answers
Plain version: This PR answers ask 5 only. Ask 5 wants an error message that names the real cause. The message names the package. It says the bundler externalized an ESM package. It gives the fix. It links the Lambda recipe. It keeps the original error, so no detail is lost.
What it means for the code (junior eng):
esmExternalizationDiagnosticinpackages/client/src/import-diagnostics.tsbuilds the text. The text containsESM package was externalized into CommonJS output. It containsallowlist: [/^@launchdarkly\/ai-/]. It links the recipe inpackages/ai-node/README.md.describeImportFailuresetscauseto the original error.packages/client/src/__tests__/import-diagnostics.test.tscovers the codes, the message forms, a realMODULE_NOT_FOUND, an unknown error, the preservedcause, and the warn-once behavior.The limit matters. The diagnostic covers the optional dynamic imports in
packages/client/src/lifecycle.ts. Those imports run after #77 fixes root package resolution. The diagnostic cannot catch the original root import failure. That failure happens in the Node resolver, before our code runs.This PR answers no other ask. Ask 1 is #77. Ask 2 is #76. Ask 4 is #76. Ask 3 is #76 plus #79.
Implementation details
The old behavior
Both optional imports discarded the error:
Both messages are wrong when the package is present. The bundler is the real cause.
The new behavior
describeImportFailure(moduleName, error, missingDependencyMessage)returns one of three values. It returns the ESM message withcause. It returns the install message withcause. It returns the original error object.How the classifier decides
The classifier reads the error
codefirst. It treatsERR_REQUIRE_ESMandERR_PACKAGE_PATH_NOT_EXPORTEDas ESM interoperability errors. It treatsMODULE_NOT_FOUNDandERR_MODULE_NOT_FOUNDas a missing package.The classifier then reads the message text. A bundled loader does not always keep the
codeproperty. The message patterns arerequire() of ES Module,Must use import to load ES Module,No "exports" main defined,is not defined by "exports", andCannot find module/package.moduleNameFromErrorreads the package name from the quoted specifier. It also reads the name from anode_modules/<pkg>path. The caller passes a fallback name, so the message never shows an empty name.Scope limit
This PR does not catch the original failure from before PR2. That failure happened in the package resolver, before any SDK code ran. PR #77 removed that failure.
One behavior change to review
The node SDK path used to report "not installed" for every failure. An unrelated failure now propagates unchanged. An example is a
TypeErrorfrom inside the package itself.Alternatives that I rejected
I did not export the classifier from the public barrel. The diagnostic is internal.
I did not keep the logic inside
lifecycle.ts. A separate file is easier to test. Vitest replaces a throwing import mock with its own factory error. That made the lifecycle tests flaky.Testing
packages/client/src/__tests__/import-diagnostics.test.tscovers each error code and each message form. It checks that a realMODULE_NOT_FOUNDkeeps the install message. It checks that an unknown error passes through by identity. It checks a non-error throw. It checkscause. It checks module name extraction from a specifier, from a path, and from the fallback. It checks the message content. It checks the warn-once behavior and the reset.I ran these commands from the repo root, and all passed:
yarn build,yarn typecheck,yarn code:check,yarn lint:pkg,yarn test(395 client tests, three runs to check for flakes), andyarn test:integration(21 tests from the PR1 harness).This PR changes no UI. There is no staging preview.
Reviewer guide
packages/client/src/import-diagnostics.tsfirst. It holds the classifier and the message text.packages/client/src/lifecycle.ts. The telemetry path still warns and continues. The node SDK path still throws.packages/client/src/__tests__/import-diagnostics.test.ts.Link to Devin session: https://app.devin.ai/sessions/d378cea89d3841e19600978ae3c8f2ad
Open in Devin Desktop: https://app.devin.ai/desktop/session/d378cea89d3841e19600978ae3c8f2ad?variant=devin
Requested by: @tonytrinh3
Note
Overview
Improves error messaging only when optional
await import()calls in client initialization fail because a bundler externalized ESM packages into CommonJS (misleading “not installed” text before).Adds
import-diagnosticsto classify failures: ESM interop (ERR_REQUIRE_ESM, message patterns), genuine missing modules, or pass-through for unrelated errors. ESM cases get a diagnostic naming the package, anallowlist: [/^@launchdarkly\/ai-/]hint, and a link to the Lambda/Webpack recipe; wrapped errors keep the original incause.lifecycle.tsnow uses this in the OpenTelemetry setup catch (one-timeconsole.warnviawarnEsmExternalizationOnce, reset onshutdownTelemetry) and in the@launchdarkly/node-server-sdkimport catch (describeImportFailurestill throws, but with the right message). TrueMODULE_NOT_FOUNDstill shows install guidance; other failures are no longer mislabeled as missing deps.Vitest coverage in
import-diagnostics.test.tsexercises classification, module name extraction, diagnostics, and warn-once behavior.Reviewed by Cursor Bugbot for commit dc16a78. Bugbot is set up for automated code reviews on this repo. Configure here.