@@ -13,6 +13,7 @@ import {
1313 type TaskIdentifier ,
1414 type TaskOptions ,
1515 type TaskSchema ,
16+ type TaskRunContext ,
1617 type TaskWithSchema ,
1718} from "@trigger.dev/core/v3" ;
1819import type {
@@ -44,6 +45,9 @@ import type { ResolvedPrompt } from "./prompt.js";
4445import { streams } from "./streams.js" ;
4546import { createTask } from "./shared.js" ;
4647import { tracer } from "./tracer.js" ;
48+
49+ /** Re-export for typing `ctx` in `chat.task` hooks without importing `@trigger.dev/core`. */
50+ export type { TaskRunContext } from "@trigger.dev/core/v3" ;
4751import {
4852 CHAT_STREAM_KEY as _CHAT_STREAM_KEY ,
4953 CHAT_MESSAGES_STREAM_ID ,
@@ -612,6 +616,11 @@ export type ChatTaskSignals = {
612616 */
613617export type ChatTaskRunPayload < TClientData = unknown > = ChatTaskPayload < TClientData > &
614618 ChatTaskSignals & {
619+ /**
620+ * Task run context — same object as the `ctx` passed to a standard `task({ run })` handler’s second argument.
621+ * Use for tags, metadata, parent run links, or any API that needs the full run record.
622+ */
623+ ctx : TaskRunContext ;
615624 /** Token usage from the previous turn. Undefined on turn 0. */
616625 previousTurnUsage ?: LanguageModelUsage ;
617626 /** Cumulative token usage across all completed turns so far. */
@@ -742,6 +751,8 @@ interface CompactionState {
742751const chatCompactionStateKey = locals . create < CompactionState > ( "chat.compaction" ) ;
743752const chatOnCompactedKey =
744753 locals . create < ( event : CompactedEvent ) => Promise < void > | void > ( "chat.onCompacted" ) ;
754+ /** @internal Full task `ctx` for the active `chat.task` run (for hooks invoked from nested compaction). */
755+ const chatTaskRunContextKey = locals . create < TaskRunContext > ( "chat.taskRunContext" ) ;
745756const chatPrepareMessagesKey =
746757 locals . create < ( event : PrepareMessagesEvent < unknown > ) => ModelMessage [ ] | Promise < ModelMessage [ ] > > (
747758 "chat.prepareMessages"
@@ -980,6 +991,8 @@ export type CompactionChunkData = {
980991 * Event passed to the `onCompacted` callback.
981992 */
982993export type CompactedEvent = {
994+ /** Task run context — same as `task` lifecycle hooks and `chat.task` `run({ ctx })`. */
995+ ctx : TaskRunContext ;
983996 /** The generated summary text. */
984997 summary : string ;
985998 /** The messages that were compacted (pre-compaction). */
@@ -1280,6 +1293,7 @@ async function chatCompact(
12801293 const onCompactedHook = locals . get ( chatOnCompactedKey ) ;
12811294 if ( onCompactedHook ) {
12821295 await onCompactedHook ( {
1296+ ctx : locals . get ( chatTaskRunContextKey ) ! ,
12831297 summary,
12841298 messages,
12851299 messageCount : messages . length ,
@@ -1863,6 +1877,8 @@ async function pipeChat(
18631877 * Event passed to the `onPreload` callback.
18641878 */
18651879export type PreloadEvent < TClientData = unknown > = {
1880+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
1881+ ctx : TaskRunContext ;
18661882 /** The unique identifier for the chat session. */
18671883 chatId : string ;
18681884 /** The Trigger.dev run ID for this conversation. */
@@ -1879,6 +1895,8 @@ export type PreloadEvent<TClientData = unknown> = {
18791895 * Event passed to the `onChatStart` callback.
18801896 */
18811897export type ChatStartEvent < TClientData = unknown > = {
1898+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
1899+ ctx : TaskRunContext ;
18821900 /** The unique identifier for the chat session. */
18831901 chatId : string ;
18841902 /** The initial model-ready messages for this conversation. */
@@ -1903,6 +1921,8 @@ export type ChatStartEvent<TClientData = unknown> = {
19031921 * Event passed to the `onTurnStart` callback.
19041922 */
19051923export type TurnStartEvent < TClientData = unknown , TUIM extends UIMessage = UIMessage > = {
1924+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
1925+ ctx : TaskRunContext ;
19061926 /** The unique identifier for the chat session. */
19071927 chatId : string ;
19081928 /** The accumulated model-ready messages (all turns so far, including new user message). */
@@ -1935,6 +1955,8 @@ export type TurnStartEvent<TClientData = unknown, TUIM extends UIMessage = UIMes
19351955 * Event passed to the `onTurnComplete` callback.
19361956 */
19371957export type TurnCompleteEvent < TClientData = unknown , TUIM extends UIMessage = UIMessage > = {
1958+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
1959+ ctx : TaskRunContext ;
19381960 /** The unique identifier for the chat session. */
19391961 chatId : string ;
19401962 /** The full accumulated conversation in model format (all turns so far). */
@@ -2022,8 +2044,9 @@ export type ChatTaskOptions<
20222044 * chat.task({
20232045 * id: "my-chat",
20242046 * clientDataSchema: z.object({ model: z.string().optional(), userId: z.string() }),
2025- * run: async ({ messages, clientData, signal }) => {
2047+ * run: async ({ messages, clientData, ctx, signal }) => {
20262048 * // clientData is typed as { model?: string; userId: string }
2049+ * // ctx is the same TaskRunContext as in task({ run: (payload, { ctx }) => ... })
20272050 * },
20282051 * });
20292052 * ```
@@ -2034,7 +2057,8 @@ export type ChatTaskOptions<
20342057 * The run function for the chat task.
20352058 *
20362059 * Receives a `ChatTaskRunPayload` with the conversation messages, chat session ID,
2037- * trigger type, and abort signals (`signal`, `cancelSignal`, `stopSignal`).
2060+ * trigger type, task `ctx` (same as `task({ run })`’s second argument), and abort signals
2061+ * (`signal`, `cancelSignal`, `stopSignal`).
20382062 *
20392063 * **Auto-piping:** If this function returns a value with `.toUIMessageStream()`,
20402064 * the stream is automatically piped to the frontend.
@@ -2049,7 +2073,7 @@ export type ChatTaskOptions<
20492073 *
20502074 * @example
20512075 * ```ts
2052- * onPreload: async ({ chatId, clientData }) => {
2076+ * onPreload: async ({ ctx, chatId, clientData }) => {
20532077 * await db.chat.create({ data: { id: chatId } });
20542078 * userContext.init(await loadUser(clientData.userId));
20552079 * }
@@ -2064,7 +2088,7 @@ export type ChatTaskOptions<
20642088 *
20652089 * @example
20662090 * ```ts
2067- * onChatStart: async ({ chatId, messages, clientData }) => {
2091+ * onChatStart: async ({ ctx, chatId, messages, clientData }) => {
20682092 * await db.chat.create({ data: { id: chatId, userId: clientData.userId } });
20692093 * }
20702094 * ```
@@ -2080,7 +2104,7 @@ export type ChatTaskOptions<
20802104 *
20812105 * @example
20822106 * ```ts
2083- * onTurnStart: async ({ chatId, uiMessages }) => {
2107+ * onTurnStart: async ({ ctx, chatId, uiMessages }) => {
20842108 * await db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } });
20852109 * }
20862110 * ```
@@ -2097,7 +2121,7 @@ export type ChatTaskOptions<
20972121 *
20982122 * @example
20992123 * ```ts
2100- * onBeforeTurnComplete: async ({ writer, usage }) => {
2124+ * onBeforeTurnComplete: async ({ ctx, writer, usage }) => {
21012125 * if (usage?.inputTokens && usage.inputTokens > 5000) {
21022126 * writer.write({ type: "data-compaction", id: generateId(), data: { status: "compacting" } });
21032127 * // ... compact messages ...
@@ -2117,7 +2141,7 @@ export type ChatTaskOptions<
21172141 *
21182142 * @example
21192143 * ```ts
2120- * onCompacted: async ({ summary, totalTokens, chatId }) => {
2144+ * onCompacted: async ({ ctx, summary, totalTokens, chatId }) => {
21212145 * logger.info("Compacted", { totalTokens, chatId });
21222146 * await db.compactionLog.create({ data: { chatId, summary } });
21232147 * }
@@ -2177,7 +2201,7 @@ export type ChatTaskOptions<
21772201 *
21782202 * @example
21792203 * ```ts
2180- * onTurnComplete: async ({ chatId, messages }) => {
2204+ * onTurnComplete: async ({ ctx, chatId, messages }) => {
21812205 * await db.chat.update({ where: { id: chatId }, data: { messages } });
21822206 * }
21832207 * ```
@@ -2366,8 +2390,10 @@ function chatTask<
23662390 ...restOptions ,
23672391 run : async (
23682392 payload : ChatTaskWirePayload < TUIMessage , inferSchemaIn < TClientDataSchema > > ,
2369- { signal : runSignal }
2393+ { signal : runSignal , ctx }
23702394 ) => {
2395+ locals . set ( chatTaskRunContextKey , ctx ) ;
2396+
23712397 // Set gen_ai.conversation.id on the run-level span for dashboard context
23722398 const activeSpan = trace . getActiveSpan ( ) ;
23732399 if ( activeSpan ) {
@@ -2433,7 +2459,7 @@ function chatTask<
24332459 activeSpan . setAttribute ( "chat.preloaded" , true ) ;
24342460 }
24352461
2436- const currentRunId = taskContext . ctx ? .run . id ?? "" ;
2462+ const currentRunId = ctx . run . id ;
24372463 let preloadAccessToken = "" ;
24382464 if ( currentRunId ) {
24392465 try {
@@ -2461,6 +2487,7 @@ function chatTask<
24612487 async ( ) => {
24622488 await withChatWriter ( async ( writer ) => {
24632489 await onPreload ( {
2490+ ctx,
24642491 chatId : payload . chatId ,
24652492 runId : currentRunId ,
24662493 chatAccessToken : preloadAccessToken ,
@@ -2671,7 +2698,7 @@ function chatTask<
26712698
26722699 // Mint a scoped public access token once per turn, reused for
26732700 // onChatStart, onTurnStart, onTurnComplete, and the turn-complete chunk.
2674- const currentRunId = taskContext . ctx ? .run . id ?? "" ;
2701+ const currentRunId = ctx . run . id ;
26752702 let turnAccessToken = "" ;
26762703 if ( currentRunId ) {
26772704 try {
@@ -2694,6 +2721,7 @@ function chatTask<
26942721 async ( ) => {
26952722 await withChatWriter ( async ( writer ) => {
26962723 await onChatStart ( {
2724+ ctx,
26972725 chatId : currentWirePayload . chatId ,
26982726 messages : accumulatedMessages ,
26992727 clientData,
@@ -2728,6 +2756,7 @@ function chatTask<
27282756 async ( ) => {
27292757 await withChatWriter ( async ( writer ) => {
27302758 await onTurnStart ( {
2759+ ctx,
27312760 chatId : currentWirePayload . chatId ,
27322761 messages : accumulatedMessages ,
27332762 uiMessages : accumulatedUIMessages ,
@@ -2799,6 +2828,7 @@ function chatTask<
27992828 preloaded,
28002829 previousTurnUsage,
28012830 totalUsage : cumulativeUsage ,
2831+ ctx,
28022832 signal : combinedSignal ,
28032833 cancelSignal,
28042834 stopSignal,
@@ -3057,6 +3087,7 @@ function chatTask<
30573087 const onCompactedHook = locals . get ( chatOnCompactedKey ) ;
30583088 if ( onCompactedHook ) {
30593089 await onCompactedHook ( {
3090+ ctx,
30603091 summary,
30613092 messages : accumulatedMessages ,
30623093 messageCount : accumulatedMessages . length ,
@@ -3108,6 +3139,7 @@ function chatTask<
31083139 }
31093140
31103141 const turnCompleteEvent = {
3142+ ctx,
31113143 chatId : currentWirePayload . chatId ,
31123144 messages : accumulatedMessages ,
31133145 uiMessages : accumulatedUIMessages ,
0 commit comments