@ai-sdk/harness 1.0.101 → 1.0.102

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.
@@ -17,6 +17,7 @@ import { toHarnessStream } from './to-harness-stream';
17
17
  import {
18
18
  executeTool,
19
19
  generateId,
20
+ type InferToolSetContext,
20
21
  isExecutableTool,
21
22
  safeParseJSON,
22
23
  type Context,
@@ -31,7 +32,7 @@ import {
31
32
  type LanguageModelV4ToolCall,
32
33
  type LanguageModelV4Usage,
33
34
  } from '@ai-sdk/provider';
34
- import { parseToolCall } from 'ai/internal';
35
+ import { asLanguageModelUsage, parseToolCall } from 'ai/internal';
35
36
  import type {
36
37
  ContentPart,
37
38
  OutputInterface as Output,
@@ -40,15 +41,18 @@ import type {
40
41
  StopCondition,
41
42
  TelemetryOptions,
42
43
  TextStreamPart,
44
+ TypedToolCall,
45
+ TypedToolError,
46
+ TypedToolResult,
43
47
  } from 'ai';
44
48
  import type { HarnessAgentToolApprovalConfiguration } from '../harness-agent-settings';
45
49
  import { HarnessStreamTextResult } from './harness-stream-text-result';
46
50
  import { translateStreamPart } from './translate-stream-part';
47
51
  import { createToolInputWorkDirStripper, stripWorkDir } from './strip-work-dir';
48
52
  import {
49
- createTurnTelemetry,
50
- type TurnContentPart,
51
- type TurnTelemetry,
53
+ createTurnLifecycle,
54
+ type HarnessAgentLifecycleCallbacks,
55
+ type TurnLifecycle,
52
56
  } from './turn-telemetry';
53
57
  import { resolveCustomToolApproval } from './permission-mode';
54
58
  import { logBridgeError } from '../../utils/bridge-diagnostics';
@@ -118,6 +122,7 @@ export function runPrompt<
118
122
  responseFormat?: HarnessV1ResponseFormat | undefined;
119
123
  output?: OUTPUT | undefined;
120
124
  telemetry?: TelemetryOptions | undefined;
125
+ callbacks?: HarnessAgentLifecycleCallbacks<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
121
126
  stopConditions?: ReadonlyArray<StopCondition<TOOLS, RUNTIME_CONTEXT>>;
122
127
  toolApproval?: HarnessAgentToolApprovalConfiguration | undefined;
123
128
  pendingToolApprovals?: readonly HarnessV1PendingToolApproval[];
@@ -141,13 +146,15 @@ export function runPrompt<
141
146
  result: HarnessStreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
142
147
  done: Promise<void>;
143
148
  } {
149
+ const callId = generateId();
150
+ const toolsContext = {} as InferToolSetContext<TOOLS>;
144
151
  const result = new HarnessStreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>({
145
152
  tools: input.tools,
146
153
  runtimeContext: input.runtimeContext,
147
- // toolsContext is not configurable for harnesses; pass undefined cast.
148
- toolsContext: undefined as never,
154
+ toolsContext,
149
155
  harnessId: input.harness.harnessId,
150
- sessionId: input.session.sessionId,
156
+ callId,
157
+ modelId: input.model ?? '',
151
158
  output: input.output,
152
159
  });
153
160
  const pendingToolApprovals = input.pendingToolApprovals ?? [];
@@ -170,16 +177,23 @@ export function runPrompt<
170
177
  })),
171
178
  );
172
179
 
