Describe the bug
When a tool's execute function spawns an AgentTask via task.run(), tool calls from the LLM are silently dropped. This affects both turns within the task and turns on the parent agent after the task completes. The agent logs "received a tool call with toolChoice set to 'none', ignoring". The LLM has no signal that its output was discarded - it keeps retrying the same tool call while the caller hears dead air.
Hypothesis: This appears to be caused by functionCallStorage context from the spawning tool persisting into subsequent generateReply() calls, which forces toolChoice = "none".
Version
Steps to reproduce
// A task that asks a yes/no question, then calls a tool to record the answer
function createConsentTask(chatCtx?: llm.ChatContext): voice.AgentTask<boolean> {
const task = new voice.AgentTask<boolean>({
chatCtx,
instructions: "Ask the user if they consent to recording. Call recordConsent with their answer.",
tools: {
recordConsent: llm.tool({
description: "Record the user's consent answer",
parameters: z.object({ consented: z.boolean() }),
execute: async ({ consented }) => {
task.complete(consented);
return undefined;
},
}),
},
});
return task;
}
// Parent agent tool that spawns the task
const checkConsent = llm.tool({
description: "Check recording consent",
parameters: z.object({ reason: z.string() }),
execute: async (_params, { ctx }) => {
const task = createConsentTask(ctx.session.chatCtx);
await task.run();
return { result: "done" };
},
});
// Flow:
// 1. LLM calls checkConsent → task starts, asks "Do you consent to recording?"
// 2. User says "Yes" → LLM tries to call recordConsent → silently dropped
// (task hangs — workaround: override onUserTurnCompleted on the task)
// 3. After task.complete() returns to parent → LLM tries to call any tool
// → silently dropped for the rest of the session
What we observed
Within the task: If the task asks the user a question and then needs to call a tool based on their answer (second turn), the tool call is dropped. The task appears to hang.
After the task completes: The parent agent continues but when it tries to call a tool, it's silently dropped.
Text input is unaffected: Sending a text message via session.run({ userInput }) works normally — tools are called. This path creates a fresh async context and calls generateReply() directly, bypassing onUserTurnCompleted. Only VAD-triggered voice turns are affected.
Confirmed in logs:
received a tool call with toolChoice set to 'none', ignoring
function: "<function>"
Partial Workaround
Override onUserTurnCompleted on the task with explicit toolChoice: "auto" + StopResponse.
task.onUserTurnCompleted = async (chatCtx, newMessage) => {
task.session.generateReply({ toolChoice: "auto", userInput: newMessage, chatCtx });
throw new voice.StopResponse();
};
This doesn't fix it for the parent agent, and likely may cause other problems.
Environment
- macOS Darwin 25.2.0 (arm64)
- Node 24.10.0 with
--no-async-context-frame
@livekit/agents 1.2.4
Describe the bug
When a tool's
executefunction spawns anAgentTaskviatask.run(), tool calls from the LLM are silently dropped. This affects both turns within the task and turns on the parent agent after the task completes. The agent logs"received a tool call with toolChoice set to 'none', ignoring". The LLM has no signal that its output was discarded - it keeps retrying the same tool call while the caller hears dead air.Hypothesis: This appears to be caused by
functionCallStoragecontext from the spawning tool persisting into subsequentgenerateReply()calls, which forcestoolChoice = "none".Version
@livekit/agents1.2.4--no-async-context-frame- see getJobContext() fails inside tool execute functions on Node 24 (AsyncContextFrame context loss) #1255Steps to reproduce
What we observed
Within the task: If the task asks the user a question and then needs to call a tool based on their answer (second turn), the tool call is dropped. The task appears to hang.
After the task completes: The parent agent continues but when it tries to call a tool, it's silently dropped.
Text input is unaffected: Sending a text message via
session.run({ userInput })works normally — tools are called. This path creates a fresh async context and callsgenerateReply()directly, bypassingonUserTurnCompleted. Only VAD-triggered voice turns are affected.Confirmed in logs:
Partial Workaround
Override
onUserTurnCompletedon the task with explicittoolChoice: "auto"+StopResponse.This doesn't fix it for the parent agent, and likely may cause other problems.
Environment
--no-async-context-frame@livekit/agents1.2.4