Skip to content

fix(AIC-3592): make native LangGraph hand-offs complete - #192

Merged
jeffdupont merged 5 commits into
mainfrom
jdupont/aic-3592-langgraph-handoff
Oct 30, 2026
Merged

jeffdupont merged 5 commits into
mainfrom
jdupont/aic-3592-langgraph-handoff

Conversation

@jeffdupont

@jeffdupont jeffdupont commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes AIC-3592 (1.0 blocker) in TypeScript. toLangGraph failed every turn that handed off: the Whodunit app's check --engine langgraph failed 30 of 30 graph turns with "Node whodunit-manor-butler is not reachable". Implements the new §2.x.3 bullets in launchdarkly/ai-sdks-monorepo#118.

In packages/langchain-agents/src/native-graph.ts:

  • The hand-off Command answers its tool call. It now adds a ToolMessage (Handed off to <target>.) with the call's id, so the target's model call never sees an unanswered tool_use.
  • The tools node declares its hand-off targets as ends, so LangGraph accepts the Command target. This is the "not reachable" error.
  • The routing node does not run again after a hand-off. The static tools -> node edge is replaced by a conditional edge that goes back to the node only when no hand-off routed. This applies to single-child and multi-child nodes.
  • Only the first hand-off in a message routes, as graph() does. A later one returns Ignored: only the first transfer in a turn routes. and does not track $ld:ai:graph:handoff_success.
  • parallel_tool_calls is no longer passed to bindTools. Bedrock Converse rejects it; the first-hand-off rule replaces it.
  • A hand-off target sees provider-neutral text history. Messages already in the state when a node is entered are rewritten for its model: reasoning and other non-text blocks dropped, each assistant message's tool calls appended as [Tool call] <name> <args> lines, the tool results after it folded into one user message of [Tool result] <name>: <text> lines in call order. Human and system messages pass unchanged. The node's own tool loop after entry stays native.

A normal tool call in the same message as a hand-off still runs, in the same tools step, and the target receives its result (decision 1A on the ticket).

Tests

native-graph.shallow.test.ts runs six new cases on the real StateGraph with a mock model: a multi-child hand-off, a single-child hand-off, a normal call plus a hand-off, two hand-offs, no parallel_tool_calls, and a node's own tool loop staying native. Each hand-off case asserts the exact messages the target receives (no tool role, tool calls, or reasoning blocks). A toNeutralHistory unit test covers every rewrite rule on the same input as the Python test, with the same expected output. The helper is exported from native-graph.ts for that test only; the package index does not re-export it (api:check passes). The unused _text and the duplicate final-output extraction now use contentText. The mocked executor test now asserts the ToolMessage in the Command update.

Local: yarn code:check, yarn typecheck, yarn api:check, yarn test all pass. The live Bedrock run is under "Live check".

Python: launchdarkly/python-ai-sdk#246. Go is out of scope for 1.0.

Live check

Passed on 29 Oct at head 0a0dd22: Whodunit check --engine langgraph on Bedrock completed 15 of 15 graph lines with 0 errors (the run at b528263 completed 9 of 16, with the toolConfig and reasoning_content errors). Reachability 5/5, verdict format 6/6. The later commit ae7a03e changes only the test file.

Bedrock end-to-end in CI: deferred to Q4 follow-up.

🤖 Generated with Claude Code


Note

Overview
Fixes broken LangGraph hand-offs in toLangGraph that caused unreachable target nodes and failed multi-agent runs (e.g. Whodunit on Bedrock).

Hand-off tools now return a Command that includes a ToolMessage answering the transfer call, register ToolNode ends for valid goto targets, and use conditional edges so the routing node does not run again after a successful hand-off. Only the first transfer_to_* in a turn routes; later ones return an ignored message. parallel_tool_calls is no longer passed to bindTools (Bedrock compatibility); first-wins routing replaces it.

When a node is entered via hand-off, prior conversation is passed through new toNeutralHistory (reasoning/non-text stripped, tool calls/results as plain [Tool call] / [Tool result] lines); the node’s own tool loop after entry stays native.

Adds real StateGraph integration tests for hand-off scenarios and a toNeutralHistory unit test aligned with Python; updates the mocked hand-off executor test for the Command message update.

Reviewed by Cursor Bugbot for commit ae7a03e. Bugbot is set up for automated code reviews on this repo. Configure here.

toLangGraph failed every turn that handed off. The hand-off Command now
carries a ToolMessage for its call, the tools node declares its hand-off
targets as ends, a node no longer runs again after it hands off (single-
and multi-child), only the first hand-off in a message routes, and
parallel_tool_calls is no longer passed to bindTools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont and others added 4 commits October 29, 2026 10:58
…t key

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hand-off target can be on another provider. Live Whodunit run on 29 Oct: a Bedrock target with no tools rejected toolUse/toolResult blocks, and an OpenAI target rejected Claude reasoning_content. History from before a node was entered is now plain text: reasoning dropped, tool calls and results as lines. The node's own tool loop stays native.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the output

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…MessageChunk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jeffdupont
jeffdupont marked this pull request as ready for review October 29, 2026 19:25
@jeffdupont
jeffdupont merged commit 025f2ce into main Oct 30, 2026
15 checks passed
@jeffdupont
jeffdupont deleted the jdupont/aic-3592-langgraph-handoff branch October 30, 2026 14:44
jeffdupont added a commit to launchdarkly/python-ai-sdk that referenced this pull request Oct 30, 2026
## Summary

