@ai-sdk/harness 1.0.100 → 1.0.101

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,16 @@
1
1
  # @ai-sdk/harness
2
2
 
3
+ ## 1.0.101
4
+
5
+ ### Patch Changes
6
+
7
+ - 951c54d: feat(harness): support `askUserQuestions` tool including support for normalization across harness adapters
8
+ - Updated dependencies [a51cc94]
9
+ - Updated dependencies [d1904d3]
10
+ - Updated dependencies [84e5a79]
11
+ - Updated dependencies [a8e8ad0]
12
+ - ai@7.0.92
13
+
3
14
  ## 1.0.100
4
15
 
5
16
  ### Patch Changes
package/agent/index.ts CHANGED
@@ -28,14 +28,8 @@ export type {
28
28
  HarnessAgentToolSpec,
29
29
  } from '../src/agent/harness-agent-types';
30
30
  export { HarnessAgentSession } from '../src/agent/harness-agent-session';
31
- export {
32
- collectHarnessAgentToolApprovalContinuations,
33
- type HarnessAgentToolApprovalContinuation,
34
- } from '../src/agent/harness-agent-tool-approval-continuation';
35
- export {
36
- collectHarnessAgentToolResultContinuations,
37
- type HarnessAgentToolResultContinuation,
38
- } from '../src/agent/harness-agent-tool-result-continuation';
31
+ export { collectHarnessAgentToolApprovalContinuations } from '../src/agent/harness-agent-tool-approval-continuation';
32
+ export { collectHarnessAgentToolResultContinuations } from '../src/agent/harness-agent-tool-result-continuation';
39
33
  export {
40
34
  prepareHarnessSandboxTemplate,
41
35
  prewarmHarness,
@@ -1,5 +1,5 @@
1
1
  import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
2
- import { Experimental_SandboxSession, UserModelMessage, ToolSet, FlexibleSchema, Tool, Context, MaybePromiseLike, Arrayable, ToolApprovalResponse, ModelMessage } from '@ai-sdk/provider-utils';
2
+ import { Experimental_SandboxSession, UserModelMessage, ToolResultPart, ProviderOptions, ToolSet, FlexibleSchema, Tool, Context, MaybePromiseLike, Arrayable, ToolApprovalResponse, ModelMessage } from '@ai-sdk/provider-utils';
3
3
  import { OutputInterface, AgentCallParameters, Prompt, StopCondition, ToolApprovalStatus, TelemetryOptions, ActiveTools, StreamTextResult, Agent, GenerateTextResult, AgentStreamParameters, Telemetry } from 'ai';
4
4
  import { z } from 'zod/v4';
5
5
  import { JSONSchema7, JSONValue, LanguageModelV4StreamPart, LanguageModelV4ToolCall, LanguageModelV4ToolApprovalRequest, LanguageModelV4ToolResult, LanguageModelV4FinishReason, LanguageModelV4Usage, AISDKError } from '@ai-sdk/provider';
@@ -347,6 +347,7 @@ type HarnessV1PromptControl = {
347
347
  toolCallId: string;
348
348
  output: unknown;
349
349
  isError?: boolean;
350
+ toolResult?: ToolResultPart;
350
351
  }): PromiseLike<void>;
