@ai-sdk/harness 1.0.42 → 1.0.44

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @ai-sdk/harness
2
2
 
3
+ ## 1.0.44
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [7bd6bdd]
8
+ - Updated dependencies [1e2f324]
9
+ - ai@7.0.38
10
+ - @ai-sdk/provider@4.0.4
11
+ - @ai-sdk/provider-utils@5.0.13
12
+
13
+ ## 1.0.43
14
+
15
+ ### Patch Changes
16
+
17
+ - a9a22e1: fix(harness): fix telemetry end events to report `final-step` text and reasoning for multi-step turns
18
+ - 9e4e816: fix(harness): avoid emitting `onTurnFinished` / `onTurnFailed` when turn is suspended mid-flight
19
+ - ea3063f: fix(harness): remove broken bridge `channel.interrupt()` layer and its usage
20
+
3
21
  ## 1.0.42
4
22
 
5
23
  ### Patch Changes
@@ -403,7 +403,7 @@ type HarnessV1ResumeSessionState = HarnessV1LifecycleStateBase & {
403
403
  /**
404
404
  * Opaque payload returned by `doSuspendTurn` and accepted by a future
405
405
  * `HarnessV1.doStart({ continueFrom })` to reconnect to the same session before
406
- * continuing the interrupted turn.
406
+ * continuing the suspended turn.
407
407
  */
408
408
  type HarnessV1ContinueTurnState = HarnessV1LifecycleStateBase & {
409
409
  readonly type: 'continue-turn';
@@ -783,7 +783,7 @@ type HarnessV1Session = {
783
783
  /**
784
784
  * Continue the in-flight turn **without a new user prompt**, returning the
785
785
  * same control surface as `doPromptTurn`. Used to keep consuming a turn that
786
- * was interrupted at a process boundary (the workflow slice loop), after the
786
+ * was suspended at a process boundary (the workflow slice loop), after the
787
787
  * session itself has been resumed via `doStart({ continueFrom })`:
788
788
  *
789
789
  * - When the runtime's turn is still live and reachable (bridge `attach` /
@@ -792,7 +792,7 @@ type HarnessV1Session = {
792
792
  * - When the live turn is gone (bridge respawned `rerun`, or a host-resident
793
793
  * runtime like Pi whose turn cannot survive its process), the adapter
794
794
  * re-drives the runtime's own thread from its persisted state. Lossy: work
795
- * in flight at the interruption is recomputed.
795
+ * in flight at the suspension is recomputed.
796
796
  *
797
797
  * Required on every adapter. The behaviour an adapter can guarantee follows
798
798
  * from its architecture; the contract is uniform.
@@ -915,7 +915,7 @@ type HarnessV1<TBuiltinTools extends ToolSet = ToolSet> = {
915
915
  }) => PromiseLike<HarnessV1Bootstrap>;
916
916
  /**
917
917
  * Start a fresh session, resume a parked session via `resumeFrom`, or resume
918
- * an interrupted turn via `continueFrom`. The host then issues prompts against
918
+ * a suspended turn via `continueFrom`. The host then issues prompts against
919
919
  * the returned session, ending with `doDetach`, `doStop`, or `doDestroy`.
920
920
  */
921
921
  doStart(options: HarnessV1StartOptions): PromiseLike<HarnessV1Session>;
@@ -937,6 +937,42 @@ var NOOP = {
937
937
  async error() {
938
938
  }
939
939
  };
940
+ function normalizeFinishReason(finishReason) {
941
+ if (finishReason != null && typeof finishReason === "object" && "unified" in finishReason) {
942
+ return finishReason.unified;
943
+ }
944
+ return finishReason;
945
+ }
946
+ function addTokenCounts(tokenCount1, tokenCount2) {
947
+ return tokenCount1 == null && tokenCount2 == null ? void 0 : (tokenCount1 != null ? tokenCount1 : 0) + (tokenCount2 != null ? tokenCount2 : 0);
948
+ }
949
+ function normalizeUsage(usage) {
950
+ if (usage == null || typeof usage !== "object" || !("inputTokens" in usage) || !("outputTokens" in usage)) {
951
+ return usage;
952
+ }
953
+ const inputTokens = usage.inputTokens;
954
+ const outputTokens = usage.outputTokens;
955
+ if (inputTokens == null || typeof inputTokens !== "object" || outputTokens == null || typeof outputTokens !== "object") {
956
+ return usage;
957
+ }
958
+ const input = inputTokens;
959
+ const output = outputTokens;
960
+ return {
961
+ inputTokens: input.total,
962
+ inputTokenDetails: {
963
+ noCacheTokens: input.noCache,
964
+ cacheReadTokens: input.cacheRead,
965
+ cacheWriteTokens: input.cacheWrite
966
+ },
967
+ outputTokens: output.total,
968
+ outputTokenDetails: {
969
+ textTokens: output.text,
970
+ reasoningTokens: output.reasoning
971
+ },
972
+ totalTokens: addTokenCounts(input.total, output.total),
973
+ raw: usage.raw
974
+ };
975
+ }
940
976
  function createTurnTelemetry(opts) {
941
977
  var _a3;
942
978
  if (opts.telemetry == null) return NOOP;
@@ -952,6 +988,10 @@ function createTurnTelemetry(opts) {
952
988
  let stepOpen = false;
953
989
  let stepNumber = 0;
954
990
  let ended = false;
991
+ let finalStepText = "";
992
+ let finalStepReasoning = [];
993
+ let finalStepProviderMetadata;
994
+ let outputToolCalls = [];
955
995
  const openTools = /* @__PURE__ */ new Map();
956
996
  const cast = (event) => event;
957
997
  const fireStart = async () => {
@@ -1020,17 +1060,37 @@ function createTurnTelemetry(opts) {
1020
1060
  };
1021
1061
  const inferenceEnd = async (info) => {
1022
1062
  var _a4;
1063
+ const finishReason = normalizeFinishReason(info.finishReason);
1064
+ const usage = normalizeUsage(info.usage);
1023
1065
  await ((_a4 = dispatcher.onLanguageModelCallEnd) == null ? void 0 : _a4.call(
1024
1066
  dispatcher,
1025
1067
  cast({
1026
1068
  callId,
1027
- finishReason: info.finishReason,
1069
+ finishReason,
1028
1070
  responseId: callId,
1029
- usage: info.usage,
1030
- content: info.content
1071
+ usage,
1072
+ content: info.content,
1073
+ performance: {
1074
+ responseTimeMs: void 0,
1075
+ timeToFirstOutputMs: void 0,
1076
+ timeBetweenOutputChunksMs: void 0
1077
+ }
1031
1078
  })
1032
1079
  ));
1033
1080
  };
1081
+ const recordOutputContent = (content) => {
1082
+ finalStepText = "";
1083
+ finalStepReasoning = [];
1084
+ for (const part of content) {
1085
+ if (part.type === "text") {
1086
+ finalStepText += part.text;
1087
+ } else if (part.type === "reasoning") {
1088
+ finalStepReasoning.push({ text: part.text });
1089
+ } else if (part.type === "tool-call") {
1090
+ outputToolCalls.push(part);
1091
+ }
1092
+ }
1093
+ };
1034
1094
  const closeOpenTools = async () => {
1035
1095
  var _a4;
1036
1096
  for (const call of openTools.values()) {
@@ -1061,10 +1121,14 @@ function createTurnTelemetry(opts) {
1061
1121
  var _a4, _b3;
1062
1122
  if (!stepOpen) return;
1063
1123
  const content = (_a4 = info.content) != null ? _a4 : [];
1124
+ const finishReason = normalizeFinishReason(info.finishReason);
1125
+ const usage = normalizeUsage(info.usage);
1126
+ recordOutputContent(content);
1127
+ finalStepProviderMetadata = info.providerMetadata;
1064
1128
  await closeOpenTools();
1065
1129
  await inferenceEnd({
1066
- finishReason: info.finishReason,
1067
- usage: info.usage,
1130
+ finishReason,
1131
+ usage,
1068
1132
  content
1069
1133
  });
1070
1134
  await ((_b3 = dispatcher.onStepEnd) == null ? void 0 : _b3.call(
@@ -1072,8 +1136,8 @@ function createTurnTelemetry(opts) {
1072
1136
  cast({
1073
1137
  callId,
1074
1138
  stepNumber,
1075
- finishReason: info.finishReason,
1076
- usage: info.usage,
1139
+ finishReason,
1140
+ usage,
1077
1141
  providerMetadata: info.providerMetadata,
1078
1142
  content,
1079
1143
  response: {
@@ -1115,6 +1179,7 @@ function createTurnTelemetry(opts) {
1115
1179
  async toolEnd(toolCallId, output) {
1116
1180
  var _a4;
1117
1181
  const call = openTools.get(toolCallId);
1182
+ const normalizedOutput = output.ok ? { type: "tool-result", output: output.output } : { type: "error", error: output.error };
1118
1183
  if (call == null) return;
1119
1184
  openTools.delete(toolCallId);
1120
1185
  await ((_a4 = dispatcher.onToolExecutionEnd) == null ? void 0 : _a4.call(
@@ -1131,19 +1196,21 @@ function createTurnTelemetry(opts) {
1131
1196
  dynamic: true
1132
1197
  },
1133
1198
  toolContext: void 0,
1134
- toolOutput: output.ok ? { type: "tool-result", output: output.output } : { type: "error", error: output.error }
1199
+ toolOutput: normalizedOutput
1135
1200
  })
1136
1201
  ));
1137
1202
  },
1138
1203
  async end(info) {
1139
1204
  var _a4, _b3;
1140
1205
  if (ended) return;
1206
+ const finishReason = normalizeFinishReason(info.finishReason);
1207
+ const usage = normalizeUsage(info.usage);
1141
1208
  if (!started) await fireStart();
1142
1209
  if (stepOpen) {
1143
1210
  await closeOpenTools();
1144
1211
  await inferenceEnd({
1145
- finishReason: info.finishReason,
1146
- usage: info.usage,
1212
+ finishReason,
1213
+ usage,
1147
1214
  content: []
1148
1215
  });
1149
1216
  await ((_a4 = dispatcher.onStepEnd) == null ? void 0 : _a4.call(
@@ -1151,8 +1218,8 @@ function createTurnTelemetry(opts) {
1151
1218
  cast({
1152
1219
  callId,
1153
1220
  stepNumber,
1154
- finishReason: info.finishReason,
1155
- usage: info.usage,
1221
+ finishReason,
1222
+ usage,
1156
1223
  providerMetadata: void 0,
1157
1224
  content: [],
1158
1225
  response: {
@@ -1171,10 +1238,17 @@ function createTurnTelemetry(opts) {
1171
1238
  cast({
1172
1239
  callId,
1173
1240
  operationId: "ai.harness",
1174
- finishReason: info.finishReason,
1175
- usage: info.usage,
1176
- totalUsage: info.usage,
1241
+ finishReason,
1242
+ usage,
1243
+ totalUsage: usage,
1177
1244
  content: [],
1245
+ text: finalStepText,
1246
+ finalStep: {
1247
+ reasoning: finalStepReasoning,
1248
+ providerMetadata: finalStepProviderMetadata
1249
+ },
1250
+ toolCalls: outputToolCalls,
1251
+ files: [],
1178
1252
  steps: new Array(stepNumber),
1179
1253
  response: {
1180
1254
  id: callId,
@@ -1317,9 +1391,11 @@ function runPrompt(input) {
1317
1391
  runtimeContext: input.runtimeContext
1318
1392
  });
1319
1393
  const settleFailure = (err) => {
1320
- var _a4, _b4;
1321
- (_a4 = input.onTurnFailed) == null ? void 0 : _a4.call(input);
1322
- if ((_b4 = input.abortSignal) == null ? void 0 : _b4.aborted) {
1394
+ var _a4, _b4, _c2;
1395
+ if (!((_a4 = input.isTurnSuspending) == null ? void 0 : _a4.call(input))) {
1396
+ (_b4 = input.onTurnFailed) == null ? void 0 : _b4.call(input);
1397
+ }
1398
+ if ((_c2 = input.abortSignal) == null ? void 0 : _c2.aborted) {
1323
1399
  result.abort({
1324
1400
  error: err,
1325
1401
  ...input.abortSignal.reason !== void 0 ? { reason: getErrorMessage(input.abortSignal.reason) } : {}
@@ -1329,7 +1405,7 @@ function runPrompt(input) {
1329
1405
  result.fail(err);
1330
1406
  };
1331
1407
  const done = (async () => {
1332
- var _a4, _b4, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k;
1408
+ var _a4, _b4, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l;
1333
1409
  let bridge;
1334
1410
  try {
1335
1411
  bridge = await toHarnessStream({
@@ -1932,10 +2008,15 @@ function runPrompt(input) {
1932
2008
  await telemetry.toolEnd(toolCall.toolCallId, execution.outcome);
1933
2009
  }
1934
2010
  }
1935
- if (finalFinish != null) {
1936
- (_j = input.onTurnFinished) == null ? void 0 : _j.call(input);
2011
+ const isTurnSuspending = ((_j = input.isTurnSuspending) == null ? void 0 : _j.call(input)) === true;
2012
+ if (isTurnSuspending) {
2013
+ if (finalFinish == null) {
2014
+ result.discardCurrentStepContent();
2015
+ }
2016
+ } else if (finalFinish != null) {
2017
+ (_k = input.onTurnFinished) == null ? void 0 : _k.call(input);
1937
2018
  } else {
1938
- (_k = input.onTurnFailed) == null ? void 0 : _k.call(input);
2019
+ (_l = input.onTurnFailed) == null ? void 0 : _l.call(input);
1939
2020
  }
1940
2021
  await result.finish(
1941
2022
  finalFinish ? {
@@ -2146,6 +2227,7 @@ var HarnessAgentSession = class {
2146
2227
  onTurnFailed: () => {
2147
2228
  this.finishTrackedTurn({ turnId });
2148
2229
  },
2230
+ isTurnSuspending: () => this.activeTurnSequence === turnId && this.suspendedTurnState != null,
2149
2231
  onStopConditionMet: () => this.captureStopConditionBoundary({ session, turnId })
2150
2232
  });
2151
2233
  return turn;
@@ -2200,6 +2282,7 @@ var HarnessAgentSession = class {
2200
2282
  onTurnFailed: () => {
2201
2283
  this.finishTrackedTurn({ turnId });
2202
2284
  },
2285
+ isTurnSuspending: () => this.activeTurnSequence === turnId && this.suspendedTurnState != null,
2203
2286
  onStopConditionMet: () => this.captureStopConditionBoundary({ session, turnId })
2204
2287
  });
2205
2288
  return turn;