Skip to content

feat(agents): add beta end call tool - #1474

Merged
toubatbrian merged 36 commits into
1.5.0from
hater-breaking-evoked
Jun 8, 2026
Merged

toubatbrian merged 36 commits into
1.5.0from
hater-breaking-evoked

Conversation

@rosetta-livekit-bot

Copy link
Copy Markdown
Contributor

Summary

  • Add beta EndCallTool ported from Python for ending calls from a tool invocation.
  • Add JobContext.deleteRoom() support and export worker credentials to child job env so room deletion works when options are passed programmatically.
  • Export the new beta tool and add a changeset.

Testing

  • pnpm --filter @livekit/agents build
  • pnpm --filter @livekit/agents lint (passes with existing warnings)
  • pnpm format:check

Notes

  • pnpm --filter @livekit/agents api:check is currently blocked by API Extractor failing on existing export * as ___ syntax in dist/index.d.ts before checking this API surface.

@changeset-bot

changeset-bot Bot commented May 12, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 27383c8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 34 packages
Name Type
@livekit/agents Major
@livekit/agents-plugin-anam Major
@livekit/agents-plugin-assemblyai Major
@livekit/agents-plugin-baseten Major
@livekit/agents-plugin-bey Major
@livekit/agents-plugin-cartesia Major
@livekit/agents-plugin-cerebras Major
@livekit/agents-plugin-deepgram Major
@livekit/agents-plugin-elevenlabs Major
@livekit/agents-plugin-fishaudio Major
@livekit/agents-plugin-google Major
@livekit/agents-plugin-hedra Major
@livekit/agents-plugin-hume Major
@livekit/agents-plugin-inworld Major
@livekit/agents-plugin-lemonslice Major
@livekit/agents-plugin-liveavatar Major
@livekit/agents-plugin-livekit Major
@livekit/agents-plugin-minimax Major
@livekit/agents-plugin-mistral Major
@livekit/agents-plugin-mistralai Major
@livekit/agents-plugin-neuphonic Major
@livekit/agents-plugin-openai Major
@livekit/agents-plugin-perplexity Major
@livekit/agents-plugin-phonic Major
@livekit/agents-plugin-resemble Major
@livekit/agents-plugin-rime Major
@livekit/agents-plugin-runway Major
@livekit/agents-plugin-sarvam Major
@livekit/agents-plugin-silero Major
@livekit/agents-plugin-soniox Major
@livekit/agents-plugin-tavus Major
@livekit/agents-plugins-test Major
@livekit/agents-plugin-trugen Major
@livekit/agents-plugin-xai Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

claude and others added 9 commits May 15, 2026 20:58
… Toolset

`Agent({ tools })` now accepts a list of `FunctionTool | ProviderDefinedTool
| Toolset` entries instead of a `Record<string, FunctionTool>` map, matching
the shape of the Python `ToolContext`. `llm.tool({ ... })` now requires an
explicit `name`. `ToolContext` is exported as a class with the same surface
as the Python one (`functionTools`, `providerTools`, `toolsets`, `flatten()`,
`getFunctionTool()`, `updateTools()`, `copy()`, `equals()`), backed by a
flat `_tools` list plus denormalized name map / provider list / toolset list.
A `Toolset` base class is included as the unit of stateful tool composition
with `setup()` / `aclose()` lifecycle hooks.

This is the foundational refactor; the standalone Toolset PR will rebase on
top of this once it lands.
- Removed `Toolset` from this PR — it lives in the stacked Toolset PR
  on top instead. `ToolContext` only handles `FunctionTool` and
  `ProviderDefinedTool` here.
- `updateTools()` now uses later-wins semantics for duplicate function
  names instead of throwing.
- Fixed every remaining call site that iterated `ToolContext` like a
  record (`Object.keys/entries(toolCtx)`): inference LLM, OpenAI chat /
  WS / responses, MistralAI, Baseten, and AgentActivity's initial-tools
  snapshot all now go through `.functionTools`.
…rray UX

- `updateTools()`: dropped redundant name-empty check (the `tool()` factory
  already enforces a non-empty name at construction).
- `ToolContext.hasTool(name)`: new helper that matches function tools by
  `name` and provider tools by `id`, so chat-context filtering remains
  correct when provider tool call items are added later.
- `ChatContext.copy({ toolCtx })`: switched the filter predicate from
  `getFunctionTool(name)` to `hasTool(name)` for that future-proofing.