351
352
  /**
352
353
  * Respond to a `tool-approval-request` the adapter emitted.
@@ -465,6 +466,7 @@ type HarnessV1PendingToolResult = {
465
466
  readonly toolCallId: string;
466
467
  readonly toolName: string;
467
468
  readonly input: string;
469
+ readonly providerOptions?: ProviderOptions;
468
470
  };
469
471
  /**
470
472
  * Framework-owned settings captured when a turn begins. The same settings are
@@ -1015,6 +1017,40 @@ declare const HARNESS_V1_BUILTIN_TOOLS: {
1015
1017
  readonly webSearch: Tool<{
1016
1018
  query: string;
1017
1019
  }, unknown, _ai_sdk_provider_utils.Context>;
1020
+ readonly askUserQuestions: _ai_sdk_provider_utils.FunctionTool<{
1021
+ allowPartialAnswers: boolean;
1022
+ questions: {
1023
+ id: string;
1024
+ question: string;
1025
+ header?: string | undefined;
1026
+ options?: {
1027
+ id: string;
1028
+ label: string;
1029
+ description?: string | undefined;
1030
+ preview?: string | undefined;
1031
+ }[] | undefined;
1032
+ allowMultiple?: boolean | undefined;
1033
+ allowFreeForm?: boolean | {
1034
+ secret: boolean;
1035
+ } | undefined;
1036
+ }[];
1037
+ }, {
1038
+ action: "answered";
1039
+ answers: Record<string, {
1040
+ optionIds: string[];
1041
+ freeform?: string | undefined;
1042
+ }>;
1043
+ } | {
1044
+ action: "partially-answered";
1045
+ answers: Record<string, {
1046
+ optionIds: string[];
1047
+ freeform?: string | undefined;
1048
+ }>;
1049
+ } | {
1050
+ action: "declined";
1051
+ } | {
1052
+ action: "cancelled";
1053
+ }>;
1018
1054
  };
1019
1055
  type HarnessV1BuiltinToolName = keyof typeof HARNESS_V1_BUILTIN_TOOLS;
1020
1056
  type HarnessV1BuiltinToolUseKind = 'readonly' | 'edit' | 'bash';
@@ -1391,44 +1427,6 @@ type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAge
1391
1427
  readonly onLog?: (event: HarnessDiagnostic) => void;
1392
1428
  } & HarnessAgentToolFilteringSettings<HarnessAllTools<THarness, TUserTools>>;
1393
1429
 
1394
- type HarnessAgentToolApprovalContinuation = {
1395
- readonly approvalResponse: ToolApprovalResponse;
1396
- readonly toolCall: {
1397
- readonly type: 'tool-call';
1398
- readonly toolCallId: string;
1399
- readonly toolName: string;
1400
- readonly input: unknown;
1401
- readonly providerExecuted?: boolean;
1402
- };
1403
- };
1404
- /**
1405
- * Extract approval decisions that should continue a suspended harness turn.
1406
- *
1407
- * AI SDK clients send approval decisions as a trailing `role: "tool"` message
1408
- * containing `tool-approval-response` parts. The response only carries the
1409
- * approval id, so the harness has to recover the matching approval request
1410
- * locally to find the original tool call before it can resume the paused turn.
1411
- * Responses that already have a tool result are ignored, because those
1412
- * approvals were already consumed by a prior continuation.
1413
- */
1414
- declare function collectHarnessAgentToolApprovalContinuations(input: {
1415
- messages: readonly ModelMessage[];
1416
- }): readonly HarnessAgentToolApprovalContinuation[];
1417
-
1418
- type HarnessAgentToolResultContinuation = {
1419
- readonly toolCallId: string;
1420
- readonly output: unknown;
1421
- readonly isError?: boolean;
1422
- };
1423
- /**
1424
- * Extract client-provided tool results from the trailing tool message and
1425
- * convert AI SDK model-output wrappers back into values a harness runtime can
1426
- * return from its pending host-tool invocation.
1427
- */
1428
- declare function collectHarnessAgentToolResultContinuations(input: {
1429
- messages: readonly ModelMessage[];
1430
- }): readonly HarnessAgentToolResultContinuation[];
1431
-
1432
1430
  type HarnessAgentTurnResult<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context, OUTPUT extends OutputInterface> = {
1433
1431
  result: StreamTextResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
1434
1432
  done: Promise<void>;
@@ -1519,8 +1517,8 @@ declare class HarnessAgentSession {
1519
1517
  output: OUTPUT | undefined;
1520
1518
  telemetry: TelemetryOptions | undefined;
1521
1519
  stopConditions: ReadonlyArray<StopCondition<TOOLS, RUNTIME_CONTEXT>>;
1522
- toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[] | undefined;
1523
- toolResultContinuations?: readonly HarnessAgentToolResultContinuation[] | undefined;
1520
+ toolApprovalContinuations?: readonly ToolApprovalResponse[] | undefined;
1521
+ toolResultContinuations?: readonly ToolResultPart[] | undefined;
1524
1522
  }): HarnessAgentTurnResult<TOOLS, RUNTIME_CONTEXT, OUTPUT>;
1525
1523
  /**
1526
1524
  * Ask the underlying runtime to compact its context. The runtime performs
@@ -1703,8 +1701,8 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
1703
1701
  */
