# LiveKit Agents for Node.js 1.5.0 is coming (Feedback Wanted)

**URL:** <https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466>\
**Category:** Announcements\
**Tags:** agent-development, node-js\
**Created:** [June 20, 2026, 2:31am UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466 "2026-06-20T02:31:33Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brian\_Yin](https://avatars.discourse-cdn.com/v4/letter/b/7ba0ec/32.png) [@Brian\_Yin](https://community.livekit.io/u/Brian_Yin)\
**Post date:** [June 20, 2026, 2:31am UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/1 "2026-06-20T02:31:34Z")

</div>

Update: 1.5.0 pre-release is up ([0.0.0-next-20260624041820](https://www.npmjs.com/package/@livekit/agents/v/0.0.0-next-20260624041820))

* * *

Hey everyone — we’re getting close to releasing **LiveKit Agents for Node.js 1.5.0** , a minor version focused on making the Node.js API more ergonomic. The headline is a new functional way to build agents — **`Agent.create()` / `AgentTask.create()`**, so you can create a custom agent without extending a class — plus a batch of new capabilities (toolsets, async/background tools, provider tools, `LLMStream.collect()`, and more). It also brings a few things in line with the Python SDK along the way, but it’s primarily a JS-side upgrade.

Because this is a version bump **with a few breaking changes around tools and `ToolContext`** , we want to **gather feedback before the full release** and run a **pre-release** that folks can try first.

Please skim the changes below and let us know in the thread, specifically, **do you read or mutate `agent.toolCtx` directly** (index it, `Object.keys`, spread it)? See the ToolContext section — that’s the one true breaking change.

* * *

## 1. Tools are now a flat array (object form still works)

`tool()` now takes a `name`, and `tools` is a flat array of `FunctionTool | ProviderTool | Toolset`.

```typescript
// 1.5+
import { Agent, tool } from '@livekit/agents';

const agent = Agent.create({
  instructions: 'You are a helpful assistant.',
  tools: [
    tool({
      name: 'getWeather',
      description: 'Look up weather for a location.',
      execute: async ({ location }) => getWeather(location),
    }),
  ],
});

```

**Backward compatible:** the legacy object form still works for function tools — the object key supplies the tool name:

```typescript
tools: {
  getWeather: tool({ description: '...', execute: async ({ location }) => getWeather(location) }),
}

```

Use the array form when you want to include **toolsets** or **provider tools** (the object form can’t express those).

## 2. `ToolContext` is now a class (the one breaking change)

Previously, `ToolContext` was effectively a `Record<string, FunctionTool>` — a plain name→tool mapping. The `toolCtx` / `tools` getters on `Agent`, `AgentSession`, and `AgentActivity` returned that map, so you could index it, `Object.keys()` it, or spread it.

With toolsets, a flat name → tool mapping no longer captures everything (a toolset is a unit with its own lifecycle), so **`ToolContext` is now a dedicated class** (this also lines up with Python):

```typescript
agent.toolCtx.tools; // readonly (FunctionTool | ProviderTool | Toolset)[]
agent.toolCtx.functionTools; // Record<string, FunctionTool> (the legacy-style map)
agent.toolCtx.providerTools; // ProviderTool[]
agent.toolCtx.toolsets; // readonly Toolset[]
agent.toolCtx.getFunctionTool('getWeather');
agent.toolCtx.hasTool('getWeather');
agent.toolCtx.flatten(); // Flatten the tool context to a list of tools

```

**Migration:**

```typescript
// Before // 1.5+
agent.toolCtx['getWeather'] → agent.toolCtx.getFunctionTool('getWeather')
Object.keys(agent.toolCtx) → Object.keys(agent.toolCtx.functionTools)
await agent.updateTools({ ...agent.toolCtx, newTool })
                                         → await agent.updateTools([...agent.toolCtx.tools, newTool])

```

Good news: `llm.chat({ toolCtx })`, `new Agent({ tools })`, and `agent.updateTools(...)` all still accept a plain name→tool object **or** an array, so most call sites don’t change. The break is specifically if you **consume** `agent.toolCtx` as a plain object.

## 3. New: build agents without subclassing — `Agent.create()` / `AgentTask.create()`

You no longer have to `extends Agent` to build a custom agent. Pass your instructions, tools, and hooks (`onEnter`, `onExit`, `onUserTurnCompleted`, `onUserTurnExceeded`, and the pipeline nodes `sttNode`/`llmNode`/`ttsNode`/`realtimeAudioOutputNode`) directly to `Agent.create()` — hooks receive a `ctx` object instead of `this`. The same applies to tasks via `AgentTask.create()`.

```typescript
import { Agent, AgentTask, tool } from '@livekit/agents';

// A custom agent — no class needed
const agent = Agent.create({
  instructions: 'You are a helpful assistant.',
  tools: [
    tool({
      name: 'getWeather',
      description: '...',
      execute: async ({ location }) => getWeather(location),
    }),
  ],
  onEnter(ctx) {
    ctx.session.generateReply({ instructions: 'Greet the user.' });
  },
});

// A custom task — also no class needed
const collectName = AgentTask.create<string>({
  instructions: 'Ask the user for their name.',
  onEnter(ctx) {
    ctx.session.generateReply({ instructions: 'Ask the user for their name.' });
  },
});

```

For now, all pipeline node hooks — such as the **STT node** (`sttNode`) and **TTS node** (`ttsNode`) — accept the **async iterable / async generator** pattern. You can iterate the input directly and `yield` values, instead of constructing a `ReadableStream`:

```typescript
import { Agent } from '@livekit/agents';

const agent = Agent.create({
  instructions: 'You are a helpful assistant.',
  // ttsNode written as an async generator
  async *ttsNode(ctx, text, modelSettings) {
    const frames = await Agent.default.ttsNode(ctx.agent, text, modelSettings);

    if (!frames) return;

    for await (const frame of frames) {
      yield postProcess(frame); // post-process audio frames here
    }
  },
});

```

This is purely additive — **subclassing `Agent` / `AgentTask` still works** exactly as before.

## 4. New capabilities (previously Python-only)

- **Toolsets** — manage groups of tools that share setup and teardown (DB connections, websockets, MCP clients).
- **Async tools** — keep talking while a long-running tool runs, with progress updates and filler speech.
- **Provider tools** — model-lab server-side tools (OpenAI `WebSearch`/`FileSearch`/`CodeInterpreter`, Gemini `GoogleSearch`/`GoogleMaps`/…).
- **`LLMStream.collect()`** — await a full chat response as a single object (`text`, `toolCalls`, `usage`).
- **`withMockTools`** — mock tools in tests.
- **Direct imports** — `Agent`, `AgentSession`, `AgentTask`, `tool`, etc. are now exported directly from `@livekit/agents`, so the `voice.`/`llm.` prefixes are optional.

* * *

## Release plan

We’re publishing a **pre-release** (not `@latest`), **available early next week** , so teams can validate their setups and get familiar with the new API before we cut the official 1.5.0. This also avoids anyone on `@latest` in prod accidentally pulling breaking changes before they’ve migrated.

**Drop a comment if:** you use `ToolContext` directly, you hit anything that doesn’t migrate cleanly. Thanks! 🙏

---

<div class="post-metadata">

**Author:** ![chase\_fagen](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/chase_fagen/32/1003_2.png) [@chase\_fagen](https://community.livekit.io/u/chase_fagen)\
**Post date:** [June 22, 2026, 11:01am UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/2 "2026-06-22T11:01:22Z")

</div>

Hi Brian,

Super excited for this release and thank you for the teams hard work. My company Acuity Health builds telephony AI scheduling medical receptionists. Here are some of my inputs for your above questions:

We are not indexing, using `Object.keys()`, or spreading `toolCtx` heavily in production yet. But we are moving toward a dynamic tool model where we likely would need those patterns.

Indexing: yes, we’d want to fetch/check specific tools by name based on patient state or workflow state.

`Object.keys()`: yes, we’d want to inspect which tools are currently active for debugging, validation, and possibly observability.

Spreading: yes, or the equivalent, we’d want to add/remove tools dynamically during a session as the patient moves through states, for example matched patient, new patient, urgent symptom, insurance flow, scheduling flow, refill request, etc.

---

<div class="post-metadata">

**Author:** ![Brian\_Yin](https://avatars.discourse-cdn.com/v4/letter/b/7ba0ec/32.png) [@Brian\_Yin](https://community.livekit.io/u/Brian_Yin)\
**Post date:** [June 22, 2026, 9:15pm UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/3 "2026-06-22T21:15:00Z")

</div>

Hi Chase, thanks a lot for the detailed feedback.

In 1.5, those patterns should still be supported, but through the `ToolContext` APIs instead of treating `toolCtx` itself as a plain object.

For the cases you mentioned:

```ts
// Check/fetch a specific function tool by name
const tool = agent.toolCtx.getFunctionTool('scheduleAppointment');

if (agent.toolCtx.hasTool('scheduleAppointment')) {
  // tool is currently available
}

```

For debugging / observability:

```ts
// Function tools only, legacy-style name -> tool map
const functionToolNames = Object.keys(agent.toolCtx.functionTools);

// Everything currently registered, including provider tools / toolsets
const allTools = agent.toolCtx.flatten();

```

For dynamically adding/removing tools during a session, the recommended path is to construct the new tool list and call `updateTools`:

```ts
// Add a tool
await agent.updateTools([
  ...agent.toolCtx.tools,
  schedulingTool,
]);

// Remove a function tool by name
await agent.updateTools(
  agent.toolCtx.tools.filter((tool) => tool.name !== 'schedulingTool'),
);

```

---

<div class="post-metadata">

**Author:** ![chase\_fagen](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/chase_fagen/32/1003_2.png) [@chase\_fagen](https://community.livekit.io/u/chase_fagen)\
**Post date:** [June 24, 2026, 5:50pm UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/4 "2026-06-24T17:50:49Z")

</div>

Thank you so much! Looking forward to testing the preview. Is it available now?

---

<div class="post-metadata">

**Author:** ![Brian\_Yin](https://avatars.discourse-cdn.com/v4/letter/b/7ba0ec/32.png) [@Brian\_Yin](https://community.livekit.io/u/Brian_Yin)\
**Post date:** [June 24, 2026, 11:27pm UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/5 "2026-06-24T23:27:59Z")

</div>

Yes, the pre-release is up ([0.0.0-next-20260624041820](https://www.npmjs.com/package/@livekit/agents/v/0.0.0-next-20260624041820))!

---

<div class="post-metadata">

**Author:** ![chase\_fagen](https://sea1.discourse-cdn.com/flex001/user_avatar/community.livekit.io/chase_fagen/32/1003_2.png) [@chase\_fagen](https://community.livekit.io/u/chase_fagen)\
**Post date:** [June 26, 2026, 11:44am UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/6 "2026-06-26T11:44:13Z")

</div>

Thank you!

Any change the production release is ready today so I can migrate over the weekend?

Super excited!

---

<div class="post-metadata">

**Author:** ![Brian\_Yin](https://avatars.discourse-cdn.com/v4/letter/b/7ba0ec/32.png) [@Brian\_Yin](https://community.livekit.io/u/Brian_Yin)\
**Post date:** [June 26, 2026, 8:31pm UTC](https://community.livekit.io/t/livekit-agents-for-node-js-1-5-0-is-coming-feedback-wanted/1466/7 "2026-06-26T20:31:20Z")

</div>

Hey @chase_fagen, we will going to release the official 1.5.0 early next week!