- `AgentActivity` initial-tools snapshot: include both function tool names
  and provider tool ids in the `AgentConfigUpdate.toolsAdded` payload.
- Inference LLM tool builder: iterates `toolCtx.flatten()` and discriminates
  on tool type instead of only `functionTools`; provider tools are skipped
  until AJS-112 wires them up properly.
- `ToolCtxInput` + `toToolContext()` helpers: LLM `chat({ toolCtx })` and
  `LLMStream` now accept either a `ToolContext` or a raw
  `(FunctionTool | ProviderDefinedTool)[]` array. Callers can write
  `toolCtx: [tool1, tool2]` directly; `run_result.ts` updated to use it.
…n amd typing

- Removed `toolListFromRecord` — `tool()` requires `name` so the legacy
  `Record<string, FunctionTool>` shape never gets produced in practice.
- `Agent.toolCtx` now returns `this._toolCtx.copy()` so external callers
  can't mutate the agent's internal state.
- `amd.ts` imports `ToolContextEntry` properly at the top instead of
  using the inline `import('../llm/index.js').ToolContextEntry` syntax.
@rosetta-livekit-bot
rosetta-livekit-bot Bot force-pushed the hater-breaking-evoked branch from 43f6089 to 5ee8112 Compare May 17, 2026 23:40
@CLAassistant

CLAassistant commented May 17, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
2 out of 3 committers have signed the CLA.

✅ u9g
✅ toubatbrian
❌ claude
You have signed the CLA already but the status is still pending? Let us recheck it.

devin-ai-integration[bot]

This comment was marked as resolved.