173
- const telemetry = createTurnTelemetry({
180
+ const lifecycle = createTurnLifecycle({
181
+ callId,
174
182
  telemetry: input.telemetry,
183
+ callbacks: input.callbacks ?? {},
175
184
  harnessId: input.harness.harnessId,
176
185
  modelId: input.model,
177
186
  instructions: input.instructions,
178
187
  tools: input.tools,
179
188
  activeToolNames,
180
189
  toolSpecs: input.toolSpecs,
181
- promptText: input.prompt != null ? promptToText(input.prompt) : '',
190
+ messages:
191
+ input.prompt == null
192
+ ? []
193
+ : [{ role: 'user', content: promptToText(input.prompt) }],
182
194
  runtimeContext: input.runtimeContext,
195
+ toolsContext,
196
+ output: input.output,
183
197
  });
184
198
 
185
199
  /*
@@ -243,7 +257,7 @@ export function runPrompt<
243
257
  },
244
258
  });
245
259
  } catch (err) {
246
- await telemetry.error(err);
260
+ await lifecycle.error(err);
247
261
  logBridgeError({
248
262
  harnessId: input.harness.harnessId,
249
263
  sessionId: input.session.sessionId,
@@ -340,25 +354,59 @@ export function runPrompt<
340
354
  pendingStopBoundary = undefined;
341
355
  };
342
356
 
343
- // Accumulate the model's output content per step so telemetry can record
344
- // `gen_ai.output.messages` and reporters can log what was actually said.
357
+ // Accumulate the model response until its step boundary. Harness runtimes
358
+ // may execute tools before emitting `finish-step`, so tool lifecycle
359
+ // notifications and consumer-visible tool outcomes are held until then.
345
360
  let stepText = '';
346
361
  let stepReasoning = '';
347
- let stepToolCalls: TurnContentPart[] = [];
362
+ let stepToolCalls: ContentPart<TOOLS>[] = [];
363
+ let stepProviderToolResults: ContentPart<TOOLS>[] = [];
364
+ let stepApprovalRequests: ContentPart<TOOLS>[] = [];
365
+ let bufferedToolOutcomes: Array<() => void> = [];
366
+ const toolExecutions = new Map<
367
+ string,
368
+ {
369
+ toolCall: TypedToolCall<TOOLS>;
370
+ toolOutput?: TypedToolResult<TOOLS> | TypedToolError<TOOLS>;
371
+ toolExecutionMs?: number;
372
+ }
373
+ >();
374
+ const publishToolExecutions = async (): Promise<void> => {
375
+ for (const execution of toolExecutions.values()) {
376
+ if (execution.toolOutput == null) continue;
377
+ await lifecycle.toolExecutionStart({
378
+ toolCall: execution.toolCall,
379
+ });
380
+ await lifecycle.toolExecutionEnd({
381
+ toolCall: execution.toolCall,
382
+ toolOutput: execution.toolOutput,
383
+ toolExecutionMs: execution.toolExecutionMs ?? 0,
384
+ });
385
+ }
386
+ for (const publish of bufferedToolOutcomes) publish();
387
+ toolExecutions.clear();
388
+ bufferedToolOutcomes = [];
389
+ };
348
390
  let expectedStepToolCallCount: number | undefined;
349
391
  let observedStepToolCallCount = 0;
350
392
  let pauseAfterStepToolCalls = false;
351
- const buildStepContent = (): TurnContentPart[] => {
352
- const parts: TurnContentPart[] = [];
393
+ const buildModelCallContent = (): ContentPart<TOOLS>[] => {
394
+ const parts: ContentPart<TOOLS>[] = [];
353
395
  if (stepText) parts.push({ type: 'text', text: stepText });
354
396
  if (stepReasoning) parts.push({ type: 'reasoning', text: stepReasoning });
355
397
  parts.push(...stepToolCalls);
398
+ parts.push(...stepApprovalRequests);
399
+ parts.push(...stepProviderToolResults);
356
400
  return parts;
357
401
  };
358
402
  const resetStepContent = (): void => {
359
403
  stepText = '';
360
404
  stepReasoning = '';
361
405
  stepToolCalls = [];
406
+ stepProviderToolResults = [];
407
+ stepApprovalRequests = [];
408
+ bufferedToolOutcomes = [];
409
+ toolExecutions.clear();
362
410
  expectedStepToolCallCount = undefined;
363
411
  observedStepToolCallCount = 0;
364
412
  pauseAfterStepToolCalls = false;
@@ -385,13 +433,13 @@ export function runPrompt<
385
433
  usage: LanguageModelV4Usage;
386
434
  providerMetadata: ProviderMetadata | undefined;
387
435
  }): Promise<StepResult<TOOLS, RUNTIME_CONTEXT>> => {
388
- await telemetry.stepFinish({
389
- finishReason: input.finishReason,
390
- usage: input.usage,
436
+ await lifecycle.languageModelCallEnd({
437
+ finishReason: input.finishReason.unified,
438
+ usage: asLanguageModelUsage(input.usage),
391
439
  providerMetadata: input.providerMetadata,
392
- content: buildStepContent(),
440
+ content: buildModelCallContent(),
393
441
  });
394
- resetStepContent();
442
+ await publishToolExecutions();
395
443
  const step = result.finishStep({
396
444
  finishReason: input.finishReason,
397
445
  usage: input.usage,
@@ -399,6 +447,8 @@ export function runPrompt<
399
447
  warnings: [],
400
448
  });
401
449
  completedSteps.push(step);
450
+ await lifecycle.stepEnd(step);
451
+ resetStepContent();
402
452
  return step;
403
453
  };
404
454
  const finishForHostInputPause = async (options: {
@@ -411,10 +461,12 @@ export function runPrompt<
411
461
  usage: zeroUsage,
412
462
  providerMetadata: undefined,
413
463
  });
464
+ } else {
465
+ await publishToolExecutions();
414
466
  }
415
- await telemetry.end({
416
- finishReason: toolCallsFinishReason,
417
- usage: zeroUsage,
467
+ await lifecycle.end({
468
+ steps: completedSteps,
469
+ usage: asLanguageModelUsage(zeroUsage),
418
470
  });
419
471
  await result.finish();
420
472
  };
@@ -423,14 +475,16 @@ export function runPrompt<
423
475
  toolCall: ToolCallTextStreamPart;
424
476
  isAutomatic?: boolean;
425
477
  }): void => {
426
- result.enqueue({
478
+ const part = {
427
479
  type: 'tool-approval-request',
428
480
  approvalId: approval.approvalId,
429
481
  toolCall: approval.toolCall,
430
482
  ...(approval.isAutomatic !== undefined
431
483
  ? { isAutomatic: approval.isAutomatic }
432
484
  : {}),
433
- } as TextStreamPart<TOOLS>);
485
+ } as TextStreamPart<TOOLS>;
486
+ result.enqueue(part);
487
+ stepApprovalRequests.push(part as ContentPart<TOOLS>);
434
488
  };
435
489
  const enqueueAutomaticApprovalResponse = (input: {
436
490
  approvalId: string;
@@ -599,16 +653,15 @@ export function runPrompt<
599
653
  return 'continued';
600
654
  }
601
655
 
602
- await telemetry.start(input.model);
603
- await telemetry.toolStart({
604
- toolCallId: rawToolCall.toolCallId,
605
- toolName: rawToolCall.toolName,
606
- input: rawToolCall.input,
656
+ await lifecycle.start(input.model);
657
+ toolExecutions.set(rawToolCall.toolCallId, {
658
+ toolCall: toolCall as TypedToolCall<TOOLS>,
607
659
  });
660
+ const executionStartedAt = Date.now();
608
661
  const execution = await maybeExecuteHostTool({
609
662
  event: rawToolCall,
610
663
  tools: activeTools,
611
- wrappedExecuteTool: telemetry.executeTool,
664
+ wrappedExecuteTool: lifecycle.executeTool,
612
665
  sandboxSession: input.sandboxSession,
613
666
  abortSignal: input.abortSignal,
614
667
  control,
@@ -625,14 +678,16 @@ export function runPrompt<
625
678
  },
626
679
  input.sessionWorkDir,
627
680
  ) as Extract<HarnessV1StreamPart, { type: 'tool-result' }>;
628
- result.enqueue({
629
- type: 'tool-result',
630
- toolCallId: rawToolCall.toolCallId,
631
- toolName: rawToolCall.toolName,
632
- input: undefined,
633
- output: stripped.result,
634
- preliminary: true,
635
- } as TextStreamPart<TOOLS>);
681
+ bufferedToolOutcomes.push(() => {
682
+ result.enqueue({
683
+ type: 'tool-result',
684
+ toolCallId: rawToolCall.toolCallId,
685
+ toolName: rawToolCall.toolName,
686
+ input: undefined,
687
+ output: stripped.result,
688
+ preliminary: true,
689
+ } as TextStreamPart<TOOLS>);
690
+ });
636
691
  },
637
692
  });
638
693
  if (!execution.executed) {
@@ -640,11 +695,19 @@ export function runPrompt<
640
695
  await finishForHostInputPause({ completeCurrentStep: false });
641
696
  return 'awaiting-tool-result';
642
697
  }
643
- enqueueHostToolOutcome({
698
+ const toolExecution = toolExecutions.get(rawToolCall.toolCallId)!;
699
+ toolExecution.toolOutput = toToolOutput({
644
700
  toolCall,
645
701
  outcome: execution.outcome,
646
702
  });
647
- await telemetry.toolEnd(rawToolCall.toolCallId, execution.outcome);
703
+ toolExecution.toolExecutionMs = Date.now() - executionStartedAt;
704
+ bufferedToolOutcomes.push(() => {
705
+ enqueueHostToolOutcome({
706
+ toolCall,
707
+ outcome: execution.outcome,
708
+ });
709
+ });
710
+ await publishToolExecutions();
648
711
  return 'continued';
649
712
  };
650
713
 
@@ -703,9 +766,12 @@ export function runPrompt<
703
766
  ).some(Boolean)
704
767
  ) {
705
768
  await input.onStopConditionMet?.();
706
- const { finishReason, usage } = pendingStopBoundary;
769
+ const { usage } = pendingStopBoundary;
707
770
  releasePendingStopBoundary();
708
- await telemetry.end({ finishReason, usage });
771
+ await lifecycle.end({
772
+ steps: completedSteps,
773
+ usage: asLanguageModelUsage(usage),
774
+ });
709
775
  await result.finish();
710
776
  return;
711
777
  } else {
@@ -716,7 +782,9 @@ export function runPrompt<
716
782
  // Begin the operation span on stream-start, using the runtime-resolved
717
783
  // model the adapter reports (falling back to the requested turn model).
718
784
  if (value.type === 'stream-start') {
719
- await telemetry.start(value.modelId ?? input.model);
785
+ const modelId = value.modelId ?? input.model;
786
+ if (modelId != null) result.setModelId(modelId);
787
+ await lifecycle.start(modelId);
720
788
  }
721
789
 
722
790
  // Open a step span lazily before the first content of each step.
@@ -726,7 +794,7 @@ export function runPrompt<
726
794
  value.type !== 'finish' &&
727
795
  value.type !== 'error'
728
796
  ) {
729
- await telemetry.ensureStepOpen();
797
+ await lifecycle.ensureStepOpen();
730
798
  }
731
799
 
732
800
  if (
@@ -774,6 +842,7 @@ export function runPrompt<
774
842
 
775
843
  if (displayValue.type === 'finish-step' && closingResumedStep) {
776
844
  closingResumedStep = false;
845
+ await publishToolExecutions();
777
846
  resetStepContent();
778
847
  result.discardCurrentStepContent();
779
848
  continue;
@@ -824,7 +893,7 @@ export function runPrompt<
824
893
  // Telemetry and stderr diagnostics keep the raw error (absolute
825
894
  // paths help debugging); the consumer-facing settle uses the
826
895
  // workDir-stripped one, like every other forwarded part.
827
- await telemetry.error(value.error);
896
+ await lifecycle.error(value.error);
828
897
  // A turn the caller itself aborted ends with an error-shaped part by
829
898
  // construction; diagnosing the caller's own signal to stderr reads
830
899
  // as a malfunction. `settleFailure` below still reports it as an
@@ -841,12 +910,16 @@ export function runPrompt<
841
910
  return;
842
911
  }
843
912
 
844
- // Forward to consumer as soon as possible.
845
- for (const part of translateStreamPart<TOOLS>(
913
+ const translatedParts = translateStreamPart<TOOLS>(
846
914
  displayValue,
847
915
  translateOptions,
848
- )) {
849
- result.enqueue(part);
916
+ );
917
+ if (value.type === 'tool-result') {
918
+ bufferedToolOutcomes.push(() => {
919
+ for (const part of translatedParts) result.enqueue(part);
920
+ });
921
+ } else {
922
+ for (const part of translatedParts) result.enqueue(part);
850
923
  }
851
924
 
852
925
  // Tool-call validation lives here (not in translateStreamPart) because
@@ -862,38 +935,48 @@ export function runPrompt<
862
935
  result.enqueue(parsed);
863
936
  }
864
937
 
865
- // Accumulate output content for telemetry / reporters.
866
938
  if (value.type === 'text-delta') {
867
939
  stepText += value.delta;
868
940
  } else if (value.type === 'reasoning-delta') {
869
941
  stepReasoning += value.delta;
870
942
  }
871
943
 
872
- // Telemetry: a tool execution begins on its `tool-call`.
873
944
  if (value.type === 'tool-call') {
874
945
  observedStepToolCallCount += 1;
875
946
  expectedStepToolCallCount ??= value.stepToolCallCount;
876
- stepToolCalls.push({
877
- type: 'tool-call',
878
- toolCallId: value.toolCallId,
879
- toolName: value.toolName,
880
- input: value.input,
881
- });
882
- await telemetry.toolStart({
883
- toolCallId: value.toolCallId,
884
- toolName: value.toolName,
885
- input: value.input,
886
- });
947
+ const toolCall = toolCallsByToolCallId.get(value.toolCallId);
948
+ if (toolCall != null) {
949
+ stepToolCalls.push(toolCall as ContentPart<TOOLS>);
950
+ toolExecutions.set(value.toolCallId, {
951
+ toolCall: toolCall as TypedToolCall<TOOLS>,
952
+ });
953
+ }
887
954
  }
888
955
 
889
- // Telemetry: close a tool span when its provider-executed result lands.
890
956
  if (value.type === 'tool-result') {
891
- await telemetry.toolEnd(
892
- value.toolCallId,
893
- value.isError
894
- ? { ok: false, error: value.result }
895
- : { ok: true, output: value.result },
896
- );
957
+ const execution = toolExecutions.get(value.toolCallId);
958
+ if (execution != null && execution.toolOutput == null) {
959
+ execution.toolOutput = value.isError
960
+ ? ({
961
+ ...execution.toolCall,
962
+ type: 'tool-error',
963
+ error: value.result,
964
+ } as TypedToolError<TOOLS>)
965
+ : ({
966
+ ...execution.toolCall,
967
+ type: 'tool-result',
968
+ output: value.result,
969
+ } as TypedToolResult<TOOLS>);
970
+ }
971
+ if (
972
+ rawToolCallsByToolCallId.get(value.toolCallId)?.providerExecuted ===
973
+ true &&
974
+ execution?.toolOutput != null
975
+ ) {
976
+ stepProviderToolResults.push(
977
+ execution.toolOutput as ContentPart<TOOLS>,
978
+ );
979
+ }
897
980
  }
898
981
 
899
982
  if (value.type === 'tool-approval-request') {
@@ -969,9 +1052,9 @@ export function runPrompt<
969
1052
  if (value.type === 'finish') {
970
1053
  await waitForOutstandingHostToolExecutions();
971
1054
  finalFinish = value;
972
- await telemetry.end({
973
- finishReason: value.finishReason,
974
- usage: value.totalUsage,
1055
+ await lifecycle.end({
1056
+ steps: completedSteps,
1057
+ usage: asLanguageModelUsage(value.totalUsage),
975
1058
  });
976
1059
  }
977
1060
 
@@ -1004,7 +1087,13 @@ export function runPrompt<
1004
1087
  toolCallId: toolCall.toolCallId,
1005
1088
  output,
1006
1089
  });
1007
- await telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1090
+ const execution = toolExecutions.get(toolCall.toolCallId);
1091
+ if (execution != null) {
1092
+ execution.toolOutput = toToolOutput({
1093
+ toolCall: parsedToolCall,
1094
+ outcome: { ok: true, output },
1095
+ });
1096
+ }
1008
1097
  continue;
1009
1098
  }
1010
1099
  if (isClientExecutedBuiltin) {
@@ -1045,7 +1134,13 @@ export function runPrompt<
1045
1134
  toolCallId: toolCall.toolCallId,
1046
1135
  output,
1047
1136
  });
1048
- await telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1137
+ const execution = toolExecutions.get(toolCall.toolCallId);
1138
+ if (execution != null) {
1139
+ execution.toolOutput = toToolOutput({
1140
+ toolCall: parsedToolCall,
1141
+ outcome: { ok: true, output },
1142
+ });
1143
+ }
1049
1144
  continue;
1050
1145
  }
1051
1146
  const pendingApproval =
@@ -1121,10 +1216,11 @@ export function runPrompt<
1121
1216
  }
1122
1217
  startHostToolExecution(
1123
1218
  (async () => {
1219
+ const executionStartedAt = Date.now();
1124
1220
  const execution = await maybeExecuteHostTool({
1125
1221
  event: toolCall,
1126
1222
  tools: activeTools,
1127
- wrappedExecuteTool: telemetry.executeTool,
1223
+ wrappedExecuteTool: lifecycle.executeTool,
1128
1224
  sandboxSession: input.sandboxSession,
1129
1225
  abortSignal: input.abortSignal,
1130
1226
  control,
@@ -1149,14 +1245,16 @@ export function runPrompt<
1149
1245
  },
1150
1246
  input.sessionWorkDir,
1151
1247
  ) as Extract<HarnessV1StreamPart, { type: 'tool-result' }>;
1152
- result.enqueue({
1153
- type: 'tool-result',
1154
- toolCallId: toolCall.toolCallId,
1155
- toolName: toolCall.toolName,
1156
- input: undefined,
1157
- output: stripped.result,
1158
- preliminary: true,
1159
- } as TextStreamPart<TOOLS>);
1248
+ bufferedToolOutcomes.push(() => {
1249
+ result.enqueue({
1250
+ type: 'tool-result',
1251
+ toolCallId: toolCall.toolCallId,
1252
+ toolName: toolCall.toolName,
1253
+ input: undefined,
1254
+ output: stripped.result,
1255
+ preliminary: true,
1256
+ } as TextStreamPart<TOOLS>);
1257
+ });
1160
1258
  },
1161
1259
  });
1162
1260
  if (!execution.executed) {
@@ -1164,7 +1262,14 @@ export function runPrompt<
1164
1262
  `Harness '${input.harness.harnessId}' could not execute host tool '${toolCall.toolName}'.`,
1165
1263
  );
1166
1264
  }
1167
- await telemetry.toolEnd(toolCall.toolCallId, execution.outcome);
1265
+ const toolExecution = toolExecutions.get(toolCall.toolCallId);
1266
+ if (toolExecution != null) {
1267
+ toolExecution.toolOutput = toToolOutput({
1268
+ toolCall: parsedToolCall,
1269
+ outcome: execution.outcome,
1270
+ });
1271
+ toolExecution.toolExecutionMs = Date.now() - executionStartedAt;
1272
+ }
1168
1273
  })(),
1169
1274
  );
1170
1275
  }
@@ -1201,7 +1306,7 @@ export function runPrompt<
1201
1306
  } catch {
1202
1307
  // Preserve the error that stopped the reader loop.
1203
1308
  }
1204
- await telemetry.error(err);
1309
+ await lifecycle.error(err);
1205
1310
  logBridgeError({
1206
1311
  harnessId: input.harness.harnessId,
1207
1312
  sessionId: input.session.sessionId,
@@ -1232,6 +1337,23 @@ type HostToolExecution =
1232
1337
  | { executed: false }
1233
1338
  | { executed: true; outcome: HostToolOutcome };
1234
1339
 
1340
+ function toToolOutput<TOOLS extends ToolSet>(input: {
1341
+ toolCall: ToolCallTextStreamPart;
1342
+ outcome: HostToolOutcome;
1343
+ }): TypedToolResult<TOOLS> | TypedToolError<TOOLS> {
1344
+ return input.outcome.ok
1345
+ ? ({
1346
+ ...input.toolCall,
1347
+ type: 'tool-result',
1348
+ output: input.outcome.output,
1349
+ } as TypedToolResult<TOOLS>)
1350
+ : ({
1351
+ ...input.toolCall,
1352
+ type: 'tool-error',
1353
+ error: input.outcome.error,
1354
+ } as TypedToolError<TOOLS>);
1355
+ }
1356
+
1235
1357
  function asToolCallTextStreamPart<TOOLS extends ToolSet>(input: {
1236
1358
  part: TextStreamPart<TOOLS>;
1237
1359
  }): ToolCallTextStreamPart {
@@ -1263,7 +1385,7 @@ function hasTool(input: { tools: ToolSet; toolName: string }): boolean {
1263
1385
  async function maybeExecuteHostTool<TOOLS extends ToolSet>(input: {
1264
1386
  event: { toolCallId: string; toolName: string; input: string };
1265
1387
  tools: TOOLS;
1266
- wrappedExecuteTool: TurnTelemetry['executeTool'];
1388
+ wrappedExecuteTool: TurnLifecycle<ToolSet, Context>['executeTool'];
1267
1389
  sandboxSession: SandboxSession;
1268
1390
  abortSignal: AbortSignal | undefined;
1269
1391
  control: HarnessV1PromptControl;