After calling AgentTask from a tool and returning ...
# ask-ai
a
After calling AgentTask from a tool and returning from that tool none of the other tools are available. Do I manually need to set the active agent to the main agent? the logs shows
Copy code
[04:50:08.947] DEBUG (22124): Agent handoff inserted into chat context
    previousAgentId: "s_i_p_transfer_task"
    newAgentId: "assistant"
but it seems none of the tools of the main agent is available
t
Thanks for reaching out, Ahron. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding to your question about whether you need to manually reset the active agent after calling an
AgentTask
, and why the main agent’s tools appear unavailable after returning from the task. Short answer: You do not need to manually set the active agent back to the main agent. The framework automatically hands control back (your log shows that handoff: previousAgentId "s_i_p_transfer_task" → newAgentId "assistant"). Why your tools may seem unavailable • Tool registration: Tools must be defined/attached to the main agent instance that regains control. If the agent instance that returns doesn’t have the tools registered, they won’t be available. • Instance replacement or reinitialization: If your code replaces or reinitializes the main agent (or creates a new agent instance) during/after the task, the returned agent may lack the original tool bindings. • Expected exclusive control during task: While an
AgentTask
runs, the task takes exclusive control of the session and the main agent’s tools are unavailable during that period. After completion, the main agent’s tools should be restored automatically. Concrete debugging steps 1. Confirm tools are registered on the main agent ◦ Ensure your main agent declares its tools (e.g., with
@function_tool
in Python or
llm.tool()
in Node) or passes them via the
tools
parameter to its constructor. 1. Verify the same agent instance is returning ◦ Check for any code that might recreate or overwrite the main agent instance before/after the task completes. ◦ Log the agent instance identity (or a unique id) before the task, during task handoff, and after return to ensure it’s the same object with the same tool bindings. 1. Reproduce with a minimal example ◦ Create a small test that registers one simple tool on the main agent, starts an
AgentTask
, and then checks availability after the task completes. If the tool is missing there, it isolates the problem to agent instantiation/registration. 1. Inspect context/merging behavior ◦ Confirm your task isn’t modifying the agent’s instruction set or tool list as part of its execution or teardown. Tools should remain attached to the main agent after context merges back. If you’d like, share: • A minimal code snippet showing how you construct the main agent and register tools, and how you call the
AgentTask
. • The logs around the task start, the handoff, and immediately after return (any lines that reference agent IDs or tool registration). I can review the snippet and logs and point out exactly where tools might be getting detached or how to ensure they persist across the task boundary. Sources: Agents and handoffs | LiveKit Documentation | Collect recording consent with tasks | LiveKit Documentation | Tool definition and use | LiveKit Documentation
a
how can I access the active agent info to see if it has any tools?
t
You're on the right track — to check the active agent and whether it has tools, inspect the session's
agent
and that agent's
tools
property. Below are concise examples for both Python and Node.js and a few small checks you can add. Python
Copy code
# Get the active agent from the session
active_agent = session.agent

# Basic presence check
if not active_agent:
    print("No active agent set on the session.")
else:
    # Check tools (could be a dict or list)
    tools = getattr(active_agent, "tools", None)
    if tools:
        print("Active agent has tools registered:", tools)
    else:
        print("Active agent has no tools.")
Node.js / TypeScript
Copy code
// Get the active agent from the session
const activeAgent = session.agent;