1704
1702
  continueGenerate(options: {
1705
1703
  session: HarnessAgentSession;
1706
- toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[];
1707
- toolResultContinuations?: readonly HarnessAgentToolResultContinuation[];
1704
+ toolApprovalContinuations?: readonly ToolApprovalResponse[];
1705
+ toolResultContinuations?: readonly ToolResultPart[];
1708
1706
  abortSignal?: AbortSignal;
1709
1707
  }): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
1710
1708
  /**
@@ -1717,8 +1715,8 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
1717
1715
  */
1718
1716
  continueStream(options: {
1719
1717
  session: HarnessAgentSession;
1720
- toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[];
1721
- toolResultContinuations?: readonly HarnessAgentToolResultContinuation[];
1718
+ toolApprovalContinuations?: readonly ToolApprovalResponse[];
1719
+ toolResultContinuations?: readonly ToolResultPart[];
1722
1720
  abortSignal?: AbortSignal;
1723
1721
  }): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, OUTPUT>>;
1724
1722
  /**
@@ -1745,6 +1743,27 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
1745
1743
  private _resolveResponseFormat;
1746
1744
  }
1747
1745
 
1746
+ /**
1747
+ * Extract approval decisions that should continue a suspended harness turn.
1748
+ *
1749
+ * AI SDK clients send approval decisions as a trailing `role: "tool"` message
1750
+ * containing `tool-approval-response` parts. The response only carries the
1751
+ * approval id, so the harness has to recover the matching approval request
1752
+ * locally to find the original tool call before it can resume the paused turn.
1753
+ * Responses that already have a tool result are ignored, because those
1754
+ * approvals were already consumed by a prior continuation.
1755
+ */
1756
+ declare function collectHarnessAgentToolApprovalContinuations(input: {
1757
+ messages: readonly ModelMessage[];
1758
+ }): readonly ToolApprovalResponse[];
1759
+
1760
+ /**
1761
+ * Extract client-provided tool results from the trailing tool message.
1762
+ */
1763
+ declare function collectHarnessAgentToolResultContinuations(input: {
1764
+ messages: readonly ModelMessage[];
1765
+ }): readonly ToolResultPart[];
1766
+
1748
1767
  type SandboxBootstrapSettings = Omit<HarnessAgentSandboxConfig, 'onSession'>;
