Skip to content

Commit 51ece8f

Browse files
committed
fix: address PR 3173 review feedback (closure + unhandled rejection)
- playground transport: route startSession through a ref so sidebar edits (tags, machine, maxAttempts, maxDuration, version, region) made before the first send aren't silently ignored. Mirrors the existing clientDataJsonRef pattern. - chat.handover: swallow the rethrown error after the recovery path has already run so processes started with --unhandled-rejections=throw don't crash. User-facing behavior unchanged.
1 parent 6c49485 commit 51ece8f

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.$agentParam
  • packages/trigger-sdk/src/v3

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.playground.$agentParam/route.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ function PlaygroundChat() {
260260
// Resource route prefix — all realtime traffic goes through session-authed routes
261261
const playgroundBaseURL = `${apiOrigin}/resources/orgs/${organization.slug}/projects/${project.slug}/env/${environment.slug}/playground`;
262262

263+
// The transport is constructed once (guarded ref below); reading
264+
// `startSession` directly there would freeze its closure to the
265+
// first render's sidebar values, so subsequent edits to tags /
266+
// machine / maxAttempts / maxDuration / version / region would be
267+
// silently ignored on the first send. Mirror the `clientDataJsonRef`
268+
// pattern so the transport always calls the latest `startSession`.
269+
const startSessionRef = useRef(startSession);
270+
startSessionRef.current = startSession;
271+
263272
// Create TriggerChatTransport directly (not via useTriggerChatTransport hook
264273
// to avoid React version mismatch between SDK and webapp)
265274
const transportRef = useRef<TriggerChatTransport | null>(null);
@@ -274,8 +283,8 @@ function PlaygroundChat() {
274283
// session-PAT-authed request. Wiring the same call to both
275284
// keeps the Preload button working without a separate refresh
276285
// route.
277-
startSession: async () => ({ publicAccessToken: await startSession() }),
278-
accessToken: () => startSession(),
286+
startSession: async () => ({ publicAccessToken: await startSessionRef.current() }),
287+
accessToken: () => startSessionRef.current(),
279288
baseURL: playgroundBaseURL,
280289
clientData: JSON.parse(clientDataJson || "{}") as Record<string, unknown>,
281290
...(activeConversation?.publicAccessToken

packages/trigger-sdk/src/v3/chat-server.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,16 @@ async function openHandoverSession(opts: {
587587
generateMessageId: () => turnMessageId,
588588
})
589589
);
590-
void handoverWhenDone(result).finally(() => clearTimeout(idleTimer));
590+
// `handoverWhenDone` re-throws on dispatch failure for visibility,
591+
// but the recovery (resolveDecision + handoverSkip) has already run
592+
// by then and `stitchHandoverStream` closes the response cleanly via
593+
// `decisionPromise`. The user-facing path is fine; we only suppress
594+
// the unhandled-rejection so processes started with
595+
// `--unhandled-rejections=throw` don't crash on what is effectively
596+
// a logged failure with no further action to take.
597+
void handoverWhenDone(result)
598+
.finally(() => clearTimeout(idleTimer))
599+
.catch(() => {});
591600

592601
const stitched = stitchHandoverStream(teed);
593602

0 commit comments

Comments
 (0)