if (!activeAgent) {
  console.log("No active agent set on the session.");
} else {
  const tools = (activeAgent as any).tools;
  if (tools && Object.keys(tools).length > 0) {
    console.log("Active agent has tools registered:", tools);
  } else {
    console.log("Active agent has no tools.");
  }
}
Quick troubleshooting tips • Log the agent identity too (e.g.,
agent.id
or a custom identifier) before and after the task to ensure the same instance returns. • If
tools
is empty after returning from an
AgentTask
, verify you aren’t accidentally recreating or replacing the main agent instance during task execution. • Tools may be a dictionary or array depending on how you registered them; check both shapes when inspecting. If you want, paste a minimal snippet showing how you construct/register tools for the main agent and how you start the
AgentTask
— I’ll point out exactly where to inspect or add logs to confirm the tools persist. Sources: Agents and handoffs | LiveKit Documentation | Tool definition and use | LiveKit Documentation
a
in js it's currentAgent
t
Thanks — good catch. You're right: in JavaScript the active agent is accessed via
session.currentAgent
. Below are a concise confirmation and a short example you can use to check whether that active agent has tools registered. JavaScript / TypeScript
Copy code
// Get the active agent from the session
const activeAgent = session.currentAgent;

if (!activeAgent) {
  console.log("No active agent set on the session.");
} else {
  const tools = (activeAgent as any).tools;
  const hasTools = tools && (Array.isArray(tools) ? tools.length > 0 : Object.keys(tools).length > 0);

  if (hasTools) {
    console.log("Active agent has tools registered:", tools);
  } else {
    console.log("Active agent has no tools.");
  }
}
Quick tips • Log a unique agent identifier (for example
activeAgent.id
or a custom property) before the task, during handoff, and after return to confirm the same instance is active. • Check both shapes for
tools
(array or object) since registration methods can produce either. • If
currentAgent.tools
is empty after returning from an
AgentTask
, look for code that might recreate or overwrite the agent instance during the task boundary. If you share a minimal snippet showing how you register tools and start the
AgentTask
, I can point out exactly where to add these checks or what else to log.
a
Copy code
this.session.on(voice.AgentSessionEventTypes.FunctionToolsExecuted, (evt) => {
                console.log(this.session.currentAgent.toolCtx);
            });
the log shows it does have the tools
t
Thanks — that log is a good signal: seeing
this.session.currentAgent.toolCtx
populated in the
FunctionToolsExecuted
event shows the active agent does have tools registered at that moment. Next steps to narrow down why tools seem unavailable after returning from an `AgentTask`: 1. Confirm timeline with logs ◦ Add logs just before you start the
AgentTask
, inside the task (if possible), on the handoff event, and immediately after the task completes: ▪︎ Log
session.currentAgent?.id
(or other identifier) ▪︎ Log
session.currentAgent?.toolCtx
(or
tools
) ◦ This will show whether the same agent instance and tool context exist at every stage. 1. Check where you attempt to call tools ◦ Are you invoking tools via the agent instance (e.g.,
session.currentAgent
) immediately after the task returns, or from some other object/context? If calls run from a stale reference to the agent, they may appear unavailable. ◦ Ensure any stored references to the agent were not captured before the task and then mutated/replaced. 1. Verify tool context vs. runtime availability ◦
toolCtx
(or
tools
) showing in the event means tools are registered, but runtime execution may fail if: ▪︎ The caller lacks the proper binding to use
toolCtx
. ▪︎ The task altered permissions/state needed to invoke the tools. ◦ After the task completes, try a simple programmatic invocation of one registered tool (a no-op or test tool) to confirm execution works. 1. Minimal repro ◦ If the above logs still show the agent and tools are present but calls fail, create a minimal script: i. Register a single test tool on the main agent. ii. Start an
AgentTask
that returns quickly. iii. After return, read
session.currentAgent.toolCtx
and immediately invoke the test tool. ◦ If the test tool runs, the problem is likely in your larger app’s lifecycle or how you reference the agent. If it fails, paste the minimal snippet and logs and I’ll examine it. If you’d like, paste: • The logs for the sequence (before task, during task, handoff, after return). • The small snippet that invokes a tool after return. I’ll review those and point out exactly where the reference/state might be getting lost.
a
happens after calling this tool
Copy code
class SIPTransferTask extends voice.AgentTask<string> {
    constructor(private config: TransferToolConfigSchema, private destination: string) {
        super({instructions: 'Performing SIP transfer...', allowInterruptions: true});
    }