1749
1768
  /**
1750
1769
  * Prepare a harness's sandbox template without running an agent. Idempotent: if
@@ -1902,4 +1921,4 @@ interface TraceTreeReporterOptions {
1902
1921
  }
1903
1922
  declare function createTraceTreeReporter(options?: TraceTreeReporterOptions): Telemetry;
1904
1923
 
1905
- export { type FileReporter, type FileReporterOptions, HarnessAgent, type HarnessAgentAdapter, type HarnessAgentAdapterSession, type HarnessAgentBuiltinTool, type HarnessAgentBuiltinToolName, type HarnessAgentBuiltinToolUseKind, type HarnessAgentBuiltinTools, type HarnessAgentContinueTurnOptions, type HarnessAgentContinueTurnState, type HarnessAgentLifecycleState, type HarnessAgentPendingToolApproval, type HarnessAgentPendingToolResult, type HarnessAgentPermissionMode, type HarnessAgentPrompt, type HarnessAgentPromptControl, type HarnessAgentPromptTurnOptions, type HarnessAgentResumeSessionState, type HarnessAgentSandboxConfig, HarnessAgentSession, type HarnessAgentSettings, type HarnessAgentSkill, type HarnessAgentStartOptions, type HarnessAgentStreamPart, type HarnessAgentToolApprovalConfiguration, type HarnessAgentToolApprovalContinuation, type HarnessAgentToolResultContinuation, type HarnessAgentToolSpec, type HarnessAllTools, HarnessCapabilityUnsupportedError, type HarnessDebugConfig, type HarnessDebugLevel, type HarnessDiagnostic, type HarnessDiagnosticConsumer, HarnessError, HarnessSandboxAuthenticationError, type PrepareSandboxForHarnessResult, type TraceTreeReporterOptions, collectHarnessAgentToolApprovalContinuations, collectHarnessAgentToolResultContinuations, createFileReporter, createTraceTreeReporter, getHarnessErrorMessage, prepareHarnessSandboxTemplate, prepareSandboxForHarness, prewarmHarness };
1924
+ export { type FileReporter, type FileReporterOptions, HarnessAgent, type HarnessAgentAdapter, type HarnessAgentAdapterSession, type HarnessAgentBuiltinTool, type HarnessAgentBuiltinToolName, type HarnessAgentBuiltinToolUseKind, type HarnessAgentBuiltinTools, type HarnessAgentContinueTurnOptions, type HarnessAgentContinueTurnState, type HarnessAgentLifecycleState, type HarnessAgentPendingToolApproval, type HarnessAgentPendingToolResult, type HarnessAgentPermissionMode, type HarnessAgentPrompt, type HarnessAgentPromptControl, type HarnessAgentPromptTurnOptions, type HarnessAgentResumeSessionState, type HarnessAgentSandboxConfig, HarnessAgentSession, type HarnessAgentSettings, type HarnessAgentSkill, type HarnessAgentStartOptions, type HarnessAgentStreamPart, type HarnessAgentToolApprovalConfiguration, type HarnessAgentToolSpec, type HarnessAllTools, HarnessCapabilityUnsupportedError, type HarnessDebugConfig, type HarnessDebugLevel, type HarnessDiagnostic, type HarnessDiagnosticConsumer, HarnessError, HarnessSandboxAuthenticationError, type PrepareSandboxForHarnessResult, type TraceTreeReporterOptions, collectHarnessAgentToolApprovalContinuations, collectHarnessAgentToolResultContinuations, createFileReporter, createTraceTreeReporter, getHarnessErrorMessage, prepareHarnessSandboxTemplate, prepareSandboxForHarness, prewarmHarness };
@@ -1556,6 +1556,25 @@ function pinSandboxChannelEventCheckpoint(event) {
1556
1556
  }
1557
1557
 
1558
1558
  // src/agent/internal/run-prompt.ts
1559
+ function unwrapToolResultOutput(toolResult) {
1560
+ switch (toolResult.output.type) {
1561
+ case "text":
1562
+ case "json":
1563
+ return { output: toolResult.output.value };
1564
+ case "error-text":
1565
+ case "error-json":
1566
+ return { output: toolResult.output.value, isError: true };
1567
+ case "execution-denied":
1568
+ return {
1569
+ output: {
1570
+ type: toolResult.output.type,
1571
+ reason: toolResult.output.reason
1572
+ }
1573
+ };
1574
+ case "content":
1575
+ return { output: toolResult.output };
1576
+ }
1577
+ }
1559
1578
  function runPrompt(input) {
1560
1579
  var _a4, _b4, _c, _d, _e, _f, _g;
1561
1580
  const result = new HarnessStreamTextResult({
@@ -1680,7 +1699,7 @@ function runPrompt(input) {
1680
1699
  );
1681
1700
  const continuationsByApprovalId = new Map(
1682
1701
  ((_b5 = input.toolApprovalContinuations) != null ? _b5 : []).map((continuation) => [
1683
- continuation.approvalResponse.approvalId,
1702
+ continuation.approvalId,
1684
1703
  continuation
1685
1704
  ])
1686
1705
  );
@@ -1812,13 +1831,13 @@ function runPrompt(input) {
1812
1831
  ...input2.providerExecuted !== void 0 ? { providerExecuted: input2.providerExecuted } : {}
1813
1832
  });
1814
1833
  };
1815
- const enqueueApprovalResponse = (approval, continuation) => {
1834
+ const enqueueApprovalResponse = (approval, continuation, toolCall) => {
1816
1835
  result.enqueueContinuation({
1817
1836
  type: "tool-approval-response",
1818
1837
  approvalId: approval.approvalId,
1819
- toolCall: continuation.toolCall,
1820
- approved: continuation.approvalResponse.approved,
1821
- ...continuation.approvalResponse.reason !== void 0 ? { reason: continuation.approvalResponse.reason } : {},
1838
+ toolCall,
1839
+ approved: continuation.approved,
1840
+ ...continuation.reason !== void 0 ? { reason: continuation.reason } : {},
1822
1841
  ...approval.providerExecuted !== void 0 ? { providerExecuted: approval.providerExecuted } : {}
1823
1842
  });
1824
1843
  };
@@ -1827,20 +1846,27 @@ function runPrompt(input) {
1827
1846
  const pendingResult = (_a6 = pendingResultsByToolCallId.get(options.toolCall.toolCallId)) != null ? _a6 : {
1828
1847
  toolCallId: options.toolCall.toolCallId,
1829
1848
  toolName: options.toolCall.toolName,
1830
- input: options.toolCall.input
1849
+ input: options.toolCall.input,
1850
+ ...options.toolCall.providerMetadata !== void 0 ? { providerOptions: options.toolCall.providerMetadata } : {}
1831
1851
  };
1832
1852
  pendingResultsByToolCallId.set(pendingResult.toolCallId, pendingResult);
1833
1853
  onPendingToolResult(pendingResult);
1834
1854
  return pendingResult;
1835
1855
  };
1836
1856
  const processPendingToolResultContinuation = async (pendingResult, continuation) => {
1857
+ const result2 = unwrapToolResultOutput(continuation);
1837
1858
  onToolResultSettled(pendingResult.toolCallId);
1838
1859
  pendingResultsByToolCallId.delete(pendingResult.toolCallId);
1839
1860
  settledHostToolCallIds.add(pendingResult.toolCallId);
1840
1861
  await control.submitToolResult({
1841
1862
  toolCallId: pendingResult.toolCallId,
1842
- output: continuation.output,
1843
- isError: continuation.isError
1863
+ output: result2.output,
1864
+ isError: result2.isError,
1865
+ toolResult: {
1866
+ ...continuation,
1867
+ toolName: pendingResult.toolName,
1868
+ ...continuation.providerOptions == null && pendingResult.providerOptions != null ? { providerOptions: pendingResult.providerOptions } : {}
1869
+ }
1844
1870
  });
1845
1871
  };
1846
1872
  const enqueueHostToolOutcome = (options) => {
@@ -1873,7 +1899,23 @@ function runPrompt(input) {
1873
1899
  };
1874
1900
  const processPendingApprovalContinuation = async (approval, continuation) => {
1875
1901
  var _a6;
1876
- enqueueApprovalResponse(approval, continuation);
1902
+ const rawToolCall = (_a6 = rawToolCallsByToolCallId.get(approval.toolCallId)) != null ? _a6 : {
1903
+ type: "tool-call",
1904
+ toolCallId: approval.toolCallId,
1905
+ toolName: approval.toolName,
1906
+ input: approval.input,
1907
+ providerExecuted: approval.providerExecuted,
1908
+ nativeName: approval.nativeName
1909
+ };
1910
+ const parsedInput = await safeParseJSON2({ text: rawToolCall.input });
1911
+ const toolCall = {
1912
+ type: "tool-call",
1913
+ toolCallId: rawToolCall.toolCallId,
1914
+ toolName: rawToolCall.toolName,
1915
+ input: parsedInput.success ? parsedInput.value : rawToolCall.input,
1916
+ ...rawToolCall.providerExecuted !== void 0 ? { providerExecuted: rawToolCall.providerExecuted } : {}
1917
+ };
1918
+ enqueueApprovalResponse(approval, continuation, toolCall);
1877
1919
  onToolApprovalSettled(approval.approvalId);
1878
1920
  pendingApprovalsByApprovalId.delete(approval.approvalId);
1879
1921
  pendingApprovalsByToolCallId.delete(approval.toolCallId);
@@ -1886,28 +1928,22 @@ function runPrompt(input) {
1886
1928
  }
1887
1929
  await control.submitToolApproval({
1888
1930
  approvalId: approval.approvalId,
1889
- approved: continuation.approvalResponse.approved,
1890
- reason: continuation.approvalResponse.reason
1931
+ approved: continuation.approved,
1932
+ reason: continuation.reason
1891
1933
  });
1892
1934
  return "continued";
1893
1935
  }
1894
1936
  settledHostToolCallIds.add(approval.toolCallId);
1895
- if (!continuation.approvalResponse.approved) {
1937
+ if (!continuation.approved) {
1896
1938
  await control.submitToolResult({
1897
1939
  toolCallId: approval.toolCallId,
1898
1940
  output: {
1899
1941
  type: "execution-denied",
1900
- reason: continuation.approvalResponse.reason
1942
+ reason: continuation.reason
1901
1943
  }
1902
1944
  });
1903
1945
  return "continued";
1904
1946
  }
1905
- const rawToolCall = (_a6 = rawToolCallsByToolCallId.get(approval.toolCallId)) != null ? _a6 : {
1906
- type: "tool-call",
1907
- toolCallId: approval.toolCallId,
1908
- toolName: approval.toolName,
1909
- input: approval.input
1910
- };
1911
1947
  await telemetry.start(input.model);
1912
1948
  await telemetry.toolStart({
1913
1949
  toolCallId: rawToolCall.toolCallId,
@@ -1947,7 +1983,7 @@ function runPrompt(input) {
1947
1983
  return "awaiting-tool-result";
1948
1984
  }
1949
1985
  enqueueHostToolOutcome({
1950
- toolCall: continuation.toolCall,
1986
+ toolCall,
1951
1987
  outcome: execution.outcome
1952
1988
  });
1953
1989
  await telemetry.toolEnd(rawToolCall.toolCallId, execution.outcome);
@@ -2200,7 +2236,11 @@ function runPrompt(input) {
2200
2236
  `Harness '${input.harness.harnessId}' could not find parsed tool call '${toolCall.toolCallId}' for custom tool approval.`
2201
2237
  );
2202
2238
  }
2203
- if (!hasTool({ tools: activeTools, toolName: toolCall.toolName })) {
2239
+ const isClientExecutedBuiltin = toolCall.toolName === "askUserQuestions" && Object.prototype.hasOwnProperty.call(
2240
+ input.harness.builtinTools,
2241
+ toolCall.toolName
2242
+ );
2243
+ if (!isClientExecutedBuiltin && !hasTool({ tools: activeTools, toolName: toolCall.toolName })) {
2204
2244
  const output = {
2205
2245
  type: "execution-denied",
2206
2246
  reason: getHarnessV1BuiltinToolFilteringDenialReason({
@@ -2214,6 +2254,15 @@ function runPrompt(input) {
2214
2254
  await telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
2215
2255
  continue;
2216
2256
  }
2257
+ if (isClientExecutedBuiltin) {
2258
+ recordPendingToolResult({ toolCall });
2259
+ if (expectedStepToolCallCount != null && observedStepToolCallCount < expectedStepToolCallCount) {
2260
+ pauseAfterStepToolCalls = true;
2261
+ continue;
2262
+ }
2263
+ await finishForHostInputPause({ completeCurrentStep: true });
2264
+ return;
2265
+ }
2217
2266
  const customToolApprovalDecision = resolveCustomToolApproval({
2218
2267
  toolName: toolCall.toolName,
2219
2268
  toolApproval: input.toolApproval
@@ -3030,7 +3079,7 @@ var HarnessAgentSession = class {
3030
3079
  function collectHarnessAgentToolApprovalContinuations(input) {
3031
3080
  const lastMessage = input.messages.at(-1);
3032
3081
  if ((lastMessage == null ? void 0 : lastMessage.role) !== "tool") return [];
3033
- const toolCallsByToolCallId = /* @__PURE__ */ new Map();
3082
+ const toolCallIds = /* @__PURE__ */ new Set();
3034
3083
  const approvalRequestsByApprovalId = /* @__PURE__ */ new Map();
