@ai-sdk/harness 1.0.74 → 1.0.75

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.74",
3
+ "version": "1.0.75",
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.7",
48
48
  "@ai-sdk/provider-utils": "5.0.27",
49
- "ai": "7.0.67"
49
+ "ai": "7.0.68"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "ws": "^8.21.0",
@@ -65,7 +65,7 @@
65
65
  "typescript": "5.8.3",
66
66
  "ws": "^8.21.0",
67
67
  "zod": "3.25.76",
68
- "@ai-sdk/otel": "1.0.67",
68
+ "@ai-sdk/otel": "1.0.68",
69
69
  "@vercel/ai-tsconfig": "0.0.0"
70
70
  },
71
71
  "engines": {
@@ -824,21 +824,10 @@ export class HarnessStreamTextResult<
824
824
  return;
825
825
  }
826
826
  case 'tool-call':
827
- this.currentStepContent.push({
828
- ...(part as object),
829
- } as ContentPart<TOOLS>);
830
- return;
831
827
  case 'tool-approval-request':
832
- this.currentStepContent.push({
833
- ...(part as object),
834
- } as ContentPart<TOOLS>);
835
- return;
836
828
  case 'tool-approval-response':
837
- this.currentStepContent.push({
838
- ...(part as object),
839
- } as ContentPart<TOOLS>);
840
- return;
841
829
  case 'tool-result':
830
+ case 'tool-error':
842
831
  this.currentStepContent.push({
843
832
  ...(part as object),
844
833
  } as ContentPart<TOOLS>);
@@ -219,6 +219,19 @@ export function runPrompt<
219
219
  string,
220
220
  Extract<HarnessV1StreamPart, { type: 'tool-call' }>
221
221
  >();
222
+ /*
223
+ * Host results are echoed back as `tool-result` events too, so the event
224
+ * alone cannot say who ran the tool — classify by the originating
225
+ * `tool-call`. Only an explicit `true` counts: `LanguageModelV4ToolCall`
226
+ * defines an omitted flag as client-executed. A call missing from the map
227
+ * arrived in an earlier slice, where nothing here can classify it.
228
+ */
229
+ const translateOptions = {
230
+ isProviderExecuted: (toolCallId: string): boolean => {
231
+ const rawToolCall = rawToolCallsByToolCallId.get(toolCallId);
232
+ return rawToolCall == null || rawToolCall.providerExecuted === true;
233
+ },
234
+ };
222
235
  const pendingApprovalsByApprovalId = new Map(
223
236
  pendingToolApprovals.map(approval => [approval.approvalId, approval]),
224
237
  );
@@ -709,7 +722,10 @@ export function runPrompt<
709
722
  }
710
723
 
711
724
  // Forward to consumer as soon as possible.
712
- for (const part of translateStreamPart<TOOLS>(displayValue)) {
725
+ for (const part of translateStreamPart<TOOLS>(
726
+ displayValue,
727
+ translateOptions,
728
+ )) {
713
729
  result.enqueue(part);
714
730
  }
715
731
 
@@ -14,6 +14,8 @@ import { generateId, type ToolSet } from '@ai-sdk/provider-utils';
14
14
  * - tool-call events are not translated here — validation against the
15
15
  * merged tool set is async and handled by `validateToolCall` in
16
16
  * `run-prompt.ts`
17
+ * - a failed `tool-result` from a provider-executed tool becomes a
18
+ * `tool-error` (see `isProviderExecuted`)
17
19
  * - the harness `raw` part is forwarded as the AI SDK `raw` part
18
20
  *
19
21
  * Returns an array of zero or more AI SDK parts. Most harness events project
@@ -26,6 +28,17 @@ import { generateId, type ToolSet } from '@ai-sdk/provider-utils';
26
28
  */
27
29
  export function translateStreamPart<TOOLS extends ToolSet>(
28
30
  event: HarnessV1StreamPart,
31
+ options: {
32
+ /**
33
+ * Whether the tool call that produced this event ran inside the harness
34
+ * runtime. Host tools travel the same `tool-result` events and their
35
+ * failures are echoed back with `isError`, so the event alone cannot say
36
+ * who ran the tool — only the originating `tool-call` can, and correlating
37
+ * the two is the caller's job. Omit the callback to treat every failure as
38
+ * provider-executed.
39
+ */
40
+ isProviderExecuted?: (toolCallId: string) => boolean;
41
+ } = {},
29
42
  ): ReadonlyArray<TextStreamPart<TOOLS>> {
30
43
  switch (event.type) {
31
44
  case 'stream-start':
@@ -101,6 +114,32 @@ export function translateStreamPart<TOOLS extends ToolSet>(
101
114
  return [];
102
115
 
103
116
  case 'tool-result':
117
+ if (
118
+ event.isError === true &&
119
+ (options.isProviderExecuted?.(event.toolCallId) ?? true)
120
+ ) {
121
+ /*
122
+ * `providerExecuted` decides whether the message survives:
123
+ * `toUIMessageChunk` forwards the real error text only for
124
+ * provider-executed errors, and replaces every other one with the
125
+ * generic `onError` string.
126
+ */
127
+ return [
128
+ {
129
+ type: 'tool-error',
130
+ toolCallId: event.toolCallId,
131
+ toolName: event.toolName,
132
+ input: undefined,
133
+ error: event.result,
134
+ providerExecuted: true,
135
+ ...(event.dynamic !== undefined ? { dynamic: event.dynamic } : {}),
136
+ ...(event.providerMetadata !== undefined
137
+ ? { providerMetadata: event.providerMetadata }
138
+ : {}),
139
+ } as TextStreamPart<TOOLS>,
140
+ ];
141
+ }
142
+
104
143
  return [
105
144
  {
106
145
  type: 'tool-result',