    override async onEnter(): Promise<void> {
        if (this.config.speak_during_execution && this.config?.speak_message) {
            if (this.config.speak_type === 'prompt') {
                this.session.generateReply({
                    instructions: this.config.speak_message,
                    allowInterruptions: true,
                })
            } else {
                this.session.say(this.config.speak_message, { allowInterruptions: true });
            }
        }

        const { room } = getJobContext();

        try {
            await transferSipCall(room, this.destination)
            this.complete(`Transfer initiated to ${this.destination}.`);
        } catch (err) {
            const message = err instanceof Error ? err.message : 'SIP transfer failed.';
            this.complete(`Transfer could not be completed: ${message}`);
        }
    }
}
Copy code
llm.tool({
        description: 'Perform a blind (cold) transfer of the current call to the given phone number, extension.',
        parameters: schema,
        execute: async ({ destination, }, { ctx }) => {
            return runInToolContext(async () => {
                console.log(`Initiating SIP transfer to ${destination} runIn options:`, config);
                await ctx.waitForPlayout();
                return new SIPTransferTask(config, destination).run();
            })
        },
    });
t
Thanks — this is helpful context. I’m responding to your example where the main agent’s tools disappear after running
SIPTransferTask
from a tool. Short answer • The framework automatically returns control to the main agent when an
AgentTask
completes, and the main agent’s tools should remain registered. You do not need to manually set the active agent back. If tools appear missing after your
SIPTransferTask
, that’s caused by something in the execution path (session/agent mutation, timing/cleanup, or tool-context handling), not by needing to reset the agent. What to check next (concise, actionable) 1. Add targeted logs around the full lifecycle ◦ Log these values at each stage: ▪︎ Before calling the tool:
session.currentAgent?.id
and
session.currentAgent?.toolCtx
(or
tools
) ▪︎ Inside
runInToolContext
right before
await ctx.waitForPlayout()
▪︎ In
SIPTransferTask.onEnter
at start and right before calling
this.complete(...)
▪︎ On task completion/handoff event (
FunctionToolsExecuted
already shows something) and immediately after the task returns ◦ This will show whether the same agent instance and tool context exist throughout. 1. Confirm the agent instance is not being replaced ◦ Look for code that might reinstantiate or overwrite
session.currentAgent
(or the main agent object) during the transfer flow or in any callbacks invoked by
transferSipCall
. ◦ Instead of relying on a previously captured agent reference, always read
session.currentAgent
after the task returns. 1. Verify
transferSipCall
and cleanup don’t terminate the session ◦ If
transferSipCall
disconnects participants or otherwise ends the room/session, the agent session could be torn down and tools would be unavailable. ◦ Ensure any playout/transfer-related cleanup runs after the agent has fully resumed, or delay cleanup until after you’ve verified tool calls. 1. Examine
runInToolContext
◦ Test the same flow without
runInToolContext
to rule it out as a cause: ▪︎ Replace the wrapper temporarily with direct creation/run of
SIPTransferTask
and compare results. ◦ If leaving it out fixes it, the issue is within how
runInToolContext
mutates session/agent state in your environment. 1. Reproduce with a minimal test ◦ Create a small repro: i. Register a single test tool on the main agent that logs when invoked. ii. From that tool, run a very small
AgentTask
that immediately completes. iii. After return, log
session.currentAgent.toolCtx
and immediately invoke the test tool. ◦ If the test tool invocation fails here, paste the repro and logs and I’ll review precisely where state is lost. Immediate checks you can paste here • Logs showing: before-tool, inside task (start and just before complete), handoff, and right after return. • The implementation of
transferSipCall
(does it disconnect or mutate session/agent?) • Any code that runs on task completion or in callbacks (especially those that touch
session
or agent creation/assignment) If you share those logs/snippets, I’ll point out the exact line(s) likely causing the tools to be lost and give a small code change to fix it. Sources: Collect recording consent with tasks | LiveKit Documentation | Tasks and task groups | LiveKit Documentation | Agent speech and audio | LiveKit Documentation
a
happens only when using AgentTask, but works fine when called like this
Copy code
llm.tool({
        description: 'Perform a blind (cold) transfer of the current call to the given phone number, extension.',
        parameters: schema,
        execute: async ({ destination, }, { ctx }) => {
            return runInToolContext(async () => {
                console.log(`Initiating SIP transfer to ${destination} runIn options:`, config);
                await ctx.waitForPlayout();
                // return new SIPTransferTask(config, destination).run();
                return await transferSipCall(getJobContext().room, destination)
            })
        },
    });
