chat.headStart relay ends the first turn after one empty session.out long-poll window (SDK 4.7.7)

オープン
#5,056 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

  • #5063 @ghazy2001 による — マージされずにクローズ

評価

難易度
4/5
見積もり時間
1〜2日
初心者へのやさしさ
72/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
node.js, typescript
領域
api, backend

調査の方向性

Start by reading stitchHandoverStream in dist/esm/v3/chat-server.js and the stream timeout section in docs/ai-chat/client-protocol.mdx; compare its EOF handling with the browser transport's resumeAfterEof. Check how the relay tracks the last event ID and handles aborts. Done means an empty long-poll window does not close the first turn before turn-complete; verify the behavior with the 90-second sleeping-tool reproduction.

索引モデルが issue の本文から書いたものです。

説明

chat.headStart relay ends the first turn after one empty session.out long-poll window

SDK: @trigger.dev/sdk 4.7.7 (also present in 0.0.0-rc-4-8-0-20261021151928 and 4.8.0-webhooks.20261020092920)

What happens

On a head-start first turn, stitchHandoverStream (dist/esm/v3/chat-server.js) reads the agent's session.out once with apiClient.subscribeToSessionStream. When a tool runs for more than ~60 s without writing a record, the long-poll window closes empty, the for await loop ends, the relay writes trigger:session-state and closes the response — without trigger:turn-complete. The browser transport's sendMessagesViaHandover then clears isStreaming, so the UI treats the turn as finished while the agent run keeps going and later completes normally.

Measured in a browser: the /api/chat body ended 59,994 ms and 60,003 ms after the last chunk, both times with trigger:session-state and no turn-complete.

Why it looks like a bug

docs/ai-chat/client-protocol.mdx § Stream timeout: "If nothing arrives by the deadline, the server sends data: [DONE] and closes. Reconnect with Last-Event-ID to continue." The browser transport does this (resumeAfterEof); the head-start relay does not.

Repro

  1. chat.headStart route + chat.agent with a tool that sleeps 90 s without writing to the stream.
  2. Send the first message of a new chat through the transport's headStart option.
  3. The response ends ~60 s into the tool; the run finishes the turn later.

Fix we are running (patch on 4.7.7)

Loop the session.out read until turn-complete or abort, resuming from the last seen record, with waitBeforeEofResubscribe between empty windows:

let emptyWindows = 0;
while (!turnComplete && !subscriptionSignal.aborted) {
  if (emptyWindows > 0) await waitBeforeEofResubscribe(emptyWindows, subscriptionSignal);
  let windowHadRecords = false;
  const resumeFrom = latestEventId ?? customerLastEventId;
  const agentStream = await apiClient.subscribeToSessionStream(chatId, "out", {
    ...(resumeFrom != null ? { lastEventId: resumeFrom } : {}),
    signal: subscriptionSignal,
    onPart: (part) => { windowHadRecords = true; if (part.id) latestEventId = part.id; },
    onControl,
  });
  // existing for-await / AbortError handling
  emptyWindows = windowHadRecords ? 0 : emptyWindows + 1;
}

Related: docs/ai-chat/fast-starts.mdx says the handler keeps the response open for the whole first turn, so the serverless maxDuration must cover it. It may be worth noting that a first turn longer than the platform maximum still ends the relay, and that the transport does not fall back to session.out when the head-start response ends or errors before turn-complete.

主要言語
TypeScript
スター
16.5k
フォーク
1.5k
平均マージ
1日 15時間
マージ済み PR(30日)
17

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

triggerdotdev/trigger.dev のほかの issue

triggerdotdev/trigger.dev の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。