@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.
package/dist/esm/v3/ai.js CHANGED
@@ -1,4 +1,4 @@
1
- import { accessoryAttributes, apiClientManager, controlSubtype, generateJWT, getSchemaParseFn, headerValue, InputStreamOncePromise, isSchemaZodEsque, logger, ManualWaitpointPromise, OutOfMemoryError, resourceCatalog, SemanticInternalAttributes, SESSION_IN_EVENT_ID_HEADER, sessionStreams, taskContext, TRIGGER_CONTROL_SUBTYPE, } from "@trigger.dev/core/v3";
1
+ import { accessoryAttributes, apiClientManager, controlSubtype, generateJWT, getSchemaParseFn, headerValue, InputStreamOncePromise, isAdditionalApiKey, isSchemaZodEsque, logger, ManualWaitpointPromise, OutOfMemoryError, resourceCatalog, SemanticInternalAttributes, SESSION_IN_EVENT_ID_HEADER, sessionStreams, taskContext, TRIGGER_CONTROL_SUBTYPE, } from "@trigger.dev/core/v3";
2
2
  // Runtime VALUES go through the ESM/CJS shim so the CJS build can `require`
3
3
  // ESM-only `ai@7` (see ../imports/ai-runtime.ts).
4
4
  import { trace } from "@opentelemetry/api";