@toubatbrian
toubatbrian changed the base branch from main to brian/toolset May 17, 2026 23:51
toubatbrian and others added 12 commits May 17, 2026 18:07
Brings the public tool surface in line with `livekit-agents` Python:
- Tool gains an `id: string` field on the base interface. `FunctionTool.id`
  mirrors `name`; for `ProviderTool` it's the provider tool id. `ToolContext`
  keys and equality use `tool.id` consistently
  (matches Python's `_fnc_tools_map[tool.info.name]`).
- `ProviderDefinedTool` → `ProviderTool`, converted from an interface to
  an `abstract class`. Plugins now subclass it
  (`class WebSearch extends ProviderTool { ... }`) to attach
  provider-specific fields and serializers, mirroring Python's
  `OpenAITool(ProviderTool, ABC)` / `WebSearch(OpenAITool)` chain.
  The unused `config: Record<string, unknown>` field is dropped, and the
  `tool({ id })` factory overload is removed — bare instantiation of the
  abstract base is now a compile error.
- `ToolType` literal `'provider-defined'` → `'provider'`.
- `Toolset` now carries a `TOOLSET_SYMBOL` marker; added `isToolset()`
  guard alongside `isFunctionTool` / `isProviderTool`. Replaced the one
  remaining `instanceof Toolset` check in `agent_activity` for
  cross-realm safety (consistent with how the other tool kinds are
  detected).
- Plugin TODO comments in google / mistralai / openai / phonic updated
  from "provider-defined tools" → "provider tools".
Tests, types, and changeset updated. All 312 LLM tests pass and the 31
publishable packages build clean.
Co-authored-by: u9g <jason.lernerman@livekit.io>
Co-authored-by: rosetta-livekit-bot[bot] <282703043+rosetta-livekit-bot[bot]@users.noreply.github.com>
@rosetta-livekit-bot
rosetta-livekit-bot Bot force-pushed the hater-breaking-evoked branch from afe2a7c to 982ff6c Compare May 26, 2026 21:32
@rosetta-livekit-bot
rosetta-livekit-bot Bot changed the base branch from brian/toolset to brian/toolsets-create May 26, 2026 21:46

@toubatbrian toubatbrian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments. Agree with the overall direction, but since this would create behavior that differs from Python, I’d recommend having @theomonnom take another pass.

Comment thread agents/src/llm/tool_context.ts Outdated
Move per-turn re-resolution into a DynamicToolset subclass so the runtime
can detect it via instanceof and skip building a fresh ToolContext each turn
when no dynamic toolset is active.
@u9g
u9g force-pushed the hater-breaking-evoked branch 2 times, most recently from 853187b to 45c4de8 Compare May 28, 2026 20:01
devin-ai-integration[bot]

This comment was marked as resolved.

@u9g
u9g force-pushed the hater-breaking-evoked branch 2 times, most recently from dbc6f12 to 436cf01 Compare May 28, 2026 20:19
…dateTools

Drops the DynamicToolset class and resolveEachTurn option. A toolset's tools
factory runs once at activation, after a new optional setup hook for one-time
init (connecting, wiring listeners). Toolsets whose tools change at runtime push
the new list via ToolsetContext.updateTools; the runtime re-flattens and
re-advertises without re-running the factory or polling each turn.
@u9g
u9g force-pushed the hater-breaking-evoked branch from 436cf01 to fff6713 Compare May 28, 2026 20:23
devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread agents/src/beta/tools/end_call.ts Outdated
u9g added 6 commits May 29, 2026 18:51
Use the per-invocation abortSignal from ToolOptions instead of the
toolset teardownSignal so close listeners and timers clean up promptly
when the tool call is cancelled.
Toolset tools are now always a static list. Dynamic toolsets can still
push tools after activation via ToolsetContext.updateTools from a setup
listener. Inline end_call's tool implementation directly into its
execute callback now that no factory-level closure is needed.
The end_call tool no longer needs a tools factory — its session reference
is reachable via ctx.session, so build it directly in the tools array
and move the implementation into the execute callback.
Match the naming convention for factory functions returning a Toolset.
Drop session/signal from ToolsetContext — toolsets no longer get lexical
access to the session or a teardown signal. Drop the ToolsetFactoryFn
form of Toolset.create: anything a factory did can be expressed as a
setup hook that calls ctx.updateTools, so tools is now always a static
list.
devin-ai-integration[bot]

This comment was marked as resolved.

Base automatically changed from brian/toolsets-create to brian/toolset June 1, 2026 17:20
Base automatically changed from brian/toolset to 1.5.0 June 1, 2026 17:23
devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	agents/src/inference/llm.ts
#	agents/src/llm/index.ts
#	agents/src/llm/tool_context.test.ts
#	agents/src/llm/tool_context.ts
#	agents/src/voice/agent.test.ts
#	agents/src/voice/agent.ts
#	agents/src/voice/agent_activity.ts
#	agents/src/voice/agent_v2.ts
#	agents/src/voice/generation.ts
#	agents/src/voice/generation_tools.test.ts
#	agents/src/voice/remote_session.ts
#	plugins/google/src/index.ts
#	plugins/google/src/llm.ts
#	plugins/google/src/realtime/realtime_api.ts
#	plugins/google/src/utils.ts
#	plugins/mistralai/src/llm.ts
#	plugins/openai/src/responses/llm.ts
#	plugins/openai/src/ws/llm.ts
@toubatbrian
toubatbrian force-pushed the hater-breaking-evoked branch from cf03aca to 27383c8 Compare June 1, 2026 19:13
devin-ai-integration[bot]

This comment was marked as resolved.

- agent_activity: route onToolsetToolsChanged through updateTools so a
  dynamic toolset push refreshes history + chat context (not just the
  realtime session), and add a regression test proving the push records
  an AgentConfigUpdate
- end_call: guard the fire-and-forget delayedSessionShutdown with .catch()
  to avoid unhandled rejections
- aiplatform_llm: accept ToolCtxInput in chat() and normalize via
  toToolContext(), matching the other LLM plugins
- tool_context: return a defensive copy from the providerTools getter
- inference/llm: drop a duplicated provider-tool comment
@toubatbrian
toubatbrian merged commit c8b56bd into 1.5.0 Jun 8, 2026
1 of 2 checks passed
@toubatbrian
toubatbrian deleted the hater-breaking-evoked branch June 8, 2026 21:59
tinalenguyen added a commit that referenced this pull request Jun 23, 2026
Squashed local branch work, reconciled with the latest remote state:

- Refactor ToolContext to parity class taking a list of Tool | Toolset (#1517)
- feat(agents): add Toolset support to ToolContext and AgentActivity (#1525)
- feat(agents): add beta end call tool (#1474)
- fix(agents): thread FlushSentinel through Agent.create llmNode types
- fix(elevenlabs): end server vad turns (#1745)
- Add Inworld delivery mode inference TTS option (#1749)
- Don't retain recorded events when recording is disabled (#1750)
- fix(deepgram): validate updated model with stored language on updateOptions
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.

4 participants