t
Thanks — this is a very useful distinction: the flow works when you call
transferSipCall
directly but the main agent’s tools disappear only when you run the same logic inside an
AgentTask
. I’ll focus on what specifically differs between those two flows and give targeted checks and a minimal repro you can run to pinpoint the cause. Why this difference matters (short) • AgentTasks take exclusive control of the session while they run. That handoff/return is automatic, but anything the task does that mutates the session/agent (or triggers session teardown) can make tools look unavailable after return. • Calling
transferSipCall
directly runs in the same agent context and avoids the task handoff mechanics, so you don’t hit whatever side effect is triggered by the task lifecycle. Targeted checks to run now 1. Confirm the task is fully completed before you try to use tools again ◦ Right after
new SIPTransferTask(...).run()
resolves, log: ▪︎
session.currentAgent?.id
▪︎
session.currentAgent?.toolCtx
(or
tools
) ◦ If those show tools present immediately after the promise resolves, the problem is likely elsewhere (e.g., race later in your app). 1. Log inside task lifecycle and callbacks ◦ Add logs at these points (include timestamps): ▪︎ Before you call the tool (start of
execute
) ▪︎ At the start of
SIPTransferTask.onEnter
▪︎ Just before
this.complete(...)
▪︎ In any
onExit
or task cleanup if present ▪︎ Immediately after the
run()
promise resolves in the tool
execute
function ◦ This will confirm whether
this.complete(...)
is hit, and when the framework performs the handoff. 1. Verify
transferSipCall
does not mutate session/agent or end the call/session ◦ Confirm it does not: ▪︎ Call any session tear-down, room disconnect, or agent re-creation. ▪︎ Reassign
session.currentAgent
or its internal state. ◦ If transfer ends the media/room, the agent session can be stopped and tools lost. 1. Test without runInToolContext and with direct call inside task ◦ Two quick variations: a) In the tool
execute
, run the task without
runInToolContext
to see if
runInToolContext
is contributing. b) Inside
SIPTransferTask.onEnter
, call
await transferSipCall(...)
directly (instead of delegating elsewhere) and then
this.complete(...)
. • If either change fixes it, the wrapper or delegation path is the culprit. 1. Minimal repro to isolate framework vs app ◦ Register a trivial tool on the main agent that: ▪︎ Logs when invoked ▪︎ From that tool, run an AgentTask that does nothing but
this.complete("ok")
▪︎ After
run()
resolves, immediately attempt to invoke the trivial tool again ◦ If the second invocation fails, it’s reproducible and likely framework-related. If it succeeds, the issue is in your SIP-specific code (e.g.,
transferSipCall
or playout/cleanup). Common root causes to look for • The task or transfer function triggers disconnection or room cleanup that stops the session. •
runInToolContext
or some callback reinitializes or replaces the agent instance (so the returned agent is not the same object with the same tool bindings). • A race: some later code runs and clears or re-registers tools after the task completes. If you want, paste these three small snippets/log sections and I’ll inspect them: • The logs around start → task onEnter → this.complete → run() resolution → immediate post-return (include timestamps). • The
transferSipCall
implementation (just the parts that manipulate session/room/participants). • The minimal repro I described (or I can supply one if you want to run it). I’ll point to the exact line that’s causing the tools to disappear and propose the smallest fix.