@ai-sdk/harness 1.0.138 → 1.0.140

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness",
3
- "version": "1.0.138",
3
+ "version": "1.0.140",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -44,9 +44,9 @@
44
44
  }
45
45
  },
46
46
  "dependencies": {
47
- "@ai-sdk/provider": "4.0.21",
48
- "@ai-sdk/provider-utils": "5.0.53",
49
- "ai": "7.0.127"
47
+ "@ai-sdk/provider": "4.0.23",
48
+ "@ai-sdk/provider-utils": "5.0.55",
49
+ "ai": "7.0.129"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "ws": "^8.21.0",
@@ -58,13 +58,13 @@
58
58
  }
59
59
  },
60
60
  "devDependencies": {
61
- "@ai-sdk/otel": "1.0.127",
61
+ "@ai-sdk/otel": "1.0.129",
62
62
  "@opentelemetry/sdk-trace-base": "2.7.1",
63
63
  "@types/node": "22.19.19",
64
64
  "@types/ws": "^8.5.13",
65
65
  "@vercel/ai-tsconfig": "0.0.0",
66
66
  "tsdown": "^0.23.0",
67
- "typescript": "5.8.3",
67
+ "typescript": "6.0.3",
68
68
  "ws": "^8.21.0",
69
69
  "zod": "3.25.76"
70
70
  },
@@ -36,6 +36,7 @@ import {
36
36
  type UIMessage,
37
37
  type UIMessageStreamOptions,
38
38
  } from 'ai';
39
+ import type { HarnessV1CallWarning } from '../../v1';
39
40
 
40
41
  type StreamProp<
41
42
  TOOLS extends ToolSet,
@@ -233,6 +234,10 @@ export class HarnessStreamTextResult<
233
234
  }
234
235
  }
235
236
 
237
+ addWarnings(warnings: ReadonlyArray<HarnessV1CallWarning>): void {
238
+ this.currentStepWarnings.push(...warnings);
239
+ }
240
+
236
241
  /**
237
242
  * Push a continuation input into the consumer stream without attributing it
238
243
  * to the next model step. Approval responses and client tool results arrive
@@ -262,7 +267,6 @@ export class HarnessStreamTextResult<
262
267
  finishReason: LanguageModelV4FinishReason;
263
268
  usage: LanguageModelV4Usage;
264
269
  providerMetadata: ProviderMetadata | undefined;
265
- warnings: CallWarning[];
266
270
  }): StepResult<TOOLS, RUNTIME_CONTEXT> {
267
271
  this.startStep();
268
272
 
@@ -282,7 +286,10 @@ export class HarnessStreamTextResult<
282
286
  rawFinishReason,
283
287
  usage: normalizedUsage,
284
288
  performance: createEmptyPerformance(),
285
- warnings: input.warnings.length > 0 ? input.warnings : undefined,
289
+ warnings:
290
+ this.currentStepWarnings.length > 0
291
+ ? this.currentStepWarnings
292
+ : undefined,
286
293
  request: {},
287
294
  response: {
288
295
  id: `${this.callId}-${this.stepNumber}`,
@@ -313,8 +320,9 @@ export class HarnessStreamTextResult<
313
320
  this.finalFinishReason = finishReason;
314
321
  this.finalRawFinishReason = rawFinishReason;
315
322
  this.finalProviderMetadata = input.providerMetadata;
316
- if (input.warnings.length > 0)
317
- this.aggregateWarnings.push(...input.warnings);
323
+ if (this.currentStepWarnings.length > 0) {
324
+ this.aggregateWarnings.push(...this.currentStepWarnings);
325
+ }
318
326
 
319
327
  this.stepNumber += 1;
320
328
  this.currentStepContent = [];
@@ -234,6 +234,14 @@ export function runPrompt<
234
234
  result.fail(err);
235
235
  };
236
236
 
237
+ const settleLifecycle = async (err: unknown): Promise<void> => {
238
+ if (input.abortSignal?.aborted) {
239
+ await lifecycle.abort(input.abortSignal.reason);
240
+ } else {
241
+ await lifecycle.error(err);
242
+ }
243
+ };
244
+
237
245
  const done = (async () => {
238
246
  let bridge: Awaited<ReturnType<typeof toHarnessStream>>;
239
247
  try {
@@ -269,7 +277,7 @@ export function runPrompt<
269
277
  },
270
278
  });
271
279
  } catch (err) {
272
- await lifecycle.error(err);
280
+ await settleLifecycle(err);
273
281
  logBridgeError({
274
282
  harnessId: input.harness.harnessId,
275
283
  sessionId: input.session.sessionId,
@@ -469,7 +477,6 @@ export function runPrompt<
469
477
  finishReason: input.finishReason,
470
478
  usage: input.usage,
471
479
  providerMetadata: input.providerMetadata,
472
- warnings: [],
473
480
  });
474
481
  completedSteps.push(step);
475
482
  await lifecycle.stepEnd(step);
@@ -890,6 +897,7 @@ export function runPrompt<
890
897
  if (value.type === 'stream-start') {
891
898
  const modelId = value.modelId ?? input.model;
892
899
  if (modelId != null) result.setModelId(modelId);
900
+ result.addWarnings(value.warnings ?? []);
893
901
  await lifecycle.start(modelId);
894
902
  }
895
903
 
@@ -998,7 +1006,7 @@ export function runPrompt<
998
1006
  // Telemetry and stderr diagnostics keep the raw error (absolute
999
1007
  // paths help debugging); the consumer-facing settle uses the
1000
1008
  // workDir-stripped one, like every other forwarded part.
1001
- await lifecycle.error(value.error);
1009
+ await settleLifecycle(value.error);
1002
1010
  // A turn the caller itself aborted ends with an error-shaped part by
1003
1011
  // construction; diagnosing the caller's own signal to stderr reads
1004
1012
  // as a malfunction. `settleFailure` below still reports it as an
@@ -1477,7 +1485,7 @@ export function runPrompt<
1477
1485
  } catch {
1478
1486
  // Preserve the error that stopped the reader loop.
1479
1487
  }
1480
- await lifecycle.error(err);
1488
+ await settleLifecycle(err);
1481
1489
  logBridgeError({
1482
1490
  harnessId: input.harness.harnessId,
1483
1491
  sessionId: input.session.sessionId,
@@ -71,6 +71,7 @@ export interface TurnLifecycle<
71
71
  toolCallId: string;
72
72
  execute: () => PromiseLike<T>;
73
73
  }): Promise<T>;
74
+ abort(reason?: unknown): Promise<void>;
74
75
  error(error: unknown): Promise<void>;
75
76
  }
76
77
 
@@ -358,11 +359,22 @@ export function createTurnLifecycle<
358
359
  });
359
360
  },
360
361
 
362
+ async abort(reason) {
363
+ if (ended) return;
364
+ if (!started) await start();
365
+ ended = true;
366
+ await telemetry.onAbort?.({
367
+ callId: options.callId,
368
+ steps: [...completedSteps],
369
+ ...(reason !== undefined ? { reason } : {}),
370
+ });
371
+ },
372
+
361
373
  async error(error) {
362
374
  if (ended) return;
363
375
  if (!started) await start();
364
376
  ended = true;
365
- await telemetry.onError?.(error);
377
+ await telemetry.onError?.({ callId: options.callId, error });
366
378
  },
367
379
  };
368
380
  }
@@ -30,6 +30,12 @@ type Record_ = { ts: number } & Record<string, unknown>;
30
30
 
31
31
  export type FileReporter = Telemetry & HarnessDiagnosticConsumer;
32
32
 
33
+ function serializeError(error: unknown): unknown {
34
+ return error instanceof Error
35
+ ? { name: error.name, message: error.message }
36
+ : error;
37
+ }
38
+
33
39
  export function createFileReporter(options: FileReporterOptions): FileReporter {
34
40
  const fileName = options.fileName ?? 'events.jsonl';
35
41
  const path = `${options.dir}/${fileName}`;
@@ -181,16 +187,25 @@ export function createFileReporter(options: FileReporterOptions): FileReporter {
181
187
  });
182
188
  finishTurn(e.callId);
183
189
  },
184
- onError(error) {
185
- if (lastOpenCallId != null) bucketFor(lastOpenCallId).errored = true;
186
- record(lastOpenCallId, {
190
+ onError(event) {
191
+ const { callId, error } = event as { callId: string; error: unknown };
192
+ bucketFor(callId).errored = true;
193
+ record(callId, {
187
194
  ts: Date.now(),
188
195
  kind: 'error',
189
- error:
190
- error instanceof Error
191
- ? { name: error.name, message: error.message }
192
- : error,
196
+ callId,
197
+ error: serializeError(error),
198
+ });
199
+ finishTurn(callId);
200
+ },
201
+ onAbort({ callId, reason }) {
202
+ record(callId, {
203
+ ts: Date.now(),
204
+ kind: 'turn-abort',
205
+ callId,
206
+ ...(reason !== undefined ? { reason: serializeError(reason) } : {}),
193
207
  });
208
+ finishTurn(callId);
194
209
  },
195
210
  ingestDiagnostic(diagnostic: HarnessDiagnostic) {
196
211
  if (diagnostic.level === 'error' && lastOpenCallId != null) {
@@ -145,7 +145,11 @@ export async function forwardBridgeProcessStream({
145
145
  tailLimit?: number;
146
146
  }): Promise<void> {
147
147
  try {
148
- const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
148
+ const reader = stream
149
+ .pipeThrough(
150
+ new TextDecoderStream() as ReadableWritablePair<string, Uint8Array>,
151
+ )
152
+ .getReader();
149
153
  const decoder = lineDecoder();
150
154
  while (true) {
151
155
  const { value, done } = await reader.read();
@@ -163,7 +167,11 @@ export async function drainBridgeProcessStream(
163
167
  stream: ReadableStream<Uint8Array>,
164
168
  ): Promise<void> {
165
169
  try {
166
- const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
170
+ const reader = stream
171
+ .pipeThrough(
172
+ new TextDecoderStream() as ReadableWritablePair<string, Uint8Array>,
173
+ )
174
+ .getReader();
167
175
  while (true) {
168
176
  const { done } = await reader.read();
169
177
  if (done) return;
@@ -79,7 +79,11 @@ export async function waitForBridgeReady({
79
79
  createTimeoutError,
80
80
  createExitError,
81
81
  }: WaitForBridgeReadyOptions): Promise<WaitForBridgeReadyResult> {
82
- const reader = proc.stdout.pipeThrough(new TextDecoderStream()).getReader();
82
+ const reader = proc.stdout
83
+ .pipeThrough(
84
+ new TextDecoderStream() as ReadableWritablePair<string, Uint8Array>,
85
+ )
86
+ .getReader();
83
87
  const decoder = lineDecoder();
84
88
  const stdoutTail: string[] = [];
85
89
  const deadline = Date.now() + timeoutMs;
@@ -2,8 +2,8 @@
2
2
  * Warning emitted by a harness adapter during a call.
3
3
  *
4
4
  * Surfaces non-fatal issues such as unsupported options or quirks of the
5
- * underlying agent runtime. Mirrors the shape of `SharedV4Warning` but lives
6
- * in the harness namespace.
5
+ * underlying agent runtime. These compatibility variants are also accepted by
6
+ * the AI SDK's `CallWarning` result type.
7
7
  */
8
8
  export type HarnessV1CallWarning =
9
9
  | {
@@ -20,3 +20,9 @@ export type HarnessV1CallWarning =
20
20
  type: 'other';
21
21
  message: string;
22
22
  };
23
+
24
+ declare module 'ai' {
25
+ interface CallWarningRegistry {
26
+ harnessV1: HarnessV1CallWarning;
27
+ }
28
+ }