@ai-sdk/harness 1.0.166 → 1.0.168
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 +21 -0
- package/dist/agent/index.js +79 -14
- package/dist/agent/index.js.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +10 -4
- package/dist/utils/index.js.map +1 -1
- package/package.json +4 -4
- package/src/agent/internal/run-prompt.ts +98 -15
- package/src/utils/sandbox-channel.ts +15 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.168",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@ai-sdk/provider": "4.0.34",
|
|
48
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
49
|
-
"ai": "7.0.
|
|
48
|
+
"@ai-sdk/provider-utils": "5.0.71",
|
|
49
|
+
"ai": "7.0.157"
|
|
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.157",
|
|
62
62
|
"@opentelemetry/sdk-trace-base": "2.7.1",
|
|
63
63
|
"@types/node": "22.19.19",
|
|
64
64
|
"@types/ws": "^8.5.13",
|
|
@@ -433,8 +433,8 @@ export function runPrompt<
|
|
|
433
433
|
* An approval request or a pending client tool result does not end the
|
|
434
434
|
* response right away: the rest of the step (other tool calls, native
|
|
435
435
|
* results, host tool executions) still has to be surfaced. The pause
|
|
436
|
-
* happens at the next `finish-step
|
|
437
|
-
*
|
|
436
|
+
* happens at the next `finish-step`. Unless a built-in approval blocks
|
|
437
|
+
* the runtime, host tools must first be echoed back by the runtime.
|
|
438
438
|
*/
|
|
439
439
|
let hostInputPauseRequested = false;
|
|
440
440
|
let deferredPauseFinishStep: FinishStepPart | undefined;
|
|
@@ -446,6 +446,10 @@ export function runPrompt<
|
|
|
446
446
|
*/
|
|
447
447
|
let ignoredFinishStep: FinishStepPart | undefined;
|
|
448
448
|
const hostToolCallIdsAwaitingEcho = new Set<string>();
|
|
449
|
+
const submittedHostToolResults = new Map<
|
|
450
|
+
string,
|
|
451
|
+
Parameters<HarnessV1PromptControl['submitToolResult']>[0]
|
|
452
|
+
>();
|
|
449
453
|
const buildModelCallContent = (): ContentPart<TOOLS>[] => {
|
|
450
454
|
const parts: ContentPart<TOOLS>[] = [];
|
|
451
455
|
if (stepText) parts.push({ type: 'text', text: stepText });
|
|
@@ -517,6 +521,40 @@ export function runPrompt<
|
|
|
517
521
|
finishStep: FinishStepPart | undefined,
|
|
518
522
|
): Promise<void> => {
|
|
519
523
|
await waitForOutstandingHostToolExecutions();
|
|
524
|
+
for (const submission of submittedHostToolResults.values()) {
|
|
525
|
+
const { toolCallId, ...completedResult } = submission;
|
|
526
|
+
const rawToolCall = rawToolCallsByToolCallId.get(toolCallId);
|
|
527
|
+
const toolCall = toolCallsByToolCallId.get(toolCallId);
|
|
528
|
+
const pendingResult =
|
|
529
|
+
pendingResultsByToolCallId.get(toolCallId) ??
|
|
530
|
+
(rawToolCall == null
|
|
531
|
+
? undefined
|
|
532
|
+
: {
|
|
533
|
+
toolCallId,
|
|
534
|
+
toolName: rawToolCall.toolName,
|
|
535
|
+
input: rawToolCall.input,
|
|
536
|
+
...(rawToolCall.providerMetadata !== undefined
|
|
537
|
+
? { providerOptions: rawToolCall.providerMetadata }
|
|
538
|
+
: {}),
|
|
539
|
+
});
|
|
540
|
+
if (pendingResult == null) continue;
|
|
541
|
+
onPendingToolResult({ ...pendingResult, completedResult });
|
|
542
|
+
const toolOutput = toolExecutions.get(toolCallId)?.toolOutput;
|
|
543
|
+
if (
|
|
544
|
+
toolCall != null &&
|
|
545
|
+
toolOutput != null &&
|
|
546
|
+
!settledHostToolCallIds.has(toolCallId)
|
|
547
|
+
) {
|
|
548
|
+
enqueueHostToolOutcome({
|
|
549
|
+
toolCall,
|
|
550
|
+
outcome:
|
|
551
|
+
toolOutput.type === 'tool-error'
|
|
552
|
+
? { ok: false, error: toolOutput.error }
|
|
553
|
+
: { ok: true, output: toolOutput.output },
|
|
554
|
+
isContinuation: false,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
520
558
|
closingResumedStep = false;
|
|
521
559
|
await completeStep({
|
|
522
560
|
finishReason: toolCallsFinishReason,
|
|
@@ -597,7 +635,15 @@ export function runPrompt<
|
|
|
597
635
|
const enqueueHostToolOutcome = (options: {
|
|
598
636
|
toolCall: ToolCallTextStreamPart;
|
|
599
637
|
outcome: HostToolOutcome;
|
|
638
|
+
isContinuation?: boolean;
|
|
600
639
|
}): void => {
|
|
640
|
+
const enqueue = (part: TextStreamPart<TOOLS>): void => {
|
|
641
|
+
if (options.isContinuation === false) {
|
|
642
|
+
result.enqueue(part);
|
|
643
|
+
} else {
|
|
644
|
+
result.enqueueContinuation(part);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
601
647
|
if (options.outcome.ok) {
|
|
602
648
|
const stripped = stripWorkDir(
|
|
603
649
|
{
|
|
@@ -611,7 +657,7 @@ export function runPrompt<
|
|
|
611
657
|
},
|
|
612
658
|
input.sessionWorkDir,
|
|
613
659
|
) as Extract<HarnessV1StreamPart, { type: 'tool-result' }>;
|
|
614
|
-
|
|
660
|
+
enqueue({
|
|
615
661
|
type: 'tool-result',
|
|
616
662
|
toolCallId: options.toolCall.toolCallId,
|
|
617
663
|
toolName: options.toolCall.toolName,
|
|
@@ -621,7 +667,7 @@ export function runPrompt<
|
|
|
621
667
|
return;
|
|
622
668
|
}
|
|
623
669
|
|
|
624
|
-
|
|
670
|
+
enqueue({
|
|
625
671
|
type: 'tool-error',
|
|
626
672
|
toolCallId: options.toolCall.toolCallId,
|
|
627
673
|
toolName: options.toolCall.toolName,
|
|
@@ -662,6 +708,7 @@ export function runPrompt<
|
|
|
662
708
|
if (!settledHostToolCallIds.has(submission.toolCallId)) {
|
|
663
709
|
hostToolCallIdsAwaitingEcho.add(submission.toolCallId);
|
|
664
710
|
}
|
|
711
|
+
submittedHostToolResults.set(submission.toolCallId, submission);
|
|
665
712
|
return control.submitToolResult(submission);
|
|
666
713
|
}
|
|
667
714
|
const toolCall =
|
|
@@ -719,7 +766,6 @@ export function runPrompt<
|
|
|
719
766
|
toolCallId: pendingResult.toolCallId,
|
|
720
767
|
...submission,
|
|
721
768
|
});
|
|
722
|
-
pendingResultsByToolCallId.delete(pendingResult.toolCallId);
|
|
723
769
|
};
|
|
724
770
|
const processPendingApprovalContinuation = async (
|
|
725
771
|
approval: HarnessV1PendingToolApproval,
|
|
@@ -744,6 +790,7 @@ export function runPrompt<
|
|
|
744
790
|
part: await validateToolCall({
|
|
745
791
|
event: rawToolCall,
|
|
746
792
|
tools: input.tools,
|
|
793
|
+
harnessId: input.harness.harnessId,
|
|
747
794
|
}),
|
|
748
795
|
});
|
|
749
796
|
toolCall = displayHostToolCall({
|
|
@@ -785,11 +832,18 @@ export function runPrompt<
|
|
|
785
832
|
|
|
786
833
|
settledHostToolCallIds.add(approval.toolCallId);
|
|
787
834
|
if (!continuation.approved) {
|
|
835
|
+
const output = {
|
|
836
|
+
type: 'execution-denied' as const,
|
|
837
|
+
reason: continuation.reason,
|
|
838
|
+
};
|
|
788
839
|
await submitToolResult({
|
|
789
840
|
toolCallId: approval.toolCallId,
|
|
790
|
-
output
|
|
791
|
-
|
|
792
|
-
|
|
841
|
+
output,
|
|
842
|
+
toolResult: {
|
|
843
|
+
type: 'tool-result',
|
|
844
|
+
toolCallId: approval.toolCallId,
|
|
845
|
+
toolName: approval.toolName,
|
|
846
|
+
output,
|
|
793
847
|
},
|
|
794
848
|
});
|
|
795
849
|
return 'continued';
|
|
@@ -927,6 +981,8 @@ export function runPrompt<
|
|
|
927
981
|
}
|
|
928
982
|
if (value.type === 'tool-result') {
|
|
929
983
|
hostToolCallIdsAwaitingEcho.delete(value.toolCallId);
|
|
984
|
+
submittedHostToolResults.delete(value.toolCallId);
|
|
985
|
+
pendingResultsByToolCallId.delete(value.toolCallId);
|
|
930
986
|
}
|
|
931
987
|
|
|
932
988
|
if (pendingStopBoundary != null) {
|
|
@@ -1021,7 +1077,12 @@ export function runPrompt<
|
|
|
1021
1077
|
if (value.type === 'finish-step') {
|
|
1022
1078
|
if (hostInputPauseRequested) {
|
|
1023
1079
|
await waitForOutstandingHostToolExecutions();
|
|
1024
|
-
if (
|
|
1080
|
+
if (
|
|
1081
|
+
hostToolCallIdsAwaitingEcho.size === 0 ||
|
|
1082
|
+
[...pendingApprovalsByApprovalId.values()].some(
|
|
1083
|
+
approval => approval.kind === 'builtin',
|
|
1084
|
+
)
|
|
1085
|
+
) {
|
|
1025
1086
|
await pauseForHostInput(value);
|
|
1026
1087
|
return;
|
|
1027
1088
|
}
|
|
@@ -1178,6 +1239,7 @@ export function runPrompt<
|
|
|
1178
1239
|
const parsed = await validateToolCall<TOOLS>({
|
|
1179
1240
|
event: isHostTool ? value : displayValue,
|
|
1180
1241
|
tools: isFilteredBuiltinDynamicCall ? undefined : input.tools,
|
|
1242
|
+
harnessId: input.harness.harnessId,
|
|
1181
1243
|
});
|
|
1182
1244
|
const parsedToolCall = asToolCallTextStreamPart({ part: parsed });
|
|
1183
1245
|
validatedHostToolCall = isHostTool ? parsedToolCall : undefined;
|
|
@@ -1347,7 +1409,7 @@ export function runPrompt<
|
|
|
1347
1409
|
!hasTool({ tools: activeTools, toolName: toolCall.toolName })
|
|
1348
1410
|
) {
|
|
1349
1411
|
const output = {
|
|
1350
|
-
type: 'execution-denied',
|
|
1412
|
+
type: 'execution-denied' as const,
|
|
1351
1413
|
reason: getHarnessV1BuiltinToolFilteringDenialReason({
|
|
1352
1414
|
toolName: toolCall.toolName,
|
|
1353
1415
|
}),
|
|
@@ -1355,6 +1417,12 @@ export function runPrompt<
|
|
|
1355
1417
|
await submitToolResult({
|
|
1356
1418
|
toolCallId: toolCall.toolCallId,
|
|
1357
1419
|
output,
|
|
1420
|
+
toolResult: {
|
|
1421
|
+
type: 'tool-result',
|
|
1422
|
+
toolCallId: toolCall.toolCallId,
|
|
1423
|
+
toolName: toolCall.toolName,
|
|
1424
|
+
output,
|
|
1425
|
+
},
|
|
1358
1426
|
});
|
|
1359
1427
|
const execution = toolExecutions.get(toolCall.toolCallId);
|
|
1360
1428
|
if (execution != null) {
|
|
@@ -1414,7 +1482,7 @@ export function runPrompt<
|
|
|
1414
1482
|
providerExecuted: false,
|
|
1415
1483
|
});
|
|
1416
1484
|
const output = {
|
|
1417
|
-
type: 'execution-denied',
|
|
1485
|
+
type: 'execution-denied' as const,
|
|
1418
1486
|
reason: customToolApprovalDecision.reason,
|
|
1419
1487
|
};
|
|
1420
1488
|
const execution = toolExecutions.get(toolCall.toolCallId);
|
|
@@ -1424,6 +1492,12 @@ export function runPrompt<
|
|
|
1424
1492
|
await submitToolResult({
|
|
1425
1493
|
toolCallId: toolCall.toolCallId,
|
|
1426
1494
|
output,
|
|
1495
|
+
toolResult: {
|
|
1496
|
+
type: 'tool-result',
|
|
1497
|
+
toolCallId: toolCall.toolCallId,
|
|
1498
|
+
toolName: toolCall.toolName,
|
|
1499
|
+
output,
|
|
1500
|
+
},
|
|
1427
1501
|
});
|
|
1428
1502
|
if (execution != null) {
|
|
1429
1503
|
execution.toolOutput = toToolOutput({
|
|
@@ -1819,8 +1893,19 @@ async function maybeExecuteHostTool<TOOLS extends ToolSet>(input: {
|
|
|
1819
1893
|
export async function validateToolCall<TOOLS extends ToolSet>(args: {
|
|
1820
1894
|
event: Extract<HarnessV1StreamPart, { type: 'tool-call' }>;
|
|
1821
1895
|
tools: TOOLS | undefined;
|
|
1896
|
+
harnessId: string;
|
|
1822
1897
|
}): Promise<TextStreamPart<TOOLS>> {
|
|
1823
|
-
const { event, tools } = args;
|
|
1898
|
+
const { event, tools, harnessId } = args;
|
|
1899
|
+
const providerMetadata =
|
|
1900
|
+
event.nativeName !== undefined && event.nativeName !== event.toolName
|
|
1901
|
+
? {
|
|
1902
|
+
...event.providerMetadata,
|
|
1903
|
+
[harnessId]: {
|
|
1904
|
+
...event.providerMetadata?.[harnessId],
|
|
1905
|
+
nativeToolName: event.nativeName,
|
|
1906
|
+
},
|
|
1907
|
+
}
|
|
1908
|
+
: event.providerMetadata;
|
|
1824
1909
|
const toolCall: LanguageModelV4ToolCall = {
|
|
1825
1910
|
type: 'tool-call',
|
|
1826
1911
|
toolCallId: event.toolCallId,
|
|
@@ -1830,9 +1915,7 @@ export async function validateToolCall<TOOLS extends ToolSet>(args: {
|
|
|
1830
1915
|
? { providerExecuted: event.providerExecuted }
|
|
1831
1916
|
: {}),
|
|
1832
1917
|
...(event.dynamic !== undefined ? { dynamic: event.dynamic } : {}),
|
|
1833
|
-
...(
|
|
1834
|
-
? { providerMetadata: event.providerMetadata }
|
|
1835
|
-
: {}),
|
|
1918
|
+
...(providerMetadata !== undefined ? { providerMetadata } : {}),
|
|
1836
1919
|
};
|
|
1837
1920
|
|
|
1838
1921
|
const parsed = await parseToolCall<TOOLS>({
|
|
@@ -635,7 +635,7 @@ export class SandboxChannel<
|
|
|
635
635
|
}
|
|
636
636
|
|
|
637
637
|
private rawSend(text: string): void {
|
|
638
|
-
if (this.connected && this.ws) {
|
|
638
|
+
if (this.connected && this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
639
639
|
this.ws.send(text);
|
|
640
640
|
} else {
|
|
641
641
|
this.pendingSends.push(text);
|
|
@@ -643,9 +643,20 @@ export class SandboxChannel<
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
private flushPending(): void {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
646
|
+
let sent = 0;
|
|
647
|
+
try {
|
|
648
|
+
while (
|
|
649
|
+
sent < this.pendingSends.length &&
|
|
650
|
+
this.connected &&
|
|
651
|
+
this.ws &&
|
|
652
|
+
this.ws.readyState === this.ws.OPEN
|
|
653
|
+
) {
|
|
654
|
+
this.ws.send(this.pendingSends[sent]);
|
|
655
|
+
sent++;
|
|
656
|
+
}
|
|
657
|
+
} finally {
|
|
658
|
+
this.pendingSends.splice(0, sent);
|
|
659
|
+
}
|
|
649
660
|
}
|
|
650
661
|
|
|
651
662
|
private enqueue(work: () => void | Promise<void>): void {
|