@trigger.dev/sdk 4.5.7 → 4.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3636,6 +3636,9 @@ function chatAgent(options) {
3636
3636
  // Declared here so the finally can detach it — a handler leaked past
3637
3637
  // its turn duplicates every mid-stream message into the shared buffer.
3638
3638
  let turnMsgSub;
3639
+ let capturedPartialResponse;
3640
+ let responseCommitted = false;
3641
+ const turnBufferedChunks = [];
3639
3642
  try {
3640
3643
  // Extract turn-level context before entering the span. Slim
3641
3644
  // wire: at most one delta message per record. `headStartMessages`
@@ -4313,11 +4316,12 @@ function chatAgent(options) {
4313
4316
  generateMessageId: resolvedOptions.generateMessageId ?? ai_runtime_js_1.generateId,
4314
4317
  onFinish: ({ responseMessage, finishReason, }) => {
4315
4318
  capturedResponseMessage = responseMessage;
4319
+ capturedPartialResponse = responseMessage;
4316
4320
  capturedFinishReason = finishReason;
4317
4321
  resolveOnFinish();
4318
4322
  },
4319
4323
  });
4320
- await pipeChat(uiStream, {
4324
+ await pipeChat(tapUIMessageChunks(uiStream, turnBufferedChunks), {
4321
4325
  signal: combinedSignal,
4322
4326
  spanName: "stream response",
4323
4327
  });
@@ -4511,6 +4515,11 @@ function chatAgent(options) {
4511
4515
  locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
4512
4516
  }
4513
4517
  }
4518
+ if (capturedResponseMessage) {
4519
+ responseCommitted = true;
4520
+ capturedPartialResponse = capturedResponseMessage;
4521
+ turnBufferedChunks.length = 0;
4522
+ }
4514
4523
  if (runSignal.aborted)
4515
4524
  return "exit";
4516
4525
  // Await deferred background work (e.g. DB writes from onTurnStart)
@@ -4701,6 +4710,7 @@ function chatAgent(options) {
4701
4710
  parts: [...(msg.parts ?? []), ...lateParts],
4702
4711
  };
4703
4712
  capturedResponseMessage = accumulatedUIMessages[idx];
4713
+ capturedPartialResponse = capturedResponseMessage;
4704
4714
  turnCompleteEvent.responseMessage = capturedResponseMessage;
4705
4715
  turnCompleteEvent.uiMessages = accumulatedUIMessages;
4706
4716
  }
@@ -4943,10 +4953,65 @@ function chatAgent(options) {
4943
4953
  !accumulatedUIMessages.some((m) => m.id === erroredWireMessage.id)
4944
4954
  ? [...accumulatedUIMessages, erroredWireMessage]
4945
4955
  : accumulatedUIMessages;
4946
- // Fire onTurnComplete on the error path too — the docs promise it
4947
- // runs "after every turn, successful or errored" so customers can
4948
- // mark the turn failed. `responseMessage` is undefined/partial and
4949
- // `error` carries the thrown value.
4956
+ let partialResponse = capturedPartialResponse ??
4957
+ (await assemblePartialFromChunks(turnBufferedChunks));
4958
+ if (partialResponse) {
4959
+ partialResponse = cleanupAbortedParts(partialResponse);
4960
+ }
4961
+ let partialIdx = partialResponse?.id
4962
+ ? erroredUIMessages.findIndex((m) => m.id === partialResponse.id)
4963
+ : -1;
4964
+ if (partialResponse && capturedPartialResponse === undefined && partialIdx !== -1) {
4965
+ partialResponse = undefined;
4966
+ partialIdx = -1;
4967
+ }
4968
+ if (partialResponse && !partialResponse.id) {
4969
+ partialResponse = { ...partialResponse, id: (0, ai_runtime_js_1.generateId)() };
4970
+ }
4971
+ if (partialResponse && !responseCommitted) {
4972
+ const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
4973
+ if (queuedParts && queuedParts.length > 0) {
4974
+ partialResponse = {
4975
+ ...partialResponse,
4976
+ parts: [...partialResponse.parts, ...queuedParts],
4977
+ };
4978
+ locals_js_1.locals.set(chatResponsePartsKey, []);
4979
+ }
4980
+ }
4981
+ const includePartial = partialResponse != null && !responseCommitted;
4982
+ let erroredUIMessagesWithPartial = !includePartial
4983
+ ? erroredUIMessages
4984
+ : partialIdx === -1
4985
+ ? [...erroredUIMessages, partialResponse]
4986
+ : erroredUIMessages.map((m, i) => i === partialIdx ? partialResponse : m);
4987
+ let erroredNewUIMessages = erroredWireMessage ? [erroredWireMessage] : [];
4988
+ if (includePartial) {
4989
+ erroredNewUIMessages.push(partialResponse);
4990
+ }
4991
+ let erroredNewModelMessages = [];
4992
+ if (!responseCommitted) {
4993
+ try {
4994
+ if (erroredNewUIMessages.length > 0) {
4995
+ erroredNewModelMessages = await toModelMessages(erroredNewUIMessages.map((m) => stripProviderMetadata(m)));
4996
+ }
4997
+ if (erroredUIMessagesWithPartial !== accumulatedUIMessages) {
4998
+ if (partialIdx === -1) {
4999
+ const appended = erroredUIMessagesWithPartial.slice(accumulatedUIMessages.length);
5000
+ accumulatedMessages.push(...(await toModelMessages(appended.map((m) => stripProviderMetadata(m)))));
5001
+ }
5002
+ else {
5003
+ accumulatedMessages = await toModelMessages(erroredUIMessagesWithPartial);
5004
+ }
5005
+ accumulatedUIMessages = erroredUIMessagesWithPartial;
5006
+ locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
5007
+ }
5008
+ }
5009
+ catch {
5010
+ erroredNewModelMessages = [];
5011
+ erroredUIMessagesWithPartial = erroredUIMessages;
5012
+ erroredNewUIMessages = erroredWireMessage ? [erroredWireMessage] : [];
5013
+ }
5014
+ }
4950
5015
  if (onTurnComplete) {
4951
5016
  try {
4952
5017
  await tracer_js_1.tracer.startActiveSpan("onTurnComplete()", async () => {
@@ -4954,11 +5019,11 @@ function chatAgent(options) {
4954
5019
  ctx,
4955
5020
  chatId: currentWirePayload.chatId,
4956
5021
  messages: accumulatedMessages,
4957
- uiMessages: erroredUIMessages,
4958
- newMessages: [],
4959
- newUIMessages: erroredWireMessage ? [erroredWireMessage] : [],
4960
- responseMessage: undefined,
4961
- rawResponseMessage: undefined,
5022
+ uiMessages: erroredUIMessagesWithPartial,
5023
+ newMessages: erroredNewModelMessages,
5024
+ newUIMessages: erroredNewUIMessages,
5025
+ responseMessage: partialResponse,
5026
+ rawResponseMessage: partialResponse,
4962
5027
  turn,
4963
5028
  runId: ctx.run.id,
4964
5029
  chatAccessToken: "",
@@ -5002,7 +5067,7 @@ function chatAgent(options) {
5002
5067
  await writeChatSnapshot(sessionIdForSnapshot, {
5003
5068
  version: 1,
5004
5069
  savedAt: Date.now(),
5005
- messages: erroredUIMessages,
5070
+ messages: erroredUIMessagesWithPartial,
5006
5071
  lastOutEventId: errorTurnCompleteResult?.lastEventId,
5007
5072
  lastInEventId: errorSnapshotInCursor !== undefined ? String(errorSnapshotInCursor) : undefined,
5008
5073
  });
@@ -5646,28 +5711,21 @@ async function chatWriteTurnComplete(options) {
5646
5711
  * can return, and propagates a source error to the consumer after buffering
5647
5712
  * whatever streamed first. See {@link pipeChatAndCapture} for why.
5648
5713
  */
5649
- async function* tapUIMessageChunks(source, buffer) {
5714
+ function tapUIMessageChunks(source, buffer) {
5650
5715
  if (isReadableStream(source)) {
5651
- const reader = source.getReader();
5652
- try {
5653
- while (true) {
5654
- const { done, value } = await reader.read();
5655
- if (done)
5656
- break;
5657
- buffer.push(value);
5658
- yield value;
5659
- }
5660
- }
5661
- finally {
5662
- reader.releaseLock();
5663
- }
5716
+ return source.pipeThrough(new TransformStream({
5717
+ transform(chunk, controller) {
5718
+ buffer.push(chunk);
5719
+ controller.enqueue(chunk);
5720
+ },
5721
+ }));
5664
5722
  }
5665
- else {
5723
+ return (async function* () {
5666
5724
  for await (const chunk of source) {
5667
5725
  buffer.push(chunk);
5668
5726
  yield chunk;
5669
5727
  }
5670
- }
5728
+ })();
5671
5729
  }
5672
5730
  /**
5673
5731
  * Reconstruct a partial assistant `UIMessage` from the raw chunks that
@@ -6318,6 +6376,15 @@ function createChatSession(payload, options) {
6318
6376
  // Surface a genuine stream failure to the caller. A user stop
6319
6377
  // (status "aborted") falls through so the partial is accumulated.
6320
6378
  if (captured.status === "error") {
6379
+ if (captured.message) {
6380
+ const partial = cleanupAbortedParts(captured.message);
6381
+ const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
6382
+ if (queuedParts && queuedParts.length > 0) {
6383
+ partial.parts = [...(partial.parts ?? []), ...queuedParts];
6384
+ locals_js_1.locals.set(chatResponsePartsKey, []);
6385
+ }
6386
+ await accumulator.addResponse(partial);
6387
+ }
6321
6388
  throw captured.error;
6322
6389
  }
6323
6390
  response = captured.message;
@@ -6862,24 +6929,49 @@ async function mintPublicTokenWithOverride(args) {
6862
6929
  throw new Error("chat.createStartSessionAction: no API access token configured for JWT mint.");
6863
6930
  }
6864
6931
  const ctx = { endpoint: "auth", chatId: args.chatId };
6865
- const url = `${resolveChatStartBaseURL("auth", args.chatId, args.baseURLOption)}/api/v1/auth/jwt/claims`;
6932
+ const scopes = [`read:sessions:${args.chatId}`, `write:sessions:${args.chatId}`];
6933
+ const serverMint = (0, v3_1.isAdditionalApiKey)(accessToken);
6934
+ const endpoint = serverMint ? "/api/v1/auth/public-tokens" : "/api/v1/auth/jwt/claims";
6935
+ const url = `${resolveChatStartBaseURL("auth", args.chatId, args.baseURLOption)}${endpoint}`;
6866
6936
  const init = {
6867
6937
  method: "POST",
6868
6938
  headers: overrideRequestHeaders(accessToken),
6939
+ ...(serverMint
6940
+ ? {
6941
+ body: JSON.stringify({
6942
+ scopes,
6943
+ expirationTime: args.expirationTime instanceof Date
6944
+ ? Math.floor(args.expirationTime.getTime() / 1000)
6945
+ : args.expirationTime,
6946
+ }),
6947
+ }
6948
+ : {}),
6869
6949
  };
6870
6950
  const response = args.fetchOverride
6871
6951
  ? await args.fetchOverride(url, init, ctx)
6872
6952
  : await fetch(url, init);
6873
6953
  if (!response.ok) {
6954
+ // An additional API key cannot self-sign, so it must use the server mint
6955
+ // endpoint. On a server too old to expose it, explain the recovery path
6956
+ // instead of surfacing a bare 404 (mirrors auth.createServerPublicToken).
6957
+ if (serverMint && response.status === 404) {
6958
+ throw new Error("This additional API key cannot self-sign public tokens, and the server does not support public-token minting. Upgrade the server or use the root API key.");
6959
+ }
6874
6960
  const text = await response.text().catch(() => "");
6875
6961
  throw new Error(`auth.createPublicToken failed: ${response.status} ${text}`);
6876
6962
  }
6877
- const claims = (await response.json());
6963
+ const responseBody = (await response.json());
6964
+ if (serverMint) {
6965
+ if (typeof responseBody.token !== "string") {
6966
+ throw new Error("auth.createPublicToken failed: server response did not include a token");
6967
+ }
6968
+ return responseBody.token;
6969
+ }
6878
6970
  return (0, v3_1.generateJWT)({
6879
6971
  secretKey: accessToken,
6880
6972
  payload: {
6881
- ...claims,
6882
- scopes: [`read:sessions:${args.chatId}`, `write:sessions:${args.chatId}`],
6973
+ ...responseBody,
6974
+ scopes,
6883
6975
  },
6884
6976
  expirationTime: args.expirationTime,
6885
6977
  });