@ai-sdk/harness 1.0.35 → 1.0.37

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.
@@ -95,6 +95,11 @@ async function validateLifecycleStateData(input) {
95
95
  message: "Resume session state cannot contain pending tool approvals; unfinished turns must be stored as `continueFrom`."
96
96
  });
97
97
  }
98
+ if (state.type === "resume-session" && "pendingToolResults" in state && state.pendingToolResults !== void 0) {
99
+ throw new HarnessError({
100
+ message: "Resume session state cannot contain pending tool results; unfinished turns must be stored as `continueFrom`."
101
+ });
102
+ }
98
103
  const data = harness.lifecycleStateSchema == null ? state.data : await (async () => {
99
104
  const result = await safeValidateTypes({
100
105
  value: state.data,
@@ -127,7 +132,8 @@ async function validateLifecycleStateData(input) {
127
132
  harnessId: state.harnessId,
128
133
  specificationVersion: state.specificationVersion,
129
134
  data,
130
- ...state.pendingToolApprovals !== void 0 ? { pendingToolApprovals: state.pendingToolApprovals } : {}
135
+ ...state.pendingToolApprovals !== void 0 ? { pendingToolApprovals: state.pendingToolApprovals } : {},
136
+ ...state.pendingToolResults !== void 0 ? { pendingToolResults: state.pendingToolResults } : {}
131
137
  };
132
138
  }
133
139
 
@@ -227,6 +233,7 @@ var HarnessStreamTextResult = class {
227
233
  this.currentStepContent = [];
228
234
  this.currentStepWarnings = [];
229
235
  this.stepNumber = 0;
236
+ this.stepStarted = false;
230
237
  // Accumulators that span the whole turn.
231
238
  this.accumulatedUsage = createNullLanguageModelUsage();
232
239
  this.finalProviderMetadata = void 0;
@@ -243,6 +250,7 @@ var HarnessStreamTextResult = class {
243
250
  const baseStream = new ReadableStream({
244
251
  start(c) {
245
252
  controllerRef = c;
253
+ c.enqueue({ type: "start" });
246
254
  }
247
255
  });
248
256
  this.fullStreamController = controllerRef;
@@ -265,9 +273,27 @@ var HarnessStreamTextResult = class {
265
273
  * into the current step's content array where applicable.
266
274
  */
267
275
  enqueue(part) {
276
+ this.startStep();
268
277
  this.fullStreamController.enqueue(part);
269
278
  this.appendToCurrentStepContent(part);
270
279
  }
280
+ /**
281
+ * Push a continuation input into the consumer stream without attributing it
282
+ * to the next model step. Approval responses and client tool results arrive
283
+ * between model calls and therefore must not create or alter a StepResult.
284
+ */
285
+ enqueueContinuation(part) {
286
+ this.fullStreamController.enqueue(part);
287
+ }
288
+ /**
289
+ * Drop content replayed while a suspended host-input pause closes its
290
+ * already-recorded model step.
291
+ */
292
+ discardCurrentStepContent() {
293
+ this.currentStepContent = [];
294
+ this.currentStepWarnings = [];
295
+ this.stepStarted = false;
296
+ }
271
297
  /**
272
298
  * Mark the end of a step. Builds a `StepResult` from the accumulated
273
299
  * content and records it in the steps array. Accepts the V4-shaped
@@ -275,6 +301,7 @@ var HarnessStreamTextResult = class {
275
301
  * flat shape internally.
276
302
  */
277
303
  finishStep(input) {
304
+ this.startStep();
278
305
  const normalizedUsage = asLanguageModelUsage(input.usage);
279
306
  const finishReason = input.finishReason.unified;
280
307
  const rawFinishReason = input.finishReason.raw;
@@ -322,46 +349,38 @@ var HarnessStreamTextResult = class {
322
349
  this.stepNumber += 1;
323
350
  this.currentStepContent = [];
324
351
  this.currentStepWarnings = [];
352
+ this.stepStarted = false;
353
+ return step;
354
+ }
355
+ startStep() {
356
+ if (this.stepStarted) return;
357
+ this.stepStarted = true;
358
+ this.fullStreamController.enqueue({
359
+ type: "start-step",
360
+ request: {},
361
+ warnings: this.currentStepWarnings
362
+ });
325
363
  }
326
364
  /**
327
365
  * Resolve every delayed promise and close `fullStream`. Idempotent.
328
366
  */
329
367
  async finish(input) {
330
368
  if (this.settled) return;
331
- this.settled = true;
369
+ if (this.currentStepContent.length > 0) {
370
+ this.fail(
371
+ new Error(
372
+ "HarnessAgent: received terminal finish with unclosed step content. Harness adapters must emit `finish-step` before `finish`."
373
+ )
374
+ );
375
+ return;
376
+ }
332
377
  if (input != null) {
333
378
  this.finalFinishReason = input.finishReason.unified;
334
379
  this.finalRawFinishReason = input.finishReason.raw;
335
380
  this.finalProviderMetadata = input.providerMetadata;
336
381
  this.accumulatedUsage = asLanguageModelUsage(input.totalUsage);
337
382
  }
338
- if (this.currentStepContent.length > 0) {
339
- const trailingStep = new DefaultStepResult({
340
- callId: generateId(),
341
- stepNumber: this.stepNumber,
342
- provider: this.providerName,
343
- modelId: this.modelId,
344
- runtimeContext: this.runtimeContext,
345
- toolsContext: this.toolsContext,
346
- content: this.currentStepContent,
347
- finishReason: this.finalFinishReason,
348
- rawFinishReason: this.finalRawFinishReason,
349
- usage: createNullLanguageModelUsage(),
350
- performance: createEmptyPerformance(),
351
- warnings: this.currentStepWarnings.length > 0 ? this.currentStepWarnings : void 0,
352
- request: {},
353
- response: {
354
- id: generateId(),
355
- timestamp: /* @__PURE__ */ new Date(),
356
- modelId: this.modelId,
357
- messages: []
358
- },
359
- providerMetadata: this.finalProviderMetadata
360
- });
361
- this.stepsBuffer.push(trailingStep);
362
- this.currentStepContent = [];
363
- this.currentStepWarnings = [];
364
- }
383
+ this.settled = true;
365
384
  const finalStep = this.stepsBuffer.length > 0 ? this.stepsBuffer[this.stepsBuffer.length - 1] : new DefaultStepResult({
366
385
  callId: generateId(),
367
386
  stepNumber: 0,
@@ -1218,7 +1237,7 @@ function logBridgeError({
1218
1237
 
1219
1238
  // src/agent/internal/run-prompt.ts
1220
1239
  function runPrompt(input) {
1221
- var _a3, _b3, _c, _d;
1240
+ var _a3, _b3, _c, _d, _e, _f, _g;
1222
1241
  const result = new HarnessStreamTextResult({
1223
1242
  tools: input.tools,
1224
1243
  runtimeContext: input.runtimeContext,
@@ -1228,11 +1247,16 @@ function runPrompt(input) {
1228
1247
  sessionId: input.session.sessionId
1229
1248
  });
1230
1249
  const pendingToolApprovals = (_a3 = input.pendingToolApprovals) != null ? _a3 : [];
1231
- const onPendingToolApproval = (_b3 = input.onPendingToolApproval) != null ? _b3 : (() => {
1250
+ const pendingToolResults = (_b3 = input.pendingToolResults) != null ? _b3 : [];
1251
+ const onPendingToolApproval = (_c = input.onPendingToolApproval) != null ? _c : (() => {
1252
+ });
1253
+ const onToolApprovalSettled = (_d = input.onToolApprovalSettled) != null ? _d : (() => {
1254
+ });
1255
+ const onPendingToolResult = (_e = input.onPendingToolResult) != null ? _e : (() => {
1232
1256
  });
1233
- const onToolApprovalSettled = (_c = input.onToolApprovalSettled) != null ? _c : (() => {
1257
+ const onToolResultSettled = (_f = input.onToolResultSettled) != null ? _f : (() => {
1234
1258
  });
1235
- const activeTools = (_d = input.activeTools) != null ? _d : input.tools;
1259
+ const activeTools = (_g = input.activeTools) != null ? _g : input.tools;
1236
1260
  const telemetry = createTurnTelemetry({
1237
1261
  telemetry: input.telemetry,
1238
1262
  harnessId: input.harness.harnessId,
@@ -1242,7 +1266,7 @@ function runPrompt(input) {
1242
1266
  runtimeContext: input.runtimeContext
1243
1267
  });
1244
1268
  const done = (async () => {
1245
- var _a4, _b4, _c2, _d2, _e, _f, _g, _h;
1269
+ var _a4, _b4, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l, _m;
1246
1270
  let bridge;
1247
1271
  try {
1248
1272
  bridge = await toHarnessStream({
@@ -1273,6 +1297,7 @@ function runPrompt(input) {
1273
1297
  context: "failed to start harness turn",
1274
1298
  error: err
1275
1299
  });
1300
+ (_a4 = input.onTurnFailed) == null ? void 0 : _a4.call(input);
1276
1301
  result.fail(err);
1277
1302
  return;
1278
1303
  }
@@ -1287,12 +1312,25 @@ function runPrompt(input) {
1287
1312
  pendingToolApprovals.map((approval) => [approval.toolCallId, approval])
1288
1313
  );
1289
1314
  const continuationsByApprovalId = new Map(
1290
- ((_a4 = input.toolApprovalContinuations) != null ? _a4 : []).map((continuation) => [
1315
+ ((_b4 = input.toolApprovalContinuations) != null ? _b4 : []).map((continuation) => [
1291
1316
  continuation.approvalResponse.approvalId,
1292
1317
  continuation
1293
1318
  ])
1294
1319
  );
1295
- const settledApprovalToolCallIds = /* @__PURE__ */ new Set();
1320
+ const pendingResultsByToolCallId = new Map(
1321
+ pendingToolResults.map((pendingResult) => [
1322
+ pendingResult.toolCallId,
1323
+ pendingResult
1324
+ ])
1325
+ );
1326
+ const continuationsByToolCallId = new Map(
1327
+ ((_c2 = input.toolResultContinuations) != null ? _c2 : []).map((continuation) => [
1328
+ continuation.toolCallId,
1329
+ continuation
1330
+ ])
1331
+ );
1332
+ const settledHostToolCallIds = /* @__PURE__ */ new Set();
1333
+ let closingResumedStep = false;
1296
1334
  let finalFinish;
1297
1335
  let stepText = "";
1298
1336
  let stepReasoning = "";
@@ -1326,19 +1364,29 @@ function runPrompt(input) {
1326
1364
  unified: "tool-calls",
1327
1365
  raw: void 0
1328
1366
  };
1329
- const finishForToolApprovalPause = async () => {
1367
+ const completeStep = (input2) => {
1330
1368
  telemetry.stepFinish({
1331
- finishReason: toolCallsFinishReason,
1332
- usage: zeroUsage,
1369
+ finishReason: input2.finishReason,
1370
+ usage: input2.usage,
1371
+ providerMetadata: input2.providerMetadata,
1333
1372
  content: buildStepContent()
1334
1373
  });
1335
1374
  resetStepContent();
1336
- result.finishStep({
1337
- finishReason: toolCallsFinishReason,
1338
- usage: zeroUsage,
1339
- providerMetadata: void 0,
1375
+ return result.finishStep({
1376
+ finishReason: input2.finishReason,
1377
+ usage: input2.usage,
1378
+ providerMetadata: input2.providerMetadata,
1340
1379
  warnings: []
1341
1380
  });
1381
+ };
1382
+ const finishForHostInputPause = async (options) => {
1383
+ if (options.completeCurrentStep) {
1384
+ completeStep({
1385
+ finishReason: toolCallsFinishReason,
1386
+ usage: zeroUsage,
1387
+ providerMetadata: void 0
1388
+ });
1389
+ }
1342
1390
  telemetry.end({
1343
1391
  finishReason: toolCallsFinishReason,
1344
1392
  usage: zeroUsage
@@ -1364,7 +1412,7 @@ function runPrompt(input) {
1364
1412
  });
1365
1413
  };
1366
1414
  const enqueueApprovalResponse = (approval, continuation) => {
1367
- result.enqueue({
1415
+ result.enqueueContinuation({
1368
1416
  type: "tool-approval-response",
1369
1417
  approvalId: approval.approvalId,
1370
1418
  toolCall: continuation.toolCall,
@@ -1373,13 +1421,34 @@ function runPrompt(input) {
1373
1421
  ...approval.providerExecuted !== void 0 ? { providerExecuted: approval.providerExecuted } : {}
1374
1422
  });
1375
1423
  };
1424
+ const recordPendingToolResult = (options) => {
1425
+ var _a5;
1426
+ const pendingResult = (_a5 = pendingResultsByToolCallId.get(options.toolCall.toolCallId)) != null ? _a5 : {
1427
+ toolCallId: options.toolCall.toolCallId,
1428
+ toolName: options.toolCall.toolName,
1429
+ input: options.toolCall.input
1430
+ };
1431
+ pendingResultsByToolCallId.set(pendingResult.toolCallId, pendingResult);
1432
+ onPendingToolResult(pendingResult);
1433
+ return pendingResult;
1434
+ };
1435
+ const processPendingToolResultContinuation = async (pendingResult, continuation) => {
1436
+ onToolResultSettled(pendingResult.toolCallId);
1437
+ pendingResultsByToolCallId.delete(pendingResult.toolCallId);
1438
+ settledHostToolCallIds.add(pendingResult.toolCallId);
1439
+ await control.submitToolResult({
1440
+ toolCallId: pendingResult.toolCallId,
1441
+ output: continuation.output,
1442
+ isError: continuation.isError
1443
+ });
1444
+ };
1376
1445
  const processPendingApprovalContinuation = async (approval, continuation) => {
1377
1446
  var _a5;
1378
1447
  enqueueApprovalResponse(approval, continuation);
1379
1448
  onToolApprovalSettled(approval.approvalId);
1380
1449
  pendingApprovalsByApprovalId.delete(approval.approvalId);
1381
1450
  pendingApprovalsByToolCallId.delete(approval.toolCallId);
1382
- settledApprovalToolCallIds.add(approval.toolCallId);
1451
+ settledHostToolCallIds.add(approval.toolCallId);
1383
1452
  if (approval.kind === "builtin") {
1384
1453
  if (control.submitToolApproval == null) {
1385
1454
  throw new Error(
@@ -1391,7 +1460,7 @@ function runPrompt(input) {
1391
1460
  approved: continuation.approvalResponse.approved,
1392
1461
  reason: continuation.approvalResponse.reason
1393
1462
  });
1394
- return;
1463
+ return "continued";
1395
1464
  }
1396
1465
  if (!continuation.approvalResponse.approved) {
1397
1466
  await control.submitToolResult({
@@ -1401,7 +1470,7 @@ function runPrompt(input) {
1401
1470
  reason: continuation.approvalResponse.reason
1402
1471
  }
1403
1472
  });
1404
- return;
1473
+ return "continued";
1405
1474
  }
1406
1475
  const rawToolCall = (_a5 = rawToolCallsByToolCallId.get(approval.toolCallId)) != null ? _a5 : {
1407
1476
  type: "tool-call",
@@ -1409,7 +1478,7 @@ function runPrompt(input) {
1409
1478
  toolName: approval.toolName,
1410
1479
  input: approval.input
1411
1480
  };
1412
- const outcome = await maybeExecuteHostTool({
1481
+ const execution = await maybeExecuteHostTool({
1413
1482
  event: rawToolCall,
1414
1483
  tools: activeTools,
1415
1484
  sandboxSession: input.sandboxSession,
@@ -1435,13 +1504,36 @@ function runPrompt(input) {
1435
1504
  });
1436
1505
  }
1437
1506
  });
1438
- telemetry.toolEnd(rawToolCall.toolCallId, outcome);
1507
+ if (!execution.executed) {
1508
+ recordPendingToolResult({ toolCall: rawToolCall });
1509
+ await finishForHostInputPause({ completeCurrentStep: false });
1510
+ return "awaiting-tool-result";
1511
+ }
1512
+ telemetry.toolEnd(rawToolCall.toolCallId, execution.outcome);
1513
+ return "continued";
1439
1514
  };
1440
1515
  try {
1441
1516
  for (const approval of pendingToolApprovals) {
1442
1517
  const continuation = continuationsByApprovalId.get(approval.approvalId);
1443
1518
  if (continuation != null) {
1444
- await processPendingApprovalContinuation(approval, continuation);
1519
+ const outcome = await processPendingApprovalContinuation(
1520
+ approval,
1521
+ continuation
1522
+ );
1523
+ if (outcome === "awaiting-tool-result") return;
1524
+ closingResumedStep = true;
1525
+ }
1526
+ }
1527
+ for (const pendingResult of pendingToolResults) {
1528
+ const continuation = continuationsByToolCallId.get(
1529
+ pendingResult.toolCallId
1530
+ );
1531
+ if (continuation != null) {
1532
+ await processPendingToolResultContinuation(
1533
+ pendingResult,
1534
+ continuation
1535
+ );
1536
+ closingResumedStep = true;
1445
1537
  }
1446
1538
  }
1447
1539
  while (true) {
@@ -1449,14 +1541,20 @@ function runPrompt(input) {
1449
1541
  if (done2) break;
1450
1542
  if (value == null) continue;
1451
1543
  if (value.type === "stream-start") {
1452
- telemetry.start((_b4 = value.modelId) != null ? _b4 : input.session.modelId);
1544
+ telemetry.start((_d2 = value.modelId) != null ? _d2 : input.session.modelId);
1453
1545
  }
1454
1546
  if (value.type !== "stream-start" && value.type !== "finish-step" && value.type !== "finish" && value.type !== "error") {
1455
1547
  telemetry.ensureStepOpen();
1456
1548
  }
1457
1549
  const displayValue = stripWorkDir(value, input.sessionWorkDir);
1458
- const settledApprovalToolCallReplay = displayValue.type === "tool-call" && !displayValue.providerExecuted && settledApprovalToolCallIds.has(displayValue.toolCallId);
1459
- if (settledApprovalToolCallReplay) {
1550
+ const settledHostInputReplay = (displayValue.type === "tool-call" || displayValue.type === "tool-result" || displayValue.type === "tool-approval-request") && settledHostToolCallIds.has(displayValue.toolCallId);
1551
+ if (settledHostInputReplay) {
1552
+ continue;
1553
+ }
1554
+ if (displayValue.type === "finish-step" && closingResumedStep) {
1555
+ closingResumedStep = false;
1556
+ resetStepContent();
1557
+ result.discardCurrentStepContent();
1460
1558
  continue;
1461
1559
  }
1462
1560
  if (displayValue.type === "tool-approval-request") {
@@ -1469,7 +1567,7 @@ function runPrompt(input) {
1469
1567
  const rawToolCall = rawToolCallsByToolCallId.get(
1470
1568
  displayValue.toolCallId
1471
1569
  );
1472
- const toolName = (_c2 = rawToolCall == null ? void 0 : rawToolCall.toolName) != null ? _c2 : toolCall.toolName;
1570
+ const toolName = (_e2 = rawToolCall == null ? void 0 : rawToolCall.toolName) != null ? _e2 : toolCall.toolName;
1473
1571
  if (!isHarnessV1BuiltinToolIncluded({
1474
1572
  toolName,
1475
1573
  toolFiltering: input.builtinToolFiltering
@@ -1534,13 +1632,13 @@ function runPrompt(input) {
1534
1632
  );
1535
1633
  }
1536
1634
  const rawToolCall = rawToolCallsByToolCallId.get(value.toolCallId);
1537
- const pendingApproval = (_f = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _f : {
1635
+ const pendingApproval = (_h = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _h : {
1538
1636
  approvalId: value.approvalId,
1539
1637
  toolCallId: value.toolCallId,
1540
1638
  toolName: toolCall.toolName,
1541
- input: (_d2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _d2 : JSON.stringify(toolCall.input),
1639
+ input: (_f2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _f2 : JSON.stringify(toolCall.input),
1542
1640
  kind: "builtin",
1543
- providerExecuted: (_e = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _e : true,
1641
+ providerExecuted: (_g2 = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _g2 : true,
1544
1642
  ...(rawToolCall == null ? void 0 : rawToolCall.nativeName) !== void 0 ? { nativeName: rawToolCall.nativeName } : {}
1545
1643
  };
1546
1644
  pendingApprovalsByApprovalId.set(
@@ -1555,10 +1653,12 @@ function runPrompt(input) {
1555
1653
  pendingApproval.approvalId
1556
1654
  );
1557
1655
  if (continuation != null) {
1558
- await processPendingApprovalContinuation(
1656
+ const outcome = await processPendingApprovalContinuation(
1559
1657
  pendingApproval,
1560
1658
  continuation
1561
1659
  );
1660
+ if (outcome === "awaiting-tool-result") return;
1661
+ closingResumedStep = true;
1562
1662
  continue;
1563
1663
  }
1564
1664
  onPendingToolApproval(pendingApproval);
@@ -1566,22 +1666,14 @@ function runPrompt(input) {
1566
1666
  approvalId: pendingApproval.approvalId,
1567
1667
  toolCall
1568
1668
  });
1569
- await finishForToolApprovalPause();
1669
+ await finishForHostInputPause({ completeCurrentStep: true });
1570
1670
  return;
1571
1671
  }
1572
1672
  if (value.type === "finish-step") {
1573
- telemetry.stepFinish({
1673
+ completeStep({
1574
1674
  finishReason: value.finishReason,
1575
1675
  usage: value.usage,
1576
- providerMetadata: value.harnessMetadata,
1577
- content: buildStepContent()
1578
- });
1579
- resetStepContent();
1580
- result.finishStep({
1581
- finishReason: value.finishReason,
1582
- usage: value.usage,
1583
- providerMetadata: value.harnessMetadata,
1584
- warnings: []
1676
+ providerMetadata: value.harnessMetadata
1585
1677
  });
1586
1678
  }
1587
1679
  if (value.type === "finish") {
@@ -1642,7 +1734,7 @@ function runPrompt(input) {
1642
1734
  telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1643
1735
  continue;
1644
1736
  }
1645
- const pendingApproval = (_g = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _g : customToolApprovalDecision.type === "request" ? {
1737
+ const pendingApproval = (_i = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _i : customToolApprovalDecision.type === "request" ? {
1646
1738
  approvalId: generateId4(),
1647
1739
  toolCallId: toolCall.toolCallId,
1648
1740
  toolName: toolCall.toolName,
@@ -1664,10 +1756,12 @@ function runPrompt(input) {
1664
1756
  pendingApproval.approvalId
1665
1757
  );
1666
1758
  if (continuation != null) {
1667
- await processPendingApprovalContinuation(
1759
+ const outcome = await processPendingApprovalContinuation(
1668
1760
  pendingApproval,
1669
1761
  continuation
1670
1762
  );
1763
+ if (outcome === "awaiting-tool-result") return;
1764
+ closingResumedStep = true;
1671
1765
  continue;
1672
1766
  }
1673
1767
  const pendingParsedToolCall = toolCallsByToolCallId.get(
@@ -1683,10 +1777,10 @@ function runPrompt(input) {
1683
1777
  approvalId: pendingApproval.approvalId,
1684
1778
  toolCall: pendingParsedToolCall
1685
1779
  });
1686
- await finishForToolApprovalPause();
1780
+ await finishForHostInputPause({ completeCurrentStep: true });
1687
1781
  return;
1688
1782
  }
1689
- const outcome = await maybeExecuteHostTool({
1783
+ const execution = await maybeExecuteHostTool({
1690
1784
  event: toolCall,
1691
1785
  tools: activeTools,
1692
1786
  sandboxSession: input.sandboxSession,
@@ -1712,7 +1806,12 @@ function runPrompt(input) {
1712
1806
  });
1713
1807
  }
1714
1808
  });
1715
- telemetry.toolEnd(toolCall.toolCallId, outcome);
1809
+ if (!execution.executed) {
1810
+ recordPendingToolResult({ toolCall });
1811
+ await finishForHostInputPause({ completeCurrentStep: true });
1812
+ return;
1813
+ }
1814
+ telemetry.toolEnd(toolCall.toolCallId, execution.outcome);
1716
1815
  }
1717
1816
  if (value.type === "error") {
1718
1817
  telemetry.error(value.error);
@@ -1722,11 +1821,16 @@ function runPrompt(input) {
1722
1821
  context: "harness stream error",
1723
1822
  error: value.error
1724
1823
  });
1824
+ (_j = input.onTurnFailed) == null ? void 0 : _j.call(input);
1725
1825
  result.fail(value.error);
1726
1826
  return;
1727
1827
  }
1728
1828
  }
1729
- (_h = input.onTurnFinished) == null ? void 0 : _h.call(input);
1829
+ if (finalFinish != null) {
1830
+ (_k = input.onTurnFinished) == null ? void 0 : _k.call(input);
1831
+ } else {
1832
+ (_l = input.onTurnFailed) == null ? void 0 : _l.call(input);
1833
+ }
1730
1834
  await result.finish(
1731
1835
  finalFinish ? {
1732
1836
  finishReason: finalFinish.finishReason,
@@ -1742,6 +1846,7 @@ function runPrompt(input) {
1742
1846
  context: "harness turn failed",
1743
1847
  error: err
1744
1848
  });
1849
+ (_m = input.onTurnFailed) == null ? void 0 : _m.call(input);
1745
1850
  result.fail(err);
1746
1851
  } finally {
1747
1852
  reader.releaseLock();
@@ -1764,7 +1869,7 @@ function hasTool(input) {
1764
1869
  }
1765
1870
  async function maybeExecuteHostTool(input) {
1766
1871
  const tool = input.tools[input.event.toolName];
1767
- if (!isExecutableTool(tool)) return { ok: true, output: void 0 };
1872
+ if (!isExecutableTool(tool)) return { executed: false };
1768
1873
  const parsed = await safeParseJSON({ text: input.event.input });
1769
1874
  const args = parsed.success ? parsed.value : input.event.input;
1770
1875
  try {
@@ -1791,14 +1896,14 @@ async function maybeExecuteHostTool(input) {
1791
1896
  toolCallId: input.event.toolCallId,
1792
1897
  output
1793
1898
  });
1794
- return { ok: true, output };
1899
+ return { executed: true, outcome: { ok: true, output } };
1795
1900
  } catch (err) {
1796
1901
  await input.control.submitToolResult({
1797
1902
  toolCallId: input.event.toolCallId,
1798
1903
  output: { error: String(err) },
1799
1904
  isError: true
1800
1905
  });
1801
- return { ok: false, error: err };
1906
+ return { executed: true, outcome: { ok: false, error: err } };
1802
1907
  }
1803
1908
  }
1804
1909
  async function validateToolCall(args) {
@@ -1837,10 +1942,11 @@ function promptToText(prompt) {
1837
1942
  var HarnessAgentSession = class {
1838
1943
  constructor(options) {
1839
1944
  this.pendingToolApprovals = /* @__PURE__ */ new Map();
1945
+ this.pendingToolResults = /* @__PURE__ */ new Map();
1840
1946
  this.sessionState = "active";
1841
1947
  this.turnSequence = 0;
1842
1948
  this.activeTurnSequence = 0;
1843
- var _a3, _b3;
1949
+ var _a3, _b3, _c;
1844
1950
  this.sessionId = options.sessionId;
1845
1951
  this.harness = options.harness;
1846
1952
  this.underlyingSession = options.underlyingSession;
@@ -1852,7 +1958,10 @@ var HarnessAgentSession = class {
1852
1958
  for (const approval of (_a3 = options.pendingToolApprovals) != null ? _a3 : []) {
1853
1959
  this.pendingToolApprovals.set(approval.approvalId, approval);
1854
1960
  }
1855
- this.turnState = (_b3 = options.turnState) != null ? _b3 : this.pendingToolApprovals.size > 0 ? "awaiting-approval" : "idle";
1961
+ for (const pendingResult of (_b3 = options.pendingToolResults) != null ? _b3 : []) {
1962
+ this.pendingToolResults.set(pendingResult.toolCallId, pendingResult);
1963
+ }
1964
+ this.turnState = (_c = options.turnState) != null ? _c : this.pendingToolApprovals.size > 0 ? "awaiting-approval" : this.pendingToolResults.size > 0 ? "awaiting-tool-result" : "idle";
1856
1965
  this.isResume = options.underlyingSession.isResume;
1857
1966
  }
1858
1967
  /**
@@ -1877,6 +1986,9 @@ var HarnessAgentSession = class {
1877
1986
  getSessionWorkDir() {
1878
1987
  return this.sessionWorkDir;
1879
1988
  }
1989
+ hasUnfinishedTurn() {
1990
+ return this.turnState !== "idle";
1991
+ }
1880
1992
  promptTurn(options) {
1881
1993
  const session = this.requireReusableSession();
1882
1994
  this.requirePromptableTurn();
@@ -1899,6 +2011,7 @@ var HarnessAgentSession = class {
1899
2011
  telemetry: options.telemetry,
1900
2012
  toolApproval: this.toolApproval,
1901
2013
  pendingToolApprovals: this.getPendingToolApprovals(),
2014
+ pendingToolResults: this.getPendingToolResults(),
1902
2015
  onPendingToolApproval: (approval) => {
1903
2016
  this.pendingToolApprovals.set(approval.approvalId, approval);
1904
2017
  this.markAwaitingApprovalIfActive();
@@ -1906,11 +2019,20 @@ var HarnessAgentSession = class {
1906
2019
  onToolApprovalSettled: (approvalId) => {
1907
2020
  this.pendingToolApprovals.delete(approvalId);
1908
2021
  },
2022
+ onPendingToolResult: (pendingResult) => {
2023
+ this.pendingToolResults.set(pendingResult.toolCallId, pendingResult);
2024
+ this.markAwaitingToolResultIfActive();
2025
+ },
2026
+ onToolResultSettled: (toolCallId) => {
2027
+ this.pendingToolResults.delete(toolCallId);
2028
+ },
1909
2029
  onTurnFinished: () => {
1910
2030
  this.finishTrackedTurn({ turnId });
2031
+ },
2032
+ onTurnFailed: () => {
2033
+ this.finishTrackedTurn({ turnId });
1911
2034
  }
1912
2035
  });
1913
- this.trackTurnCompletion({ done: turn.done, turnId });
1914
2036
  return turn;
1915
2037
  } catch (error) {
1916
2038
  this.finishTrackedTurn({ turnId });
@@ -1939,7 +2061,9 @@ var HarnessAgentSession = class {
1939
2061
  telemetry: options.telemetry,
1940
2062
  toolApproval: this.toolApproval,
1941
2063
  pendingToolApprovals: this.getPendingToolApprovals(),
2064
+ pendingToolResults: this.getPendingToolResults(),
1942
2065
  toolApprovalContinuations: options.toolApprovalContinuations,
2066
+ toolResultContinuations: options.toolResultContinuations,
1943
2067
  onPendingToolApproval: (approval) => {
1944
2068
  this.pendingToolApprovals.set(approval.approvalId, approval);
1945
2069
  this.markAwaitingApprovalIfActive();
@@ -1947,11 +2071,20 @@ var HarnessAgentSession = class {
1947
2071
  onToolApprovalSettled: (approvalId) => {
1948
2072
  this.pendingToolApprovals.delete(approvalId);
1949
2073
  },
2074
+ onPendingToolResult: (pendingResult) => {
2075
+ this.pendingToolResults.set(pendingResult.toolCallId, pendingResult);
2076
+ this.markAwaitingToolResultIfActive();
2077
+ },
2078
+ onToolResultSettled: (toolCallId) => {
2079
+ this.pendingToolResults.delete(toolCallId);
2080
+ },
1950
2081
  onTurnFinished: () => {
1951
2082
  this.finishTrackedTurn({ turnId });
2083
+ },
2084
+ onTurnFailed: () => {
2085
+ this.finishTrackedTurn({ turnId });
1952
2086
  }
1953
2087
  });
1954
- this.trackTurnCompletion({ done: turn.done, turnId });
1955
2088
  return turn;
1956
2089
  } catch (error) {
1957
2090
  this.finishTrackedTurn({ turnId });
@@ -2095,9 +2228,13 @@ var HarnessAgentSession = class {
2095
2228
  getPendingToolApprovals() {
2096
2229
  return Array.from(this.pendingToolApprovals.values());
2097
2230
  }
2098
- addPendingToolApprovals(state) {
2231
+ getPendingToolResults() {
2232
+ return Array.from(this.pendingToolResults.values());
2233
+ }
2234
+ addPendingToolState(state) {
2099
2235
  const pendingToolApprovals = this.getPendingToolApprovals();
2100
- if (pendingToolApprovals.length === 0) {
2236
+ const pendingToolResults = this.getPendingToolResults();
2237
+ if (pendingToolApprovals.length === 0 && pendingToolResults.length === 0) {
2101
2238
  return {
2102
2239
  type: state.type,
2103
2240
  harnessId: state.harnessId,
@@ -2107,7 +2244,8 @@ var HarnessAgentSession = class {
2107
2244
  }
2108
2245
  return {
2109
2246
  ...state,
2110
- pendingToolApprovals
2247
+ ...pendingToolApprovals.length > 0 ? { pendingToolApprovals } : {},
2248
+ ...pendingToolResults.length > 0 ? { pendingToolResults } : {}
2111
2249
  };
2112
2250
  }
2113
2251
  async suspendCurrentTurn(options) {
@@ -2118,7 +2256,7 @@ var HarnessAgentSession = class {
2118
2256
  expectedType: "continue-turn"
2119
2257
  });
2120
2258
  this.turnState = "suspended";
2121
- return this.addPendingToolApprovals(validated);
2259
+ return this.addPendingToolState(validated);
2122
2260
  }
2123
2261
  toResumeStateWithContinuation(options) {
2124
2262
  const { continueFrom } = options;
@@ -2142,7 +2280,7 @@ var HarnessAgentSession = class {
2142
2280
  );
2143
2281
  }
2144
2282
  requireContinuableTurn() {
2145
- if (this.turnState === "awaiting-approval" || this.turnState === "suspended") {
2283
+ if (this.turnState === "awaiting-approval" || this.turnState === "awaiting-tool-result" || this.turnState === "suspended") {
2146
2284
  return;
2147
2285
  }
2148
2286
  if (this.turnState === "running") {
@@ -2159,22 +2297,23 @@ var HarnessAgentSession = class {
2159
2297
  this.turnState = "awaiting-approval";
2160
2298
  }
2161
2299
  }
2300
+ markAwaitingToolResultIfActive() {
2301
+ if (this.sessionState === "active") {
2302
+ this.turnState = "awaiting-tool-result";
2303
+ }
2304
+ }
2162
2305
  startTrackedTurn() {
2163
2306
  const turnId = ++this.turnSequence;
2164
2307
  this.activeTurnSequence = turnId;
2165
2308
  this.turnState = "running";
2166
2309
  return turnId;
2167
2310
  }
2168
- trackTurnCompletion(options) {
2169
- void Promise.resolve(options.done).finally(() => {
2170
- this.finishTrackedTurn({ turnId: options.turnId });
2171
- }).catch(() => {
2172
- });
2173
- }
2174
2311
  finishTrackedTurn(options) {
2175
2312
  if (this.sessionState !== "active") return;
2176
2313
  if (this.activeTurnSequence !== options.turnId) return;
2177
- this.turnState = this.pendingToolApprovals.size > 0 ? "awaiting-approval" : "idle";
2314
+ this.pendingToolApprovals.clear();
2315
+ this.pendingToolResults.clear();
2316
+ this.turnState = "idle";
2178
2317
  }
2179
2318
  endLocalHandle(options) {
2180
2319
  this.sessionState = options.sessionState;
@@ -2256,6 +2395,48 @@ function collectHarnessAgentToolApprovalContinuations(input) {
2256
2395
  return continuations;
2257
2396
  }
2258
2397
 
2398
+ // src/agent/harness-agent-tool-result-continuation.ts
2399
+ function collectHarnessAgentToolResultContinuations(input) {
2400
+ const lastMessage = input.messages.at(-1);
2401
+ if ((lastMessage == null ? void 0 : lastMessage.role) !== "tool") return [];
2402
+ return lastMessage.content.filter((part) => part.type === "tool-result").map(
2403
+ (part) => toToolResultContinuation({
2404
+ toolCallId: part.toolCallId,
2405
+ output: part.output
2406
+ })
2407
+ );
2408
+ }
2409
+ function toToolResultContinuation(input) {
2410
+ switch (input.output.type) {
2411
+ case "text":
2412
+ case "json":
2413
+ return {
2414
+ toolCallId: input.toolCallId,
2415
+ output: input.output.value
2416
+ };
2417
+ case "error-text":
2418
+ case "error-json":
2419
+ return {
2420
+ toolCallId: input.toolCallId,
2421
+ output: input.output.value,
2422
+ isError: true
2423
+ };
2424
+ case "execution-denied":
2425
+ return {
2426
+ toolCallId: input.toolCallId,
2427
+ output: {
2428
+ type: input.output.type,
2429
+ reason: input.output.reason
2430
+ }
2431
+ };
2432
+ case "content":
2433
+ return {
2434
+ toolCallId: input.toolCallId,
2435
+ output: input.output
2436
+ };
2437
+ }
2438
+ }
2439
+
2259
2440
  // src/agent/internal/bootstrap-recipe.ts
2260
2441
  var BOOTSTRAP_SCHEMA_VERSION = 1;
2261
2442
  async function hashHarnessBootstrap(recipe) {
@@ -2807,7 +2988,8 @@ var HarnessAgent = class {
2807
2988
  sessionWorkDir,
2808
2989
  toolApproval: this.settings.toolApproval,
2809
2990
  pendingToolApprovals: effectiveContinueFrom == null ? void 0 : effectiveContinueFrom.pendingToolApprovals,
2810
- turnState: effectiveContinueFrom == null ? "idle" : effectiveContinueFrom.pendingToolApprovals != null && effectiveContinueFrom.pendingToolApprovals.length > 0 ? "awaiting-approval" : "suspended"
2991
+ pendingToolResults: effectiveContinueFrom == null ? void 0 : effectiveContinueFrom.pendingToolResults,
2992
+ turnState: effectiveContinueFrom == null ? "idle" : effectiveContinueFrom.pendingToolApprovals != null && effectiveContinueFrom.pendingToolApprovals.length > 0 ? "awaiting-approval" : effectiveContinueFrom.pendingToolResults != null && effectiveContinueFrom.pendingToolResults.length > 0 ? "awaiting-tool-result" : "suspended"
2811
2993
  });
2812
2994
  } catch (error) {
2813
2995
  await cleanupAfterStartFailure({
@@ -2848,13 +3030,14 @@ var HarnessAgent = class {
2848
3030
  * consuming a turn that crossed a process boundary.
2849
3031
  */
2850
3032
  async continueGenerate(options) {
2851
- var _a3;
3033
+ var _a3, _b3;
2852
3034
  const runtimeContext = {};
2853
3035
  const { result, done } = this._startTurn({
2854
3036
  session: options.session,
2855
3037
  turnInput: {
2856
3038
  mode: "continue",
2857
- toolApprovalContinuations: (_a3 = options.toolApprovalContinuations) != null ? _a3 : []
3039
+ toolApprovalContinuations: (_a3 = options.toolApprovalContinuations) != null ? _a3 : [],
3040
+ toolResultContinuations: (_b3 = options.toolResultContinuations) != null ? _b3 : []
2858
3041
  },
2859
3042
  runtimeContext,
2860
3043
  abortSignal: options.abortSignal
@@ -2871,13 +3054,14 @@ var HarnessAgent = class {
2871
3054
  * follows from how the adapter resumed the session.
2872
3055
  */
2873
3056
  async continueStream(options) {
2874
- var _a3;
3057
+ var _a3, _b3;
2875
3058
  const runtimeContext = {};
2876
3059
  const { result } = this._startTurn({
2877
3060
  session: options.session,
2878
3061
  turnInput: {
2879
3062
  mode: "continue",
2880
- toolApprovalContinuations: (_a3 = options.toolApprovalContinuations) != null ? _a3 : []
3063
+ toolApprovalContinuations: (_a3 = options.toolApprovalContinuations) != null ? _a3 : [],
3064
+ toolResultContinuations: (_b3 = options.toolResultContinuations) != null ? _b3 : []
2881
3065
  },
2882
3066
  runtimeContext,
2883
3067
  abortSignal: options.abortSignal
@@ -2896,7 +3080,8 @@ var HarnessAgent = class {
2896
3080
  runtimeContext: input.runtimeContext,
2897
3081
  abortSignal: input.abortSignal,
2898
3082
  telemetry: this.settings.telemetry,
2899
- toolApprovalContinuations: input.turnInput.toolApprovalContinuations
3083
+ toolApprovalContinuations: input.turnInput.toolApprovalContinuations,
3084
+ toolResultContinuations: input.turnInput.toolResultContinuations
2900
3085
  });
2901
3086
  }
2902
3087
  return input.session.promptTurn({
@@ -2947,10 +3132,12 @@ var HarnessAgent = class {
2947
3132
  const messages = Array.isArray(options.prompt) ? options.prompt : options.messages;
2948
3133
  if (Array.isArray(messages)) {
2949
3134
  const toolApprovalContinuations = collectHarnessAgentToolApprovalContinuations({ messages });
2950
- if (toolApprovalContinuations.length > 0) {
3135
+ const toolResultContinuations = collectHarnessAgentToolResultContinuations({ messages });
3136
+ if (toolApprovalContinuations.length > 0 || toolResultContinuations.length > 0) {
2951
3137
  return {
2952
3138
  mode: "continue",
2953
- toolApprovalContinuations
3139
+ toolApprovalContinuations,
3140
+ toolResultContinuations
2954
3141
  };
2955
3142
  }
2956
3143
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -3482,6 +3669,7 @@ export {
3482
3669
  HarnessCapabilityUnsupportedError,
3483
3670
  HarnessError,
3484
3671
  collectHarnessAgentToolApprovalContinuations,
3672
+ collectHarnessAgentToolResultContinuations,
3485
3673
  createFileReporter,
3486
3674
  createTraceTreeReporter,
3487
3675
  prepareHarnessSandboxTemplate,