Fixes [AIC-3592](https://launchdarkly.atlassian.net/browse/AIC-3592)
(1.0 blocker) in Python. `to_lang_graph` failed every turn that handed
off: the Whodunit app's `check --engine langgraph` failed 30 of 30 graph
turns with `ChatBedrockConverse._converse_params() got an unexpected
keyword argument 'parallelToolCalls'`. Implements the new §2.x.3 bullets
in launchdarkly/ai-sdks-monorepo#118.

In
`packages/langchain-agents/src/launchdarkly_ai_langchain_agents/native_graph.py`:

- **`parallel_tool_calls` is no longer passed to `bind_tools`.** This is
the Bedrock error. The first-hand-off rule below replaces it.
- **The hand-off `Command` answers its tool call.** It now adds a
`ToolMessage` (`Handed off to <target>.`) with the call's id. The id
comes from `InjectedToolCallId`, so the hand-off schema is now a small
pydantic model (a dict schema skips the injection). `pydantic>=2.13` is
declared, as in the other packages that import it.
- **The routing node does not run again after a hand-off.** The static
`tools -> node` edge is replaced by a conditional edge that goes back to
the node only when no hand-off routed. This applies to single-child and
multi-child nodes.
- **Only the first hand-off in a message routes**, as `graph()` does. A
later one returns `Ignored: only the first transfer in a turn routes.`
and does not track `$ld:ai:graph:handoff_success`.
- **A hand-off target sees provider-neutral text history.** Messages
already in the state when a node is entered are rewritten for its model:
reasoning and other non-text blocks dropped, each assistant message's
tool calls appended as `[Tool call] <name> <args>` lines, the tool
results after it folded into one user message of `[Tool result] <name>:
<text>` lines in call order. Human and system messages pass unchanged.
The node's own tool loop after entry stays native.

A normal tool call in the same message as a hand-off still runs, in the
same tools step, and the target receives its result (decision 1A on the
ticket).

## Tests

`TestNativeHandoffRealStateGraph` runs six cases on the real
`StateGraph` with a mock model: a multi-child hand-off, a single-child
hand-off, a normal call plus a hand-off, two hand-offs, no
`parallel_tool_calls`, and a node's own tool loop staying native. Each
hand-off case asserts the exact messages the target receives (no tool
role, tool calls, or reasoning blocks). `TestToNeutralHistory`
unit-tests the rewrite on one input covering every rule (reasoning, null
text, an empty assistant, an unmatched result, call order, nested and
non-ASCII args, a message chunk, an image in a human message); the
TypeScript test uses the same input and expects the same output.

Local: `make lint`, `make format-check`, `make typecheck`, `make test`
(3718 passed, 18 skipped) all pass. The live Bedrock run is under "Live
check".

TypeScript: launchdarkly/js-ai-sdk#192. Go is out of scope for 1.0.

## Live check

Passed on 29 Oct: js-ai-sdk#192 at `0a0dd22`, Whodunit `check --engine
langgraph` on Bedrock, 15 of 15 graph lines, 0 errors.

Bedrock end-to-end in CI: deferred to Q4 follow-up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


[AIC-3592]:
https://launchdarkly.atlassian.net/browse/AIC-3592?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> Fixes broken **LangGraph hand-offs** in `to_lang_graph` (AIC-3592),
including the Bedrock failure from passing `parallel_tool_calls` to
`bind_tools`. Hand-off routing now matches `graph()`: only the **first**
`transfer_to_*` call in a turn navigates; extras return an ignored
message.
> 
> Hand-off **`Command`** updates now include a **`ToolMessage`** that
answers the transfer tool call (via **`InjectedToolCallId`** on a small
Pydantic `_HandoffArgs` schema). **`pydantic>=2.13`** is added as a
direct dependency. After tools run, a **conditional edge** stops the
routing node from executing again when a hand-off fired, instead of
always looping back through `_tools → node`.
> 
> When a node is **entered** (not continuing its own tool loop), prior
conversation is rewritten with **`_to_neutral_history`**:
reasoning/non-text blocks dropped, tool calls and results flattened to
plain `[Tool call]` / `[Tool result]` text so downstream agents on other
providers never see native tool or reasoning payloads.
> 
> **Tests** add `TestNativeHandoffRealStateGraph` (six StateGraph
scenarios) and `TestToNeutralHistory` aligned with the TypeScript
rewrite fixture.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cee3051. 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>
@cursor cursor Bot mentioned this pull request Oct 30, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants