Skip to content

Commit c8b5507

Browse files
committed
fix(sdk): hydrate chat session handle from tool metadata in ai.toolExecute subtasks
`chat.stream.writer({ target: "root" })` and the rest of the `chat.stream` helpers all funnel through `getChatSession()`, which looked up the session handle in run-scoped `locals`. That key is only populated inside a `chat.agent` run — `ai.toolExecute` subtasks running in a separate worker process don't inherit it, so any chat helper used from a subtask threw `"chat.agent session handle is not initialized"`. Resolve the handle lazily: if it's not in `locals`, fall back to the `chatId` already threaded through tool metadata under `METADATA_KEY` and call `sessions.open(chatId)` to materialize it. Cache the result back into `locals` so subsequent calls in the same subtask hit the fast path.
1 parent 950c0b5 commit c8b5507

1 file changed

Lines changed: 25 additions & 10 deletions

File tree

  • packages/trigger-sdk/src/v3

packages/trigger-sdk/src/v3/ai.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -658,20 +658,35 @@ export async function __replaySessionOutTailProductionPathForTests<
658658
}
659659

660660
/**
661-
* Resolve the Session handle for the current chat.agent run. Throws if
662-
* called outside of a chat.agent `run()` — every internal consumer is
663-
* inside the run, and every external consumer goes through the public
664-
* `sessions.open(id)` entry point.
661+
* Resolve the Session handle for the current chat.agent run.
662+
*
663+
* Two contexts populate this:
664+
* 1. Inside a `chat.agent` run — `locals.chatSessionHandleKey` is set
665+
* at boot (lines 4665 / 4760).
666+
* 2. Inside an `ai.toolExecute` subtask — the tool wrapper threads the
667+
* parent's `chatId` through tool metadata under `METADATA_KEY`. We
668+
* lazily open the session from that chatId here so subtasks can use
669+
* `chat.stream.writer({ target: "root" })` to write back to the
670+
* parent's chat session without any wiring.
671+
*
672+
* Throws if neither is available.
665673
* @internal
666674
*/
667675
function getChatSession(): SessionHandle {
668-
const handle = locals.get(chatSessionHandleKey);
669-
if (!handle) {
670-
throw new Error(
671-
"chat.agent session handle is not initialized. This indicates a chat.agent helper was used outside of a chat.agent run, or the transport did not send a sessionId."
672-
);
676+
let handle = locals.get(chatSessionHandleKey);
677+
if (handle) return handle;
678+
679+
// Fallback: subtask context. The parent threaded chatId via tool metadata.
680+
const toolMeta = metadata.get(METADATA_KEY) as ToolCallExecutionOptions | undefined;
681+
if (toolMeta?.chatId) {
682+
handle = sessions.open(toolMeta.chatId);
683+
locals.set(chatSessionHandleKey, handle);
684+
return handle;
673685
}
674-
return handle;
686+
687+
throw new Error(
688+
"chat.agent session handle is not initialized. This indicates a chat.agent helper was used outside of a chat.agent run, or the transport did not send a sessionId."
689+
);
675690
}
676691

677692
/**

0 commit comments

Comments
 (0)