@ai-sdk/harness 1.0.71 → 1.0.73

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.
@@ -250,6 +250,7 @@ var HarnessStreamTextResult = class {
250
250
  this.toolsContext = options.toolsContext;
251
251
  this.providerName = `harness:${options.harnessId}`;
252
252
  this.modelId = options.sessionId;
253
+ this.outputSpecification = options.output;
253
254
  let controllerRef;
254
255
  const baseStream = new ReadableStream({
255
256
  start(c) {
@@ -258,18 +259,13 @@ var HarnessStreamTextResult = class {
258
259
  }
259
260
  });
260
261
  this.fullStreamController = controllerRef;
261
- const [forFull, forText] = baseStream.tee();
262
- this.stream = forFull;
263
- this.fullStream = this.stream;
264
- this.textStream = forText.pipeThrough(
262
+ this.baseStream = options.output == null ? baseStream.pipeThrough(
265
263
  new TransformStream({
266
264
  transform(part, controller) {
267
- if (part.type === "text-delta") {
268
- controller.enqueue(part.text);
269
- }
265
+ controller.enqueue({ part, partialOutput: void 0 });
270
266
  }
271
267
  })
272
- );
268
+ ) : baseStream.pipeThrough(createOutputTransformStream(options.output));
273
269
  }
274
270
  // ─── Writer-side methods used by the driver ────────────────────────
275
271
  /**
@@ -525,6 +521,38 @@ var HarnessStreamTextResult = class {
525
521
  }
526
522
  }
527
523
  // ─── Reader-side public surface (StreamTextResult contract) ────────
524
+ teeStream() {
525
+ const [stream, remainingStream] = this.baseStream.tee();
526
+ this.baseStream = remainingStream;
527
+ return stream;
528
+ }
529
+ get stream() {
530
+ return createAsyncIterableStream(
531
+ this.teeStream().pipeThrough(
532
+ new TransformStream({
533
+ transform({ part }, controller) {
534
+ controller.enqueue(part);
535
+ }
536
+ })
537
+ )
538
+ );
539
+ }
540
+ get fullStream() {
541
+ return this.stream;
542
+ }
543
+ get textStream() {
544
+ return createAsyncIterableStream(
545
+ this.stream.pipeThrough(
546
+ new TransformStream({
547
+ transform(part, controller) {
548
+ if (part.type === "text-delta") {
549
+ controller.enqueue(part.text);
550
+ }
551
+ }
552
+ })
553
+ )
554
+ );
555
+ }
528
556
  get content() {
529
557
  return this._content.promise;
530
558
  }
@@ -594,18 +622,48 @@ var HarnessStreamTextResult = class {
594
622
  get providerMetadata() {
595
623
  return this._providerMetadata.promise;
596
624
  }
597
- // Output-specification surfaces are not yet supported.
598
625
  get experimental_partialOutputStream() {
599
- throw notSupportedYet("partial output stream");
626
+ return this.partialOutputStream;
600
627
  }
601
628
  get partialOutputStream() {
602
- throw notSupportedYet("partial output stream");
629
+ return createAsyncIterableStream(
630
+ this.teeStream().pipeThrough(
631
+ new TransformStream({
632
+ transform({ partialOutput }, controller) {
633
+ if (partialOutput != null) {
634
+ controller.enqueue(partialOutput);
635
+ }
636
+ }
637
+ })
638
+ )
639
+ );
603
640
  }
604
641
  get elementStream() {
605
- throw notSupportedYet("element stream");
642
+ var _a4, _b4, _c;
643
+ const transform = (_a4 = this.outputSpecification) == null ? void 0 : _a4.createElementStreamTransform();
644
+ if (transform == null) {
645
+ throw notSupportedYet(
646
+ `element streams in ${(_c = (_b4 = this.outputSpecification) == null ? void 0 : _b4.name) != null ? _c : "text"} mode`
647
+ );
648
+ }
649
+ return createAsyncIterableStream(
650
+ this.teeStream().pipeThrough(transform)
651
+ );
606
652
  }
607
653
  get output() {
608
- throw notSupportedYet("structured output");
654
+ return this.finalStep.then((step) => {
655
+ if (this.outputSpecification == null) {
656
+ throw notSupportedYet("structured output");
657
+ }
658
+ return this.outputSpecification.parseCompleteOutput(
659
+ { text: step.text },
660
+ {
661
+ response: step.response,
662
+ usage: step.usage,
663
+ finishReason: step.finishReason
664
+ }
665
+ );
666
+ });
609
667
  }
610
668
  async consumeStream() {
611
669
  const reader = this.fullStream.getReader();
@@ -725,6 +783,73 @@ var HarnessStreamTextResult = class {
725
783
  }
726
784
  }
727
785
  };
786
+ function createOutputTransformStream(output) {
787
+ let firstTextChunkId;
788
+ let text = "";
789
+ let textChunk = "";
790
+ let textProviderMetadata;
791
+ let lastPublishedValue = "";
792
+ const publishTextChunk = (options) => {
793
+ options.controller.enqueue({
794
+ part: {
795
+ type: "text-delta",
796
+ id: firstTextChunkId,
797
+ text: textChunk,
798
+ providerMetadata: textProviderMetadata
799
+ },
800
+ partialOutput: options.partialOutput
801
+ });
802
+ textChunk = "";
803
+ };
804
+ return new TransformStream({
805
+ async transform(chunk, controller) {
806
+ var _a4;
807
+ if (chunk.type === "finish-step" && textChunk.length > 0) {
808
+ publishTextChunk({ controller });
809
+ }
810
+ if (chunk.type !== "text-delta" && chunk.type !== "text-start" && chunk.type !== "text-end") {
811
+ controller.enqueue({ part: chunk, partialOutput: void 0 });
812
+ return;
813
+ }
814
+ if (firstTextChunkId == null) {
815
+ firstTextChunkId = chunk.id;
816
+ } else if (chunk.id !== firstTextChunkId) {
817
+ controller.enqueue({ part: chunk, partialOutput: void 0 });
818
+ return;
819
+ }
820
+ if (chunk.type === "text-start") {
821
+ controller.enqueue({ part: chunk, partialOutput: void 0 });
822
+ return;
823
+ }
824
+ if (chunk.type === "text-end") {
825
+ if (textChunk.length > 0) {
826
+ publishTextChunk({ controller });
827
+ }
828
+ controller.enqueue({ part: chunk, partialOutput: void 0 });
829
+ return;
830
+ }
831
+ text += chunk.text;
832
+ textChunk += chunk.text;
833
+ textProviderMetadata = (_a4 = chunk.providerMetadata) != null ? _a4 : textProviderMetadata;
834
+ if (chunk.text.length === 0 && chunk.providerMetadata != null) {
835
+ controller.enqueue({ part: chunk, partialOutput: void 0 });
836
+ return;
837
+ }
838
+ const result = await output.parsePartialOutput({ text });
839
+ if (result === void 0) {
840
+ return;
841
+ }
842
+ const currentValue = typeof result.partial === "string" ? result.partial : JSON.stringify(result.partial);
843
+ if (currentValue !== lastPublishedValue) {
844
+ publishTextChunk({
845
+ controller,
846
+ partialOutput: result.partial
847
+ });
848
+ lastPublishedValue = currentValue;
849
+ }
850
+ }
851
+ });
852
+ }
728
853
  function createEmptyPerformance() {
729
854
  return {
730
855
  effectiveOutputTokensPerSecond: 0,
@@ -1372,7 +1497,8 @@ function runPrompt(input) {
1372
1497
  // toolsContext is not configurable for harnesses; pass undefined cast.
1373
1498
  toolsContext: void 0,
1374
1499
  harnessId: input.harness.harnessId,
1375
- sessionId: input.session.sessionId
1500
+ sessionId: input.session.sessionId,
1501
+ output: input.output
1376
1502
  });
1377
1503
  const pendingToolApprovals = (_a4 = input.pendingToolApprovals) != null ? _a4 : [];
1378
1504
  const pendingToolResults = (_b4 = input.pendingToolResults) != null ? _b4 : [];
@@ -1413,6 +1539,7 @@ function runPrompt(input) {
1413
1539
  try {
1414
1540
  bridge = await toHarnessStream({
1415
1541
  invoke: input.mode === "continue" ? (emit) => input.session.doContinueTurn({
1542
+ responseFormat: input.responseFormat,
1416
1543
  tools: input.toolSpecs,
1417
1544
  instructions: input.instructions,
1418
1545
  abortSignal: input.abortSignal,
@@ -1425,6 +1552,7 @@ function runPrompt(input) {
1425
1552
  }
1426
1553
  return input.session.doPromptTurn({
1427
1554
  prompt: input.prompt,
1555
+ responseFormat: input.responseFormat,
1428
1556
  tools: input.toolSpecs,
1429
1557
  instructions: input.instructions,
1430
1558
  abortSignal: input.abortSignal,
@@ -2273,6 +2401,8 @@ var HarnessAgentSession = class {
2273
2401
  sessionWorkDir: this.sessionWorkDir,
2274
2402
  runtimeContext: options.runtimeContext,
2275
2403
  abortSignal: options.abortSignal,
2404
+ responseFormat: options.responseFormat,
2405
+ output: options.output,
2276
2406
  telemetry: options.telemetry,
2277
2407
  stopConditions: options.stopConditions,
2278
2408
  toolApproval: this.toolApproval,
@@ -2326,6 +2456,8 @@ var HarnessAgentSession = class {
2326
2456
  sessionWorkDir: this.sessionWorkDir,
2327
2457
  runtimeContext: options.runtimeContext,
2328
2458
  abortSignal: options.abortSignal,
2459
+ responseFormat: options.responseFormat,
2460
+ output: options.output,
2329
2461
  telemetry: options.telemetry,
2330
2462
  stopConditions: options.stopConditions,
2331
2463
  toolApproval: this.toolApproval,
@@ -3342,11 +3474,13 @@ var HarnessAgent = class {
3342
3474
  async generate(options) {
3343
3475
  const turnInput = this._resolveTurnInput(options);
3344
3476
  const runtimeContext = {};
3477
+ const responseFormat = await this._resolveResponseFormat();
3345
3478
  const { result, done } = this._startTurn({
3346
3479
  session: options.session,
3347
3480
  turnInput,
3348
3481
  runtimeContext,
3349
- abortSignal: options.abortSignal
3482
+ abortSignal: options.abortSignal,
3483
+ responseFormat
3350
3484
  });
3351
3485
  await done;
3352
3486
  return this._toGenerateResult(result);
@@ -3354,11 +3488,13 @@ var HarnessAgent = class {
3354
3488
  async stream(options) {
3355
3489
  const turnInput = this._resolveTurnInput(options);
3356
3490
  const runtimeContext = {};
3491
+ const responseFormat = await this._resolveResponseFormat();
3357
3492
  const { result } = this._startTurn({
3358
3493
  session: options.session,
3359
3494
  turnInput,
3360
3495
  runtimeContext,
3361
- abortSignal: options.abortSignal
3496
+ abortSignal: options.abortSignal,
3497
+ responseFormat
3362
3498
  });
3363
3499
  return result;
3364
3500
  }
@@ -3370,6 +3506,7 @@ var HarnessAgent = class {
3370
3506
  async continueGenerate(options) {
3371
3507
  var _a4, _b4;
3372
3508
  const runtimeContext = {};
3509
+ const responseFormat = await this._resolveResponseFormat();
3373
3510
  const { result, done } = this._startTurn({
3374
3511
  session: options.session,
3375
3512
  turnInput: {
@@ -3378,7 +3515,8 @@ var HarnessAgent = class {
3378
3515
  toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
3379
3516
  },
3380
3517
  runtimeContext,
3381
- abortSignal: options.abortSignal
3518
+ abortSignal: options.abortSignal,
3519
+ responseFormat
3382
3520
  });
3383
3521
  await done;
3384
3522
  return this._toGenerateResult(result);
@@ -3394,6 +3532,7 @@ var HarnessAgent = class {
3394
3532
  async continueStream(options) {
3395
3533
  var _a4, _b4;
3396
3534
  const runtimeContext = {};
3535
+ const responseFormat = await this._resolveResponseFormat();
3397
3536
  const { result } = this._startTurn({
3398
3537
  session: options.session,
3399
3538
  turnInput: {
@@ -3402,7 +3541,8 @@ var HarnessAgent = class {
3402
3541
  toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
3403
3542
  },
3404
3543
  runtimeContext,
3405
- abortSignal: options.abortSignal
3544
+ abortSignal: options.abortSignal,
3545
+ responseFormat
3406
3546
  });
3407
3547
  return result;
3408
3548
  }
@@ -3417,6 +3557,8 @@ var HarnessAgent = class {
3417
3557
  builtinToolFiltering: this.builtinToolFiltering,
3418
3558
  runtimeContext: input.runtimeContext,
3419
3559
  abortSignal: input.abortSignal,
3560
+ responseFormat: input.responseFormat,
3561
+ output: this.settings.output,
3420
3562
  telemetry: this.settings.telemetry,
3421
3563
  stopConditions: this.stopConditions,
3422
3564
  toolApprovalContinuations: input.turnInput.toolApprovalContinuations,
@@ -3432,6 +3574,8 @@ var HarnessAgent = class {
3432
3574
  builtinToolFiltering: this.builtinToolFiltering,
3433
3575
  runtimeContext: input.runtimeContext,
3434
3576
  abortSignal: input.abortSignal,
3577
+ responseFormat: input.responseFormat,
3578
+ output: this.settings.output,
3435
3579
  telemetry: this.settings.telemetry,
3436
3580
  stopConditions: this.stopConditions
3437
3581
  });
@@ -3516,20 +3660,34 @@ var HarnessAgent = class {
3516
3660
  return specs;
3517
3661
  }
3518
3662
  async _toGenerateResult(streamResult) {
3519
- const [steps, usage, responseMessages] = await Promise.all([
3663
+ const [steps, usage, responseMessages, output] = await Promise.all([
3520
3664
  streamResult.steps,
3521
3665
  streamResult.usage,
3522
- streamResult.responseMessages
3666
+ streamResult.responseMessages,
3667
+ this.settings.output == null ? Promise.resolve(void 0) : streamResult.output
3523
3668
  ]);
3524
- return new HarnessGenerateTextResult({ steps, usage, responseMessages });
3669
+ return new HarnessGenerateTextResult({ steps, usage, responseMessages, output });
3670
+ }
3671
+ async _resolveResponseFormat() {
3672
+ var _a4;
3673
+ const responseFormat = await ((_a4 = this.settings.output) == null ? void 0 : _a4.responseFormat);
3674
+ if (responseFormat == null || responseFormat.type === "text") {
3675
+ return responseFormat == null ? void 0 : { type: "text" };
3676
+ }
3677
+ return {
3678
+ type: "json",
3679
+ ...responseFormat.schema == null ? {} : { schema: responseFormat.schema },
3680
+ ...responseFormat.name == null ? {} : { name: responseFormat.name },
3681
+ ...responseFormat.description == null ? {} : { description: responseFormat.description }
3682
+ };
3525
3683
  }
3526
3684
  };
3527
3685
  var HarnessGenerateTextResult = class {
3528
3686
  constructor(options) {
3529
- this.output = void 0;
3530
3687
  this.steps = options.steps;
3531
3688
  this.usage = options.usage;
3532
3689
  this.responseMessages = options.responseMessages;
3690
+ this.output = options.output;
3533
3691
  }
3534
3692
  get finalStep() {
3535
3693
  return this.steps.at(-1);
@@ -3691,10 +3849,11 @@ async function prepareSandboxForHarness(options) {
3691
3849
  "prepareSandboxForHarness: at least one harness must be provided."
3692
3850
  );
3693
3851
  }
3694
- const harnesses = [...options.harnesses].sort(
3695
- (a, b) => a.harnessId.localeCompare(b.harnessId)
3696
- );
3697
- assertUniqueHarnessIds(harnesses);
3852
+ const harnesses = [
3853
+ ...new Map(
3854
+ options.harnesses.map((harness) => [harness.harnessId, harness])
3855
+ ).values()
3856
+ ].sort((a, b) => a.harnessId.localeCompare(b.harnessId));
3698
3857
  const workDir = sandboxConfig.workDir == null ? void 0 : normalizeSandboxWorkDir(sandboxConfig.workDir);
3699
3858
  const recipeIdentities = {};
3700
3859
  const skippedHarnessIds = [];
@@ -3741,17 +3900,6 @@ async function prepareSandboxForHarness(options) {
3741
3900
  skippedHarnessIds
3742
3901
  };
3743
3902
  }
3744
- function assertUniqueHarnessIds(harnesses) {
3745
- const seen = /* @__PURE__ */ new Set();
3746
- for (const harness of harnesses) {
3747
- if (seen.has(harness.harnessId)) {
3748
- throw new Error(
3749
- `prepareSandboxForHarness: duplicate harness id "${harness.harnessId}".`
3750
- );
3751
- }
3752
- seen.add(harness.harnessId);
3753
- }
3754
- }
3755
3903
  async function resolvePreparedSandboxIdentity({
3756
3904
  recipeIdentities,
3757
3905
  bootstrapHash,