@@ -3630,6 +3630,9 @@ function chatAgent(options) {
3630
3630
  // Declared here so the finally can detach it — a handler leaked past
3631
3631
  // its turn duplicates every mid-stream message into the shared buffer.
3632
3632
  let turnMsgSub;
3633
+ let capturedPartialResponse;
3634
+ let responseCommitted = false;
3635
+ const turnBufferedChunks = [];
3633
3636
  try {
3634
3637
  // Extract turn-level context before entering the span. Slim
3635
3638
  // wire: at most one delta message per record. `headStartMessages`
@@ -4307,11 +4310,12 @@ function chatAgent(options) {
4307
4310
  generateMessageId: resolvedOptions.generateMessageId ?? generateMessageId,
4308
4311
  onFinish: ({ responseMessage, finishReason, }) => {
4309
4312
  capturedResponseMessage = responseMessage;
4313
+ capturedPartialResponse = responseMessage;
4310
4314
  capturedFinishReason = finishReason;
4311
4315
  resolveOnFinish();
4312
4316
  },
4313
4317
  });
4314
- await pipeChat(uiStream, {
4318
+ await pipeChat(tapUIMessageChunks(uiStream, turnBufferedChunks), {
4315
4319
  signal: combinedSignal,
4316
4320
  spanName: "stream response",
4317
4321
  });
@@ -4505,6 +4509,11 @@ function chatAgent(options) {
4505
4509
  locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
4506
4510
  }
4507
4511
  }
4512
+ if (capturedResponseMessage) {
4513
+ responseCommitted = true;
4514
+ capturedPartialResponse = capturedResponseMessage;
4515
+ turnBufferedChunks.length = 0;
4516
+ }
4508
4517
  if (runSignal.aborted)
4509
4518
  return "exit";
4510
4519
  // Await deferred background work (e.g. DB writes from onTurnStart)
@@ -4695,6 +4704,7 @@ function chatAgent(options) {
4695
4704
  parts: [...(msg.parts ?? []), ...lateParts],
4696
4705
  };
4697
4706
  capturedResponseMessage = accumulatedUIMessages[idx];
4707
+ capturedPartialResponse = capturedResponseMessage;
4698
4708
  turnCompleteEvent.responseMessage = capturedResponseMessage;
4699
4709
  turnCompleteEvent.uiMessages = accumulatedUIMessages;
4700
4710
  }
@@ -4937,10 +4947,65 @@ function chatAgent(options) {
4937
4947
  !accumulatedUIMessages.some((m) => m.id === erroredWireMessage.id)
4938
4948
  ? [...accumulatedUIMessages, erroredWireMessage]
4939
4949
  : accumulatedUIMessages;
4940
- // Fire onTurnComplete on the error path too — the docs promise it
4941
- // runs "after every turn, successful or errored" so customers can
4942
- // mark the turn failed. `responseMessage` is undefined/partial and
4943
- // `error` carries the thrown value.
4950
+ let partialResponse = capturedPartialResponse ??
4951
+ (await assemblePartialFromChunks(turnBufferedChunks));
4952
+ if (partialResponse) {
4953
+ partialResponse = cleanupAbortedParts(partialResponse);
4954
+ }
4955
+ let partialIdx = partialResponse?.id
4956
+ ? erroredUIMessages.findIndex((m) => m.id === partialResponse.id)
4957
+ : -1;
4958
+ if (partialResponse && capturedPartialResponse === undefined && partialIdx !== -1) {
4959
+ partialResponse = undefined;
4960
+ partialIdx = -1;
4961
+ }
4962
+ if (partialResponse && !partialResponse.id) {
4963
+ partialResponse = { ...partialResponse, id: generateMessageId() };
4964
+ }
4965
+ if (partialResponse && !responseCommitted) {
4966
+ const queuedParts = locals.get(chatResponsePartsKey);
4967
+ if (queuedParts && queuedParts.length > 0) {
4968
+ partialResponse = {
4969
+ ...partialResponse,
4970
+ parts: [...partialResponse.parts, ...queuedParts],
4971
+ };
4972
+ locals.set(chatResponsePartsKey, []);
4973
+ }
4974
+ }
4975
+ const includePartial = partialResponse != null && !responseCommitted;
4976
+ let erroredUIMessagesWithPartial = !includePartial
4977
+ ? erroredUIMessages
4978
+ : partialIdx === -1
4979
+ ? [...erroredUIMessages, partialResponse]
4980
+ : erroredUIMessages.map((m, i) => i === partialIdx ? partialResponse : m);
4981
+ let erroredNewUIMessages = erroredWireMessage ? [erroredWireMessage] : [];
4982
+ if (includePartial) {
4983
+ erroredNewUIMessages.push(partialResponse);
4984
+ }
4985
+ let erroredNewModelMessages = [];
4986
+ if (!responseCommitted) {
4987
+ try {
4988
+ if (erroredNewUIMessages.length > 0) {
4989
+ erroredNewModelMessages = await toModelMessages(erroredNewUIMessages.map((m) => stripProviderMetadata(m)));
4990
+ }
4991
+ if (erroredUIMessagesWithPartial !== accumulatedUIMessages) {
4992
+ if (partialIdx === -1) {
4993
+ const appended = erroredUIMessagesWithPartial.slice(accumulatedUIMessages.length);
4994
+ accumulatedMessages.push(...(await toModelMessages(appended.map((m) => stripProviderMetadata(m)))));
4995
+ }
4996
+ else {
4997
+ accumulatedMessages = await toModelMessages(erroredUIMessagesWithPartial);
4998
+ }
4999
+ accumulatedUIMessages = erroredUIMessagesWithPartial;
5000
+ locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
5001
+ }
5002
+ }
5003
+ catch {
5004
+ erroredNewModelMessages = [];
5005
+ erroredUIMessagesWithPartial = erroredUIMessages;
5006
+ erroredNewUIMessages = erroredWireMessage ? [erroredWireMessage] : [];
5007
+ }
5008
+ }
4944
5009
  if (onTurnComplete) {
4945
5010
  try {
4946
5011
  await tracer.startActiveSpan("onTurnComplete()", async () => {
@@ -4948,11 +5013,11 @@ function chatAgent(options) {
4948
5013
  ctx,
4949
5014
  chatId: currentWirePayload.chatId,
4950
5015
  messages: accumulatedMessages,
4951
- uiMessages: erroredUIMessages,
4952
- newMessages: [],
4953
- newUIMessages: erroredWireMessage ? [erroredWireMessage] : [],
4954
- responseMessage: undefined,
4955
- rawResponseMessage: undefined,
5016
+ uiMessages: erroredUIMessagesWithPartial,
5017
+ newMessages: erroredNewModelMessages,
5018
+ newUIMessages: erroredNewUIMessages,
5019
+ responseMessage: partialResponse,
5020
+ rawResponseMessage: partialResponse,
4956
5021
  turn,
4957
5022
  runId: ctx.run.id,
4958
5023
  chatAccessToken: "",
@@ -4996,7 +5061,7 @@ function chatAgent(options) {
4996
5061
  await writeChatSnapshot(sessionIdForSnapshot, {
4997
5062
  version: 1,
4998
5063
  savedAt: Date.now(),
4999
- messages: erroredUIMessages,
5064
+ messages: erroredUIMessagesWithPartial,
5000
5065
  lastOutEventId: errorTurnCompleteResult?.lastEventId,
5001
5066
  lastInEventId: errorSnapshotInCursor !== undefined ? String(errorSnapshotInCursor) : undefined,
5002
5067
  });
@@ -5640,28 +5705,21 @@ async function chatWriteTurnComplete(options) {
5640
5705
  * can return, and propagates a source error to the consumer after buffering
5641
5706
  * whatever streamed first. See {@link pipeChatAndCapture} for why.
5642
5707
  */
5643
- async function* tapUIMessageChunks(source, buffer) {
5708
+ function tapUIMessageChunks(source, buffer) {
5644
5709
  if (isReadableStream(source)) {
5645
- const reader = source.getReader();
5646
- try {
5647
- while (true) {
5648
- const { done, value } = await reader.read();
5649
- if (done)
5650
- break;
5651
- buffer.push(value);
5652
- yield value;
5653
- }
5654
- }
5655
- finally {
5656
- reader.releaseLock();
5657
- }
5710
+ return source.pipeThrough(new TransformStream({
5711
+ transform(chunk, controller) {
5712
+ buffer.push(chunk);
5713
+ controller.enqueue(chunk);
5714
+ },
5715
+ }));
5658
5716
  }
5659
- else {
5717
+ return (async function* () {
5660
5718
  for await (const chunk of source) {
5661
5719
  buffer.push(chunk);
5662
5720
  yield chunk;
5663
5721
  }
5664
- }
5722
+ })();
5665
5723
  }
5666
5724
  /**
5667
5725
  * Reconstruct a partial assistant `UIMessage` from the raw chunks that
@@ -6312,6 +6370,15 @@ function createChatSession(payload, options) {
6312
6370
  // Surface a genuine stream failure to the caller. A user stop
6313
6371
  // (status "aborted") falls through so the partial is accumulated.
6314
6372
  if (captured.status === "error") {
6373
+ if (captured.message) {
6374
+ const partial = cleanupAbortedParts(captured.message);
6375
+ const queuedParts = locals.get(chatResponsePartsKey);
6376
+ if (queuedParts && queuedParts.length > 0) {
6377
+ partial.parts = [...(partial.parts ?? []), ...queuedParts];
6378
+ locals.set(chatResponsePartsKey, []);
6379
+ }
6380
+ await accumulator.addResponse(partial);
6381
+ }
6315
6382
  throw captured.error;
6316
6383
  }
6317
6384
  response = captured.message;
@@ -6856,24 +6923,49 @@ async function mintPublicTokenWithOverride(args) {
6856
6923
  throw new Error("chat.createStartSessionAction: no API access token configured for JWT mint.");
6857
6924
  }
6858
6925
  const ctx = { endpoint: "auth", chatId: args.chatId };
6859
- const url = `${resolveChatStartBaseURL("auth", args.chatId, args.baseURLOption)}/api/v1/auth/jwt/claims`;
6926
+ const scopes = [`read:sessions:${args.chatId}`, `write:sessions:${args.chatId}`];
6927
+ const serverMint = isAdditionalApiKey(accessToken);
6928
+ const endpoint = serverMint ? "/api/v1/auth/public-tokens" : "/api/v1/auth/jwt/claims";
6929
+ const url = `${resolveChatStartBaseURL("auth", args.chatId, args.baseURLOption)}${endpoint}`;
6860
6930
  const init = {
6861
6931
  method: "POST",
6862
6932
  headers: overrideRequestHeaders(accessToken),
6933
+ ...(serverMint
6934
+ ? {
6935
+ body: JSON.stringify({
6936
+ scopes,
6937
+ expirationTime: args.expirationTime instanceof Date
6938
+ ? Math.floor(args.expirationTime.getTime() / 1000)
6939
+ : args.expirationTime,
6940
+ }),
6941
+ }
6942
+ : {}),
6863
6943
  };
6864
6944
  const response = args.fetchOverride
6865
6945
  ? await args.fetchOverride(url, init, ctx)
6866
6946
  : await fetch(url, init);
6867
6947
  if (!response.ok) {
6948
+ // An additional API key cannot self-sign, so it must use the server mint
6949
+ // endpoint. On a server too old to expose it, explain the recovery path
6950
+ // instead of surfacing a bare 404 (mirrors auth.createServerPublicToken).
6951
+ if (serverMint && response.status === 404) {
6952
+ 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.");
6953
+ }
6868
6954
  const text = await response.text().catch(() => "");
6869
6955
  throw new Error(`auth.createPublicToken failed: ${response.status} ${text}`);
6870
6956
  }
6871
- const claims = (await response.json());
6957
+ const responseBody = (await response.json());
6958
+ if (serverMint) {
6959
+ if (typeof responseBody.token !== "string") {
6960
+ throw new Error("auth.createPublicToken failed: server response did not include a token");
6961
+ }
6962
+ return responseBody.token;
6963
+ }
6872
6964
  return generateJWT({
6873
6965
  secretKey: accessToken,
6874
6966
  payload: {
6875
- ...claims,
6876
- scopes: [`read:sessions:${args.chatId}`, `write:sessions:${args.chatId}`],
6967
+ ...responseBody,
6968
+ scopes,
6877
6969
  },
6878
6970
  expirationTime: args.expirationTime,
6879
6971
  });