Repository navigation
fix(AIC-3573): never send a NativeTool to the model as a function tool - #194
Merged
Merged
Conversation
This was referenced Oct 29, 2026
jeffdupont
marked this pull request as ready for review
October 29, 2026 18:25
The tool wrapper turns every NativeTool into a callable tracking stub, so every handler but Claude Agents sent it to the model as a function tool that returns nothing. Add an isFunctionTool helper that rejects a NativeTool or its stub and warns once per tool key, and use it wherever a handler or native graph builds its tool list. No provider built-in mappings yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…andler keys Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont
force-pushed
the
jdupont/aic-3573-native-tools
branch
from
October 30, 2026 14:39
6192ba0 to
e7dd8c0
Compare
apucacao
approved these changes
Oct 30, 2026
Merged
andrewklatzke
pushed a commit
to launchdarkly/python-ai-sdk
that referenced
this pull request
Oct 30, 2026
#248) Jira: [AIC-3573](https://launchdarkly.atlassian.net/browse/AIC-3573) ## Problem `wrap_tool_handlers` turns every `NativeTool` into a callable tracking stub, so Claude Agents and OpenCode can wire it to their built-in tools. Every other handler sent it to the model as a function tool. The openai, claude and langchain messages handlers and the openai and langchain agents handlers offer every `config.tools` entry. litellm and vercel keep any callable, and the stub is callable. So a configured built-in tool (such as web search) never turned on, and the model saw a made-up function that returns nothing. ## Change - **`is_function_tool(handler, name)`** in `launchdarkly_ai_server` (`utils.py`). It returns `False` for a `NativeTool` or its stub (`NATIVE_TOOL_KEY`) and logs one warning per tool key. Otherwise it returns `callable(handler)`, as a `TypeGuard`. It is exported like `end_span_once` (handler plumbing). - **Every handler and native graph that builds a tool list** now skips native tools with it: 9 handler builders and the openai and langchain agents native graphs. The litellm and vercel graphs reuse their handler's builder. Claude Agents and OpenCode are unchanged. - **Unchanged on purpose:** the openai, claude and langchain messages handlers and the openai and langchain agents handlers still offer a tool that has no handler, and so do the openai and langchain agents native graphs (they return `""` when it is called). TypeScript drops these. That gap predates this PR. This PR removes only native tools. - There are no provider built-in mappings yet. A handler with no built-in skips the tool with a warning and never raises. ## Tests - `tests/test_native_tools.py` in each affected package: the builder drops a `NativeTool` and its stub, keeps the function tool, and warns once per key. - Unit tests for `is_function_tool` with the real `wrap_tool_handlers` stub, plus the public-surface snapshot. - langchain-agents `test_native_tool_is_not_called` becomes `test_native_tool_is_not_registered`. - Control: with the handler sources reverted to `main`, all 12 new builder tests fail. Checks run locally: `ruff check`, `ruff format --check`, `mypy packages/*/src`, `make api-report-check`, `pytest` (all pass except `test_lifecycle.py::TestInitClientSDKKeyPath`'s two tests, which also fail on `main` in a whole-repo run and pass on their own). TypeScript: launchdarkly/js-ai-sdk#194 🤖 Generated with [Claude Code](https://claude.com/claude-code) [AIC-3573]: https://launchdarkly.atlassian.net/browse/AIC-3573?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Fixes a bug where **`NativeTool`** entries (and the callable tracking stubs from **`wrap_tool_handlers`**) were exposed to the model as custom function tools instead of provider built-ins, so configured native tools like web search never activated and the model saw no-op functions. > > Adds shared **`is_function_tool(handler, name)`** on **`launchdarkly_ai_server`**: it rejects **`NativeTool`** / stub handlers, logs **one warning per tool key**, and otherwise treats only real callables as function tools. **Nine handler tool builders** (Claude/OpenAI/LangChain/LiteLLM/Vercel messages and agents) plus **OpenAI and LangChain native graph** node tool builders now filter through it; **`_build_tools`** in messages handlers also takes **`tool_handlers`** so filtering can run at catalog build time. > > **Unchanged:** configs can still advertise tools with no registered handler on several paths (predates this PR). **Tests** cover **`is_function_tool`**, public exports, and per-package builders dropping native tools while keeping ordinary function tools. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 21d6150. 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: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont
added a commit
that referenced
this pull request
Oct 30, 2026
Steps 1 and 3 of [AIC-3673](https://launchdarkly.atlassian.net/browse/AIC-3673). The Python half is launchdarkly/python-ai-sdk#242. ## What this does - **`release-as: 1.0.0`** on all 14 packages in `release-please-config.json`. The `node-workspace` plugin updates the internal dependency ranges. - **Publish jobs for `litellm-agents` and `litellm-messages`.** They had none, so `@launchdarkly/ai-litellm-*-0.2.0` was tagged but never published. Both are also added to the manual-publish choices. `changelog-sections` is left alone: #181 (merged) removed the evaluations Experimental rows when it promoted Evaluations from Code to core. ##⚠️ npm setup: trusted publishers still to confirm for 5 of 14 packages The 26 Oct release run ([40393827352](https://github.com/launchdarkly/js-ai-sdk/actions/runs/40393827352)) failed 3 publish jobs, and the litellm packages have never had a job: | Package | On npm | Failure | |---|---|---| | `@launchdarkly/ai-typesafe` | 0.0.0 placeholder | `E404 … PUT @launchdarkly/ai-typesafe`: the package doesn't exist, and OIDC can't create it | | `@launchdarkly/ai-vercel-agents` | 0.0.0 placeholder | `E403 OIDC permission denied` | | `@launchdarkly/ai-vercel-messages` | 0.0.0 placeholder | `E403 OIDC permission denied` | | `@launchdarkly/ai-litellm-agents` | 0.0.0 placeholder | no publish job (added here) | | `@launchdarkly/ai-litellm-messages` | 0.0.0 placeholder | no publish job (added here) | Before the release PR merges, an npm org admin needs to: 1. ~~Publish a 0.0.0 placeholder for `ai-typesafe`, `ai-litellm-agents` and `ai-litellm-messages`.~~ Done: all three are on npm at 0.0.0 (checked 29 Oct). 2. Add a trusted publisher (`launchdarkly/js-ai-sdk`, workflow `release-please.yml`) to all five. Otherwise 1.0.0 ships 9 of 14 packages. ## Merge this last Merge this only after the items below are done. Anything merged before the release PR (#171) merges ships in 1.0.0, and a `!` change merged after 1.0 forces 2.0. - [x] #181 feat(evaluations)!: promote Evaluations from Code to core. After it, `@launchdarkly/ai-server/experimental` exports nothing. - [x] #180 fix(SEC-9507)!: dispatch tools only from the active variation's config.tools - [x] #183 fix(claude-agents)!: honour max_tokens and disable thinking by default - [x] #111 feat(AIC-3439)!: wildcard handlers accept only named providers (merged 29 Oct) - [x] npm placeholders (above) - [ ] Trusted publishers on all five packages (above; needs an npm org admin to confirm) - [ ] #192 fix(AIC-3592): make native LangGraph hand-offs complete (ready for review; spec launchdarkly/ai-sdks-monorepo#118) - [ ] #194 fix(AIC-3573): never send a NativeTool to the model as a function tool (ready for review; spec launchdarkly/ai-sdks-monorepo#116) - [ ] #190 feat(client)!: capture conversation content by default and add configure() (approved; breaking, so it must land before #171) - [ ] #196 test(AIC-3716): run the OpenAI native graph with a tool on the real SDK (test only; optional for the release) Not in 1.0: #187 (Shadow Requests, closed). 1.0 ships with nothing experimental. ## After merging 1. Confirm the regenerated #171 lists 1.0.0 for all 14 packages and its CI is green, then merge it. 2. Install each package from npm into an empty project and import its entry points, in both ESM and CJS (AIC-3673 step 5). 3. Open a PR removing `release-as` straight away. ## Checks - The workflow YAML parses: 14 per-package publish jobs plus release-please and the canary. The new jobs are copies of `release-typesafe`. - No local build was run: this is config only, made through the GitHub API. 🤖 Generated with [Claude Code](https://claude.com/claude-code) [AIC-3673]: https://launchdarkly.atlassian.net/browse/AIC-3673?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Prepares the monorepo for a coordinated **1.0.0** npm release by setting **`release-as: "1.0.0"`** on all 14 workspaces in `release-please-config.json` (the `node-workspace` plugin will align internal dependency ranges when the release PR lands). > > The **`release-please`** workflow now fully covers **`litellm-agents`** and **`litellm-messages`**: they appear in manual `workflow_dispatch` package choices, expose release-please job outputs, and get dedicated **`release-litellm-*`** publish jobs (build, attest, npm publish) matching the other packages—closing the gap where LiteLLM tags could be created without a publish step. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit af882d4. 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: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: andrewklatzke <aklatzke@launchdarkly.com>
jeffdupont
pushed a commit
that referenced
this pull request
Oct 30, 2026
🤖 I have created a release *beep* *boop* --- <details><summary>@launchdarkly/ai-claude-agents: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-claude-agents-0.5.0...@launchdarkly/ai-claude-agents-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **client:** add result variant and value to the feature_flag span event ([#186](#186)) * **SEC-9507:** enable Claude native tools only when the active variation attaches them * **claude-agents:** honour the config's max_tokens and disable thinking unless the config asks ([#183](#183)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **client:** add result variant and value to the feature_flag span event ([#186](#186)) ([8dbe2ec](8dbe2ec)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) * **skills:** give Claude Agents runs the skills attached to their variation ([#136](#136)) ([df16ba0](df16ba0)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) ([d3e7c5f](d3e7c5f)) ### Bug Fixes * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **claude-agents:** honour the config's max_tokens and disable thinking unless the config asks ([#183](#183)) ([f58ce21](f58ce21)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) * **SEC-9507:** dispatch litellm-messages tool calls only to tools offered for the request ([6926e8a](6926e8a)) * **SEC-9507:** enable Claude native tools only when the active variation attaches them ([6926e8a](6926e8a)) * **SEC-9507:** pass handlers only the tools attached to the active variation ([6926e8a](6926e8a)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-claude-messages: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-claude-messages-0.5.0...@launchdarkly/ai-claude-messages-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-langchain-agents: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-langchain-agents-0.5.0...@launchdarkly/ai-langchain-agents-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **client:** add result variant and value to the feature_flag span event ([#186](#186)) * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) ([9371975](9371975)) * **client:** add result variant and value to the feature_flag span event ([#186](#186)) ([8dbe2ec](8dbe2ec)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3592:** make native LangGraph hand-offs complete ([#192](#192)) ([025f2ce](025f2ce)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-langchain-messages: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-langchain-messages-0.5.0...@launchdarkly/ai-langchain-messages-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **client:** add result variant and value to the feature_flag span event ([#186](#186)) * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) ### Features * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) ([9371975](9371975)) * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) ([15a0d37](15a0d37)) * **client:** add result variant and value to the feature_flag span event ([#186](#186)) ([8dbe2ec](8dbe2ec)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3726:** realign three usage IDs with the 1.0 factory names ([#197](#197)) ([fc83c7a](fc83c7a)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-litellm-agents: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-litellm-agents-0.2.0...@launchdarkly/ai-litellm-agents-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-litellm-messages: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-litellm-messages-0.2.0...@launchdarkly/ai-litellm-messages-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **SEC-9507:** enable Claude native tools only when the active variation attaches them * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) * **SEC-9507:** dispatch litellm-messages tool calls only to tools offered for the request ([6926e8a](6926e8a)) * **SEC-9507:** enable Claude native tools only when the active variation attaches them ([6926e8a](6926e8a)) * **SEC-9507:** pass handlers only the tools attached to the active variation ([6926e8a](6926e8a)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-node: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-node-0.4.0...@launchdarkly/ai-node-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **evaluations:** promote Evaluations from Code to core ([#181](#181)) * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) ### Features * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) ([15a0d37](15a0d37)) * **evaluations:** promote Evaluations from Code to core ([#181](#181)) ([676a2ed](676a2ed)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) ([d3e7c5f](d3e7c5f)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-openai-agents: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-openai-agents-0.5.0...@launchdarkly/ai-openai-agents-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **client:** add result variant and value to the feature_flag span event ([#186](#186)) * **AIC-3687:** rename createOpenAIAgentHandler to createOpenAIAgentsHandler ([#174](#174)) ### Features * **AIC-3687:** rename createOpenAIAgentHandler to createOpenAIAgentsHandler ([#174](#174)) ([cfba4d2](cfba4d2)) * **client:** add result variant and value to the feature_flag span event ([#186](#186)) ([8dbe2ec](8dbe2ec)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3726:** realign three usage IDs with the 1.0 factory names ([#197](#197)) ([fc83c7a](fc83c7a)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-openai-messages: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-openai-messages-0.5.0...@launchdarkly/ai-openai-messages-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) ([15a0d37](15a0d37)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **AIC-3726:** realign three usage IDs with the 1.0 factory names ([#197](#197)) ([fc83c7a](fc83c7a)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-otel: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-otel-0.3.0...@launchdarkly/ai-otel-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** spans carry launchdarkly.project_id instead of highlight.project_id ### Bug Fixes * **client:** stamp launchdarkly.project_id on the telemetry resource ([#176](#176)) ([93fb412](93fb412)) </details> <details><summary>@launchdarkly/ai-server: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-server-0.5.0...@launchdarkly/ai-server-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. * **client:** add result variant and value to the feature_flag span event ([#186](#186)) * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) * **SEC-9507:** enable Claude native tools only when the active variation attaches them * **evaluations:** export each offline row as one trace ([#175](#175)) * **evaluations:** promote Evaluations from Code to core ([#181](#181)) * **evaluations:** remove default pass rate threshold ([#184](#184)) * **client:** spans carry launchdarkly.project_id instead of highlight.project_id * **AIC-3687:** rename createOpenAIAgentHandler to createOpenAIAgentsHandler ([#174](#174)) * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ### Features * **AIC-3439:** let wildcard handlers accept only named providers ([#111](#111)) ([9371975](9371975)) * **AIC-3686:** rename the LangChain and OpenAI messages factories to *MessagesHandler ([#173](#173)) ([15a0d37](15a0d37)) * **AIC-3687:** rename createOpenAIAgentHandler to createOpenAIAgentsHandler ([#174](#174)) ([cfba4d2](cfba4d2)) * **client:** add result variant and value to the feature_flag span event ([#186](#186)) ([8dbe2ec](8dbe2ec)) * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) * **client:** point judge spans at the run they graded ([#200](#200)) ([ba9c936](ba9c936)) * **evaluations:** export each offline row as one trace ([#175](#175)) ([05a7eb1](05a7eb1)) * **evaluations:** promote Evaluations from Code to core ([#181](#181)) ([676a2ed](676a2ed)) * **evaluations:** remove default pass rate threshold ([#184](#184)) ([f3a3e37](f3a3e37)) * **skills:** give Claude Agents runs the skills attached to their variation ([#136](#136)) ([df16ba0](df16ba0)) * **skills:** rename FDv2SkillStore to LaunchDarklySkillStore ([#172](#172)) ([d3e7c5f](d3e7c5f)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **AIC-3598:** type toolHandlers on the handler contract; declare @langchain/anthropic ([#170](#170)) ([c6c00fc](c6c00fc)) * **AIC-3601:** stop multi-edge routing text from asking a router for a final answer ([#177](#177)) ([10cce54](10cce54)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) * **client:** stamp launchdarkly.project_id on the telemetry resource ([#176](#176)) ([93fb412](93fb412)) * **evaluations:** update stale test import ([#189](#189)) ([f4d8083](f4d8083)) * **evaluations:** warn once when a scorer returns an invalid score ([#199](#199)) ([f43c888](f43c888)) * **graph:** report a node's own judge scores on its node_done stream event ([#182](#182)) ([1230f02](1230f02)) * **SEC-9507:** dispatch litellm-messages tool calls only to tools offered for the request ([6926e8a](6926e8a)) * **SEC-9507:** enable Claude native tools only when the active variation attaches them ([6926e8a](6926e8a)) * **SEC-9507:** pass handlers only the tools attached to the active variation ([6926e8a](6926e8a)) </details> <details><summary>@launchdarkly/ai-typesafe: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-typesafe-0.2.0...@launchdarkly/ai-typesafe-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-vercel-agents: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-vercel-agents-0.3.0...@launchdarkly/ai-vercel-agents-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.0 </details> <details><summary>@launchdarkly/ai-vercel-messages: 1.0.0</summary> ## [1.0.0](https://github.com/launchdarkly/js-ai-sdk/compare/@launchdarkly/ai-vercel-messages-0.3.0...@launchdarkly/ai-vercel-messages-1.0.0) (2026-10-30) ### ⚠ BREAKING CHANGES * **client:** conversation content is now captured on spans by default. To keep it off, call configure({ options: { captureContent: false } }) once at startup. * **client:** createHandler returns a new wrapper function rather than adding providesFor to the function you pass and returning that same function. Use the handler createHandler returns; the function you passed in no longer carries providesFor. ### Features * **client:** capture conversation content on spans by default and add configure() ([d9c7fc3](d9c7fc3)) ### Bug Fixes * **AIC-3573:** never send a NativeTool to the model as a function tool ([#194](#194)) ([ec43b85](ec43b85)) * **client:** return a new handler from createHandler instead of changing the function passed in ([d9c7fc3](d9c7fc3)) ### Dependencies * The following workspace dependencies were updated * dependencies * @launchdarkly/ai-server bumped from ^0.5.0 to ^1.0.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** > This **release-please** PR cuts **1.0.0** for every workspace package (`@launchdarkly/ai-server`, `@launchdarkly/ai-node`, provider adapters, `@launchdarkly/ai-otel`, `@launchdarkly/ai-typesafe`) by updating `.release-please-manifest.json`, each `package.json` / `src/version.ts`, and adding **1.0.0** changelog sections. Provider packages bump their `@launchdarkly/ai-server` dependency to **^1.0.0**. > > The release notes bundled here flag **breaking** upgrades integrators must handle: **`configure()`** with **conversation content on spans by default** (opt out via `captureContent: false`); **`createHandler`** now returns a **new wrapper** instead of mutating the passed function; renamed factories (**`*MessagesHandler`**, **`createOpenAIAgentsHandler`**); **`LaunchDarklySkillStore`** (was `FDv2SkillStore`); **Evaluations promoted to core** with offline export/trace and threshold behavior changes; telemetry **`launchdarkly.project_id`** (was `highlight.project_id`); richer **`feature_flag`** span events; and **SEC-9507** tool scoping (native tools and handlers only see tools on the active variation). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f87c9a6. 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>
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.
Jira: AIC-3573
Problem
wrapToolHandlersturns everyNativeToolinto a callable tracking stub, so Claude Agents can wire it to the built-in tool. Every other handler keeps any callable. So openai, claude, langchain, litellm and vercel messages, the openai, langchain, litellm and vercel agents handlers, and their native graphs all sent a configured built-in tool (such as web search) to the model as a function tool:Change
isFunctionTool(name, handler)in@launchdarkly/ai-server(utils.ts). It returnsfalsefor aNativeToolor its stub (NATIVE_TOOL_KEY) and logs one warning per tool key, then for anything that is not a function. It is listed as- isFunctionTool(handler plumbing) inapi-surface/ai-server.txt, likeendSpanOnce.typeof handler === 'function': 9 handlers and 4 native graphs. Claude Agents is unchanged: it already wires native tools.config.toolsentry and return''for a native or missing handler. They now filter onObject.hasOwnplusisFunctionTool. A tool with no handler is no longer offered, like the litellm and vercel graphs. Neither is a name such asconstructorinherited from the object; the litellm and vercel graphs and the agents handlers still lack thathasOwnguard, which predates this PR.web_searchand so on) yet. A handler with no built-in skips the tool with a warning and never throws, so adding a mapping later is non-breaking.Tests
NativeTool. The provider receives only the function tool, for both the stub and the rawNativeTool, and the second call logs no second warning.constructor, is not registered.isFunctionTool, using the realwrapToolHandlersstub.toLangGraphtest that asserted an empty-string tool now asserts that the tool is not registered.main, every new test fails.Checks run locally:
code:check,build,typecheck,api:check,api:surface,test:api-surface,test,lint:pkg.Python: launchdarkly/python-ai-sdk#248
🤖 Generated with Claude Code
Note
Overview
Fixes a bug where
wrapToolHandlersmakesNativeToolhandlers look callable, so most provider handlers were advertising configured built-ins (e.g. web search) as function tools instead of using provider natives—leading to useless tool calls and misleading telemetry.Adds
isFunctionTool(name, handler)in@launchdarkly/ai-server: it returns false for aNativeToolor its tracking stub (NATIVE_TOOL_KEY), logs one console warning per tool key, and otherwise requires a real function. Nine message/agent handlers and four native graphs now filter tool lists through this helper instead oftypeof handler === 'function'(Claude Agents is unchanged).OpenAI Agents and LangChain Agents native graphs no longer register every
config.toolsentry and return''for native/missing handlers; they only register tools with an own handler that passesisFunctionTool, including skipping names likeconstructorinherited from the tools object.Reviewed by Cursor Bugbot for commit e7dd8c0. Bugbot is set up for automated code reviews on this repo. Configure here.