@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/CHANGELOG.md +39 -0
- package/dist/agent/index.d.ts +7 -2
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +50 -15
- package/dist/agent/index.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/utils/index.d.ts +26 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +6 -6
- package/src/agent/internal/harness-stream-text-result.ts +12 -4
- package/src/agent/internal/run-prompt.ts +12 -4
- package/src/agent/internal/turn-telemetry.ts +13 -1
- package/src/agent/observability/file-reporter.ts +22 -7
- package/src/utils/bridge-diagnostics.ts +10 -2
- package/src/utils/bridge-ready.ts +5 -1
- package/src/v1/harness-v1-call-warning.ts +8 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
48
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
49
|
-
"ai": "7.0.
|
|
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.
|
|
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": "
|
|
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:
|
|
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 (
|
|
317
|
-
this.aggregateWarnings.push(...
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
6
|
-
*
|
|
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
|
+
}
|