@ai-sdk/harness 1.0.149 → 1.0.150
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 +11 -0
- package/dist/agent/index.js +91 -26
- package/dist/agent/index.js.map +1 -1
- package/package.json +3 -3
- package/src/agent/internal/harness-stream-text-result.ts +120 -27
- package/src/agent/internal/run-prompt.ts +7 -4
- package/src/agent/internal/translate-stream-part.ts +20 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.150",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@ai-sdk/provider": "4.0.27",
|
|
48
48
|
"@ai-sdk/provider-utils": "5.0.59",
|
|
49
|
-
"ai": "7.0.
|
|
49
|
+
"ai": "7.0.139"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"ws": "^8.21.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@ai-sdk/otel": "1.0.
|
|
61
|
+
"@ai-sdk/otel": "1.0.139",
|
|
62
62
|
"@opentelemetry/sdk-trace-base": "2.7.1",
|
|
63
63
|
"@types/node": "22.19.19",
|
|
64
64
|
"@types/ws": "^8.5.13",
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
createUIMessageStreamResponse,
|
|
23
23
|
toUIMessageStream as toUIMessageStreamHelper,
|
|
24
24
|
type CallWarning,
|
|
25
|
+
type Citation,
|
|
25
26
|
type ContentPart,
|
|
26
27
|
type FinishReason,
|
|
27
28
|
type InferUIMessageChunk,
|
|
@@ -53,6 +54,19 @@ type EnrichedStreamPart<TOOLS extends ToolSet, PARTIAL_OUTPUT> = {
|
|
|
53
54
|
partialOutput: PARTIAL_OUTPUT | undefined;
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
type AccumulatedTextContent = {
|
|
58
|
+
type: 'text';
|
|
59
|
+
text: string;
|
|
60
|
+
citations?: Citation[];
|
|
61
|
+
providerMetadata?: ProviderMetadata;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type AccumulatedReasoningContent = {
|
|
65
|
+
type: 'reasoning';
|
|
66
|
+
text: string;
|
|
67
|
+
providerMetadata?: ProviderMetadata;
|
|
68
|
+
};
|
|
69
|
+
|
|
56
70
|
/**
|
|
57
71
|
* Concrete `StreamTextResult` implementation backed by a single
|
|
58
72
|
* harness prompt turn.
|
|
@@ -149,6 +163,11 @@ export class HarnessStreamTextResult<
|
|
|
149
163
|
|
|
150
164
|
private readonly stepsBuffer: StepResult<TOOLS, RUNTIME_CONTEXT>[] = [];
|
|
151
165
|
private currentStepContent: ContentPart<TOOLS>[] = [];
|
|
166
|
+
private currentStepTextContent = new Map<string, AccumulatedTextContent>();
|
|
167
|
+
private currentStepReasoningContent = new Map<
|
|
168
|
+
string,
|
|
169
|
+
AccumulatedReasoningContent
|
|
170
|
+
>();
|
|
152
171
|
private currentStepWarnings: CallWarning[] = [];
|
|
153
172
|
private stepNumber = 0;
|
|
154
173
|
private stepStarted = false;
|
|
@@ -261,6 +280,8 @@ export class HarnessStreamTextResult<
|
|
|
261
280
|
*/
|
|
262
281
|
discardCurrentStepContent(): void {
|
|
263
282
|
this.currentStepContent = [];
|
|
283
|
+
this.currentStepTextContent.clear();
|
|
284
|
+
this.currentStepReasoningContent.clear();
|
|
264
285
|
this.currentStepWarnings = [];
|
|
265
286
|
this.stepStarted = false;
|
|
266
287
|
}
|
|
@@ -334,6 +355,8 @@ export class HarnessStreamTextResult<
|
|
|
334
355
|
|
|
335
356
|
this.stepNumber += 1;
|
|
336
357
|
this.currentStepContent = [];
|
|
358
|
+
this.currentStepTextContent.clear();
|
|
359
|
+
this.currentStepReasoningContent.clear();
|
|
337
360
|
this.currentStepWarnings = [];
|
|
338
361
|
this.stepStarted = false;
|
|
339
362
|
|
|
@@ -421,14 +444,12 @@ export class HarnessStreamTextResult<
|
|
|
421
444
|
>,
|
|
422
445
|
);
|
|
423
446
|
this._text.resolve(finalStep.text);
|
|
424
|
-
// Reasoning content parts are not yet derived from harness events; the
|
|
425
|
-
// foundation surfaces an empty array. Adapters that emit reasoning
|
|
426
|
-
// deltas can be wired up to produce real reasoning content in a later
|
|
427
|
-
// pass.
|
|
428
447
|
this._reasoning.resolve(
|
|
429
|
-
|
|
448
|
+
finalStep.content.filter(
|
|
449
|
+
part => part.type === 'reasoning' || part.type === 'reasoning-file',
|
|
450
|
+
) as StreamProp<TOOLS, RUNTIME_CONTEXT, OUTPUT, 'reasoning'>,
|
|
430
451
|
);
|
|
431
|
-
this._reasoningText.resolve(
|
|
452
|
+
this._reasoningText.resolve(finalStep.reasoningText);
|
|
432
453
|
this._files.resolve(this.stepsBuffer.flatMap(s => s.files));
|
|
433
454
|
this._sources.resolve(this.stepsBuffer.flatMap(s => s.sources));
|
|
434
455
|
this._toolCalls.resolve(
|
|
@@ -491,10 +512,16 @@ export class HarnessStreamTextResult<
|
|
|
491
512
|
this._response.resolve(finalStep.response);
|
|
492
513
|
this._providerMetadata.resolve(this.finalProviderMetadata);
|
|
493
514
|
|
|
494
|
-
const responseMessages
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
515
|
+
const responseMessages: Array<AssistantModelMessage | ToolModelMessage> =
|
|
516
|
+
[];
|
|
517
|
+
for (const step of this.stepsBuffer) {
|
|
518
|
+
responseMessages.push(
|
|
519
|
+
...(await toResponseMessages<TOOLS>({
|
|
520
|
+
content: step.content,
|
|
521
|
+
tools: this.tools,
|
|
522
|
+
})),
|
|
523
|
+
);
|
|
524
|
+
}
|
|
498
525
|
this._responseMessages.resolve(responseMessages);
|
|
499
526
|
|
|
500
527
|
// Forward AI SDK finish event before closing.
|
|
@@ -755,6 +782,8 @@ export class HarnessStreamTextResult<
|
|
|
755
782
|
toUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
756
783
|
originalMessages,
|
|
757
784
|
generateMessageId,
|
|
785
|
+
onStepEnd,
|
|
786
|
+
onStepFinish,
|
|
758
787
|
onEnd,
|
|
759
788
|
onFinish,
|
|
760
789
|
messageMetadata,
|
|
@@ -772,6 +801,8 @@ export class HarnessStreamTextResult<
|
|
|
772
801
|
tools: this.tools,
|
|
773
802
|
originalMessages,
|
|
774
803
|
generateMessageId,
|
|
804
|
+
onStepEnd,
|
|
805
|
+
onStepFinish,
|
|
775
806
|
onEnd,
|
|
776
807
|
onFinish,
|
|
777
808
|
messageMetadata,
|
|
@@ -795,6 +826,8 @@ export class HarnessStreamTextResult<
|
|
|
795
826
|
toUIMessageStreamResponse<UI_MESSAGE extends UIMessage>({
|
|
796
827
|
originalMessages,
|
|
797
828
|
generateMessageId,
|
|
829
|
+
onStepEnd,
|
|
830
|
+
onStepFinish,
|
|
798
831
|
onEnd,
|
|
799
832
|
onFinish,
|
|
800
833
|
messageMetadata,
|
|
@@ -813,6 +846,8 @@ export class HarnessStreamTextResult<
|
|
|
813
846
|
stream: this.toUIMessageStream<UI_MESSAGE>({
|
|
814
847
|
originalMessages,
|
|
815
848
|
generateMessageId,
|
|
849
|
+
onStepEnd,
|
|
850
|
+
onStepFinish,
|
|
816
851
|
onEnd,
|
|
817
852
|
onFinish,
|
|
818
853
|
messageMetadata,
|
|
@@ -834,30 +869,88 @@ export class HarnessStreamTextResult<
|
|
|
834
869
|
|
|
835
870
|
private appendToCurrentStepContent(part: TextStreamPart<TOOLS>): void {
|
|
836
871
|
switch (part.type) {
|
|
872
|
+
case 'text-start': {
|
|
873
|
+
const content: AccumulatedTextContent = {
|
|
874
|
+
type: 'text',
|
|
875
|
+
text: '',
|
|
876
|
+
...(part.providerMetadata != null
|
|
877
|
+
? { providerMetadata: part.providerMetadata }
|
|
878
|
+
: {}),
|
|
879
|
+
};
|
|
880
|
+
this.currentStepTextContent.set(part.id, content);
|
|
881
|
+
this.currentStepContent.push(content);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
837
884
|
case 'text-delta': {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (last && last.type === 'text') {
|
|
842
|
-
(last as { text: string }).text += part.text;
|
|
843
|
-
} else {
|
|
844
|
-
this.currentStepContent.push({
|
|
885
|
+
let content = this.currentStepTextContent.get(part.id);
|
|
886
|
+
if (content == null) {
|
|
887
|
+
content = {
|
|
845
888
|
type: 'text',
|
|
846
|
-
text:
|
|
847
|
-
|
|
889
|
+
text: '',
|
|
890
|
+
...(part.providerMetadata != null
|
|
891
|
+
? { providerMetadata: part.providerMetadata }
|
|
892
|
+
: {}),
|
|
893
|
+
};
|
|
894
|
+
this.currentStepTextContent.set(part.id, content);
|
|
895
|
+
this.currentStepContent.push(content);
|
|
848
896
|
}
|
|
897
|
+
content.text += part.text;
|
|
898
|
+
if (part.providerMetadata != null) {
|
|
899
|
+
content.providerMetadata = part.providerMetadata;
|
|
900
|
+
}
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
case 'text-end': {
|
|
904
|
+
const content = this.currentStepTextContent.get(part.id);
|
|
905
|
+
if (content != null) {
|
|
906
|
+
if (part.providerMetadata != null) {
|
|
907
|
+
content.providerMetadata = part.providerMetadata;
|
|
908
|
+
}
|
|
909
|
+
if (part.citations != null) {
|
|
910
|
+
content.citations = part.citations;
|
|
911
|
+
}
|
|
912
|
+
this.currentStepTextContent.delete(part.id);
|
|
913
|
+
}
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
case 'reasoning-start': {
|
|
917
|
+
const content: AccumulatedReasoningContent = {
|
|
918
|
+
type: 'reasoning',
|
|
919
|
+
text: '',
|
|
920
|
+
...(part.providerMetadata != null
|
|
921
|
+
? { providerMetadata: part.providerMetadata }
|
|
922
|
+
: {}),
|
|
923
|
+
};
|
|
924
|
+
this.currentStepReasoningContent.set(part.id, content);
|
|
925
|
+
this.currentStepContent.push(content);
|
|
849
926
|
return;
|
|
850
927
|
}
|
|
851
928
|
case 'reasoning-delta': {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
(last as { text: string }).text += part.text;
|
|
856
|
-
} else {
|
|
857
|
-
this.currentStepContent.push({
|
|
929
|
+
let content = this.currentStepReasoningContent.get(part.id);
|
|
930
|
+
if (content == null) {
|
|
931
|
+
content = {
|
|
858
932
|
type: 'reasoning',
|
|
859
|
-
text:
|
|
860
|
-
|
|
933
|
+
text: '',
|
|
934
|
+
...(part.providerMetadata != null
|
|
935
|
+
? { providerMetadata: part.providerMetadata }
|
|
936
|
+
: {}),
|
|
937
|
+
};
|
|
938
|
+
this.currentStepReasoningContent.set(part.id, content);
|
|
939
|
+
this.currentStepContent.push(content);
|
|
940
|
+
}
|
|
941
|
+
content.text += part.text;
|
|
942
|
+
if (part.providerMetadata != null) {
|
|
943
|
+
content.providerMetadata = part.providerMetadata;
|
|
944
|
+
}
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
case 'reasoning-end': {
|
|
948
|
+
const content = this.currentStepReasoningContent.get(part.id);
|
|
949
|
+
if (content != null) {
|
|
950
|
+
if (part.providerMetadata != null) {
|
|
951
|
+
content.providerMetadata = part.providerMetadata;
|
|
952
|
+
}
|
|
953
|
+
this.currentStepReasoningContent.delete(part.id);
|
|
861
954
|
}
|
|
862
955
|
return;
|
|
863
956
|
}
|
|
@@ -303,12 +303,15 @@ export function runPrompt<
|
|
|
303
303
|
>();
|
|
304
304
|
/*
|
|
305
305
|
* Host results are echoed back as `tool-result` events too, so the event
|
|
306
|
-
* alone cannot say who ran the tool —
|
|
307
|
-
* `tool-call`. Only an explicit `true` counts:
|
|
308
|
-
* defines an omitted flag as client-executed. A
|
|
309
|
-
* arrived in an earlier slice, where nothing
|
|
306
|
+
* alone cannot provide the parsed input or say who ran the tool — restore
|
|
307
|
+
* both from the originating `tool-call`. Only an explicit `true` counts:
|
|
308
|
+
* `LanguageModelV4ToolCall` defines an omitted flag as client-executed. A
|
|
309
|
+
* call missing from the map arrived in an earlier slice, where nothing
|
|
310
|
+
* here can classify it.
|
|
310
311
|
*/
|
|
311
312
|
const translateOptions = {
|
|
313
|
+
getToolCall: (toolCallId: string) =>
|
|
314
|
+
toolCallsByToolCallId.get(toolCallId),
|
|
312
315
|
isProviderExecuted: (toolCallId: string): boolean => {
|
|
313
316
|
const rawToolCall = rawToolCallsByToolCallId.get(toolCallId);
|
|
314
317
|
return rawToolCall == null || rawToolCall.providerExecuted === true;
|
|
@@ -29,6 +29,17 @@ import { generateId, type ToolSet } from '@ai-sdk/provider-utils';
|
|
|
29
29
|
export function translateStreamPart<TOOLS extends ToolSet>(
|
|
30
30
|
event: HarnessV1StreamPart,
|
|
31
31
|
options: {
|
|
32
|
+
/**
|
|
33
|
+
* Returns the parsed tool call that produced a result. Harness tool-result
|
|
34
|
+
* events only carry the output, so their input and execution origin must
|
|
35
|
+
* be restored from the originating call.
|
|
36
|
+
*/
|
|
37
|
+
getToolCall?: (toolCallId: string) =>
|
|
38
|
+
| {
|
|
39
|
+
input: unknown;
|
|
40
|
+
providerExecuted?: boolean;
|
|
41
|
+
}
|
|
42
|
+
| undefined;
|
|
32
43
|
/**
|
|
33
44
|
* Whether the tool call that produced this event ran inside the harness
|
|
34
45
|
* runtime. Host tools travel the same `tool-result` events and their
|
|
@@ -119,9 +130,12 @@ export function translateStreamPart<TOOLS extends ToolSet>(
|
|
|
119
130
|
return [];
|
|
120
131
|
|
|
121
132
|
case 'tool-result':
|
|
133
|
+
const toolCall = options.getToolCall?.(event.toolCallId);
|
|
122
134
|
if (
|
|
123
135
|
event.isError === true &&
|
|
124
|
-
(
|
|
136
|
+
(toolCall?.providerExecuted ??
|
|
137
|
+
options.isProviderExecuted?.(event.toolCallId) ??
|
|
138
|
+
true)
|
|
125
139
|
) {
|
|
126
140
|
/*
|
|
127
141
|
* `providerExecuted` decides whether the message survives:
|
|
@@ -134,7 +148,7 @@ export function translateStreamPart<TOOLS extends ToolSet>(
|
|
|
134
148
|
type: 'tool-error',
|
|
135
149
|
toolCallId: event.toolCallId,
|
|
136
150
|
toolName: event.toolName,
|
|
137
|
-
input:
|
|
151
|
+
input: toolCall?.input,
|
|
138
152
|
error: event.result,
|
|
139
153
|
providerExecuted: true,
|
|
140
154
|
...(event.dynamic !== undefined ? { dynamic: event.dynamic } : {}),
|
|
@@ -150,8 +164,11 @@ export function translateStreamPart<TOOLS extends ToolSet>(
|
|
|
150
164
|
type: 'tool-result',
|
|
151
165
|
toolCallId: event.toolCallId,
|
|
152
166
|
toolName: event.toolName,
|
|
153
|
-
input:
|
|
167
|
+
input: toolCall?.input,
|
|
154
168
|
output: event.result,
|
|
169
|
+
...(toolCall?.providerExecuted !== undefined
|
|
170
|
+
? { providerExecuted: toolCall.providerExecuted }
|
|
171
|
+
: {}),
|
|
155
172
|
...(event.preliminary !== undefined
|
|
156
173
|
? { preliminary: event.preliminary }
|
|
157
174
|
: {}),
|