ai 6.0.199 → 6.0.200

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # ai
2
2
 
3
+ ## 6.0.200
4
+
5
+ ### Patch Changes
6
+
7
+ - 14098e7: fix(ai): reject `streamText` result promises with `NoOutputGeneratedError` when the model stream ends without producing any output. Previously such streams resolved with an empty step. Incomplete streams with partial output still resolve with the partial result.
8
+ - 2cabe9c: Harden UI message stream processing against prototype pollution from chunk IDs.
9
+
3
10
  ## 6.0.199
4
11
 
5
12
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1254,7 +1254,7 @@ function detectMediaType({
1254
1254
  var import_provider_utils3 = require("@ai-sdk/provider-utils");
1255
1255
 
1256
1256
  // src/version.ts
1257
- var VERSION = true ? "6.0.199" : "0.0.0-test";
1257
+ var VERSION = true ? "6.0.200" : "0.0.0-test";
1258
1258
 
1259
1259
  // src/util/download/download.ts
1260
1260
  var download = async ({
@@ -5405,6 +5405,11 @@ function isDataUIMessageChunk(chunk) {
5405
5405
  return chunk.type.startsWith("data-");
5406
5406
  }
5407
5407
 
5408
+ // src/util/create-id-map.ts
5409
+ function createIdMap() {
5410
+ return /* @__PURE__ */ Object.create(null);
5411
+ }
5412
+
5408
5413
  // src/ui/ui-messages.ts
5409
5414
  function isDataUIPart(part) {
5410
5415
  return part.type.startsWith("data-");
@@ -5448,9 +5453,9 @@ function createStreamingUIMessageState({
5448
5453
  role: "assistant",
5449
5454
  parts: []
5450
5455
  },
5451
- activeTextParts: {},
5452
- activeReasoningParts: {},
5453
- partialToolCalls: {}
5456
+ activeTextParts: createIdMap(),
5457
+ activeReasoningParts: createIdMap(),
5458
+ partialToolCalls: createIdMap()
5454
5459
  };
5455
5460
  }
5456
5461
  function processUIMessageStream({
@@ -5924,8 +5929,8 @@ function processUIMessageStream({
5924
5929
  break;
5925
5930
  }
5926
5931
  case "finish-step": {
5927
- state.activeTextParts = {};
5928
- state.activeReasoningParts = {};
5932
+ state.activeTextParts = createIdMap();
5933
+ state.activeReasoningParts = createIdMap();
5929
5934
  break;
5930
5935
  }
5931
5936
  case "start": {
@@ -6564,6 +6569,28 @@ var originalGenerateId2 = (0, import_provider_utils18.createIdGenerator)({
6564
6569
  prefix: "aitxt",
6565
6570
  size: 24
6566
6571
  });
6572
+ var isOutputChunkType = {
6573
+ file: true,
6574
+ source: true,
6575
+ "text-start": true,
6576
+ "text-end": true,
6577
+ "text-delta": true,
6578
+ "reasoning-start": true,
6579
+ "reasoning-end": true,
6580
+ "reasoning-delta": true,
6581
+ "tool-input-start": true,
6582
+ "tool-input-end": true,
6583
+ "tool-input-delta": true,
6584
+ "tool-approval-request": true,
6585
+ "tool-call": true,
6586
+ "tool-result": true,
6587
+ "tool-error": true,
6588
+ "stream-start": false,
6589
+ "response-metadata": false,
6590
+ finish: false,
6591
+ error: false,
6592
+ raw: false
6593
+ };
6567
6594
  function streamText({
6568
6595
  model,
6569
6596
  tools,
@@ -6782,6 +6809,7 @@ var DefaultStreamTextResult = class {
6782
6809
  let recordedRequest = {};
6783
6810
  let recordedWarnings = [];
6784
6811
  const recordedSteps = [];
6812
+ let recordedNoOutputError;
6785
6813
  const pendingDeferredToolCalls = /* @__PURE__ */ new Map();
6786
6814
  let rootSpan;
6787
6815
  let activeTextContent = {};
@@ -6795,7 +6823,11 @@ var DefaultStreamTextResult = class {
6795
6823
  await (onChunk == null ? void 0 : onChunk({ chunk: part }));
6796
6824
  }
6797
6825
  if (part.type === "error") {
6798
- await onError({ error: wrapGatewayError(part.error) });
6826
+ const error = wrapGatewayError(part.error);
6827
+ if (NoOutputGeneratedError.isInstance(error)) {
6828
+ recordedNoOutputError = error;
6829
+ }
6830
+ await onError({ error });
6799
6831
  }
6800
6832
  if (part.type === "text-start") {
6801
6833
  activeTextContent[part.id] = {
@@ -6946,8 +6978,8 @@ var DefaultStreamTextResult = class {
6946
6978
  async flush(controller) {
6947
6979
  var _a21, _b, _c, _d, _e, _f, _g;
6948
6980
  try {
6949
- if (recordedSteps.length === 0) {
6950
- const error = (abortSignal == null ? void 0 : abortSignal.aborted) ? abortSignal.reason : new NoOutputGeneratedError({
6981
+ if (recordedSteps.length === 0 || recordedNoOutputError != null) {
6982
+ const error = (abortSignal == null ? void 0 : abortSignal.aborted) ? abortSignal.reason : recordedNoOutputError != null ? recordedNoOutputError : new NoOutputGeneratedError({
6951
6983
  message: "No output generated. Check the stream for errors."
6952
6984
  });
6953
6985
  self._finishReason.reject(error);
@@ -7446,6 +7478,8 @@ var DefaultStreamTextResult = class {
7446
7478
  const activeToolCallToolNames = {};
7447
7479
  let stepFinishReason = "other";
7448
7480
  let stepRawFinishReason = void 0;
7481
+ let hasReceivedTerminalChunk = false;
7482
+ let hasReceivedOutputChunk = false;
7449
7483
  let stepUsage = createNullLanguageModelUsage();
7450
7484
  let stepProviderMetadata;
7451
7485
  let stepFirstChunk = true;
@@ -7481,6 +7515,9 @@ var DefaultStreamTextResult = class {
7481
7515
  });
7482
7516
  }
7483
7517
  const chunkType = chunk.type;
7518
+ if (isOutputChunkType[chunkType]) {
7519
+ hasReceivedOutputChunk = true;
7520
+ }
7484
7521
  switch (chunkType) {
7485
7522
  case "tool-approval-request":
7486
7523
  case "text-start":
@@ -7540,6 +7577,7 @@ var DefaultStreamTextResult = class {
7540
7577
  break;
7541
7578
  }
7542
7579
  case "finish": {
7580
+ hasReceivedTerminalChunk = true;
7543
7581
  stepUsage = chunk.usage;
7544
7582
  stepFinishReason = chunk.finishReason;
7545
7583
  stepRawFinishReason = chunk.rawFinishReason;
@@ -7599,6 +7637,7 @@ var DefaultStreamTextResult = class {
7599
7637
  break;
7600
7638
  }
7601
7639
  case "error": {
7640
+ hasReceivedTerminalChunk = true;
7602
7641
  controller.enqueue(chunk);
7603
7642
  stepFinishReason = "error";
7604
7643
  break;
@@ -7620,6 +7659,19 @@ var DefaultStreamTextResult = class {
7620
7659
  // invoke onFinish callback and resolve toolResults promise when the stream is about to close:
7621
7660
  async flush(controller) {
7622
7661
  var _a22, _b2, _c2, _d2, _e2, _f2, _g2;
7662
+ if (!hasReceivedTerminalChunk && !hasReceivedOutputChunk) {
7663
+ controller.enqueue({
7664
+ type: "error",
7665
+ error: new NoOutputGeneratedError({
7666
+ message: "No output generated. The model stream ended without a finish chunk."
7667
+ })
7668
+ });
7669
+ doStreamSpan.end();
7670
+ clearStepTimeout();
7671
+ clearChunkTimeout();
7672
+ self.closeStream();
7673
+ return;
7674
+ }
7623
7675
  const stepToolCallsJson = stepToolCalls.length > 0 ? JSON.stringify(stepToolCalls) : void 0;
7624
7676
  try {
7625
7677
  doStreamSpan.setAttributes(
@@ -7897,14 +7949,29 @@ var DefaultStreamTextResult = class {
7897
7949
  )
7898
7950
  );
7899
7951
  }
7952
+ rejectResultPromises(error) {
7953
+ if (this._finishReason.isPending())
7954
+ this._finishReason.reject(error);
7955
+ if (this._rawFinishReason.isPending())
7956
+ this._rawFinishReason.reject(error);
7957
+ if (this._totalUsage.isPending())
7958
+ this._totalUsage.reject(error);
7959
+ if (this._steps.isPending())
7960
+ this._steps.reject(error);
7961
+ }
7900
7962
  async consumeStream(options) {
7901
7963
  var _a21;
7902
7964
  try {
7903
7965
  await consumeStream({
7904
7966
  stream: this.fullStream,
7905
- onError: options == null ? void 0 : options.onError
7967
+ onError: (error) => {
7968
+ var _a22;
7969
+ this.rejectResultPromises(error);
7970
+ (_a22 = options == null ? void 0 : options.onError) == null ? void 0 : _a22.call(options, error);
7971
+ }
7906
7972
  });
7907
7973
  } catch (error) {
7974
+ this.rejectResultPromises(error);
7908
7975
  (_a21 = options == null ? void 0 : options.onError) == null ? void 0 : _a21.call(options, error);
7909
7976
  }
7910
7977
  }