3035
3084
  for (const message of input.messages) {
3036
3085
  if (message.role !== "assistant" || typeof message.content === "string") {
@@ -3038,13 +3087,7 @@ function collectHarnessAgentToolApprovalContinuations(input) {
3038
3087
  }
3039
3088
  for (const part of message.content) {
3040
3089
  if (part.type === "tool-call") {
3041
- toolCallsByToolCallId.set(part.toolCallId, {
3042
- type: "tool-call",
3043
- toolCallId: part.toolCallId,
3044
- toolName: part.toolName,
3045
- input: part.input,
3046
- ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {}
3047
- });
3090
+ toolCallIds.add(part.toolCallId);
3048
3091
  } else if (part.type === "tool-approval-request") {
3049
3092
  approvalRequestsByApprovalId.set(part.approvalId, part);
3050
3093
  }
@@ -3066,16 +3109,12 @@ function collectHarnessAgentToolApprovalContinuations(input) {
3066
3109
  });
3067
3110
  }
3068
3111
  if (toolResultIds.has(approvalRequest.toolCallId)) continue;
3069
- const toolCall = toolCallsByToolCallId.get(approvalRequest.toolCallId);
3070
- if (toolCall == null) {
3112
+ if (!toolCallIds.has(approvalRequest.toolCallId)) {
3071
3113
  throw new HarnessError({
3072
3114
  message: `Tool approval request '${approvalRequest.approvalId}' references unknown tool call '${approvalRequest.toolCallId}'.`
3073
3115
  });
3074
3116
  }
3075
- continuations.push({
3076
- approvalResponse: part,
3077
- toolCall
3078
- });
3117
+ continuations.push(part);
3079
3118
  }
3080
3119
  return continuations;
3081
3120
  }
@@ -3084,42 +3123,7 @@ function collectHarnessAgentToolApprovalContinuations(input) {
3084
3123
  function collectHarnessAgentToolResultContinuations(input) {
3085
3124
  const lastMessage = input.messages.at(-1);
3086
3125
  if ((lastMessage == null ? void 0 : lastMessage.role) !== "tool") return [];
3087
- return lastMessage.content.filter((part) => part.type === "tool-result").map(
3088
- (part) => toToolResultContinuation({
3089
- toolCallId: part.toolCallId,
3090
- output: part.output
3091
- })
3092
- );
3093
- }
3094
- function toToolResultContinuation(input) {
3095
- switch (input.output.type) {
3096
- case "text":
3097
- case "json":
3098
- return {
3099
- toolCallId: input.toolCallId,
3100
- output: input.output.value
3101
- };
3102
- case "error-text":
3103
- case "error-json":
3104
- return {
3105
- toolCallId: input.toolCallId,
3106
- output: input.output.value,
3107
- isError: true
3108
- };
3109
- case "execution-denied":
3110
- return {
3111
- toolCallId: input.toolCallId,
3112
- output: {
3113
- type: input.output.type,
3114
- reason: input.output.reason
3115
- }
3116
- };
3117
- case "content":
3118
- return {
3119
- toolCallId: input.toolCallId,
3120
- output: input.output
3121
- };
3122
- }
3126
+ return lastMessage.content.filter((part) => part.type === "tool-result");
3123
3127
  }
3124
3128
 
3125
3129
  // src/agent/internal/bootstrap-recipe.ts
@@ -3582,6 +3586,10 @@ var HarnessAgent = class {
3582
3586
  this.sandboxConfig = sandboxConfig;
3583
3587
  this.id = settings.id;
3584
3588
  const userTools = (_a4 = settings.tools) != null ? _a4 : {};
3589
+ assertNoReservedQuestionTool({
3590
+ harness: settings.harness,
3591
+ userTools
3592
+ });
3585
3593
  this.permissionMode = resolvePermissionMode({
3586
3594
  permissionMode: settings.permissionMode
3587
3595
  });
@@ -4059,6 +4067,10 @@ var HarnessAgent = class {
4059
4067
  _prepareTurnSettings(options) {
4060
4068
  var _a4, _b4;
4061
4069
  const userTools = (_a4 = options.tools) != null ? _a4 : {};
4070
+ assertNoReservedQuestionTool({
4071
+ harness: this.settings.harness,
4072
+ userTools
4073
+ });
4062
4074
  const tools = {
4063
4075
  ...this.settings.harness.builtinTools,
4064
4076
  ...userTools
@@ -4127,6 +4139,16 @@ var HarnessAgent = class {
4127
4139
  };
4128
4140
  }
4129
4141
  };
4142
+ function assertNoReservedQuestionTool(input) {
4143
+ if (Object.prototype.hasOwnProperty.call(
4144
+ input.harness.builtinTools,
4145
+ "askUserQuestions"
4146
+ ) && Object.prototype.hasOwnProperty.call(input.userTools, "askUserQuestions")) {
4147
+ throw new Error(
4148
+ "HarnessAgent tool name 'askUserQuestions' is reserved for harness question requests."
4149
+ );
4150
+ }
4151
+ }
4130
4152
  var HarnessGenerateTextResult = class {
4131
4153
  constructor(options) {
4132
4154
  this.steps = options.steps;