@ai-sdk/harness 1.0.11 → 1.0.12

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,12 @@
1
1
  # @ai-sdk/harness
2
2
 
3
+ ## 1.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 7859cea: feat(harness): add tool filtering via `activeTools` and `inactiveTools`
8
+ - c857346: feat(harness): add utility functions for certain duplicated layers in harnesses
9
+
3
10
  ## 1.0.11
4
11
 
5
12
  ### Patch Changes
package/agent/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { HarnessAgent } from '../src/agent/harness-agent';
2
+ export type { HarnessAllTools } from '../src/agent/harness-agent-tool-types';
2
3
  export type {
3
4
  HarnessAgentSandboxConfig,
4
5
  HarnessAgentSettings,
@@ -1,6 +1,6 @@
1
1
  import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
2
2
  import { Experimental_SandboxSession, UserModelMessage, ToolSet, FlexibleSchema, Tool, ToolApprovalResponse, ModelMessage, Context } from '@ai-sdk/provider-utils';
3
- import { ToolApprovalStatus, TelemetryOptions, StreamTextResult, Agent, AgentCallParameters, GenerateTextResult, AgentStreamParameters, Telemetry } from 'ai';
3
+ import { ToolApprovalStatus, TelemetryOptions, ActiveTools, StreamTextResult, Agent, AgentCallParameters, GenerateTextResult, AgentStreamParameters, Telemetry } from 'ai';
4
4
  import { z } from 'zod/v4';
5
5
  import { JSONValue, LanguageModelV4ToolCall, LanguageModelV4ToolApprovalRequest, LanguageModelV4ToolResult, LanguageModelV4FinishReason, LanguageModelV4Usage, JSONSchema7, AISDKError } from '@ai-sdk/provider';
6
6
 
@@ -585,6 +585,14 @@ type HarnessV1ToolSpec = {
585
585
  readonly inputSchema?: JSONSchema7;
586
586
  };
587
587
 
588
+ type HarnessV1BuiltinToolFiltering = {
589
+ mode: 'allow';
590
+ toolNames: string[];
591
+ } | {
592
+ mode: 'deny';
593
+ toolNames: string[];
594
+ };
595
+
588
596
  /**
589
597
  * Options passed to `HarnessV1.doStart`.
590
598
  *
@@ -622,6 +630,12 @@ type HarnessV1StartOptions = {
622
630
  * the adapter.
623
631
  */
624
632
  readonly permissionMode?: HarnessV1PermissionMode;
633
+ /**
634
+ * Adapter-native built-in tools that should be available for this session.
635
+ * Custom host-executed tools are filtered by the framework before they reach
636
+ * the adapter.
637
+ */
638
+ readonly builtinToolFiltering?: HarnessV1BuiltinToolFiltering;
625
639
  /**
626
640
  * Signal that aborts startup. The adapter must propagate cancellation to
627
641
  * any spawned processes or network calls.
@@ -858,6 +872,16 @@ type HarnessV1<TBuiltinTools extends ToolSet = ToolSet> = {
858
872
  * only describes adapter-native tool approval support.
859
873
  */
860
874
  readonly supportsBuiltinToolApprovals?: boolean;
875
+ /**
876
+ * Whether the adapter can prevent its underlying runtime from seeing or
877
+ * calling inactive built-in tools for every tool in `builtinTools`.
878
+ *
879
+ * Adapters without native filtering can still support `activeTools` and
880
+ * `inactiveTools` for built-ins when `supportsBuiltinToolApprovals` is
881
+ * `true`: the framework routes inactive built-in tool calls through the
882
+ * approval path and auto-denies them before they execute.
883
+ */
884
+ readonly supportsBuiltinToolFiltering?: boolean;
861
885
  /**
862
886
  * Optional schema for the adapter-defined `data` payload returned by session
863
887
  * lifecycle methods. When present, the adapter promises that exported state
@@ -1038,6 +1062,14 @@ type HarnessAgentPendingToolApproval = HarnessV1PendingToolApproval;
1038
1062
  type HarnessAgentSkill = HarnessV1Skill;
1039
1063
  type HarnessAgentPermissionMode = HarnessV1PermissionMode;
1040
1064
 
1065
+ /** Extract the builtin tool set type from a harness adapter parameter. */
1066
+ type HarnessBuiltinToolsOf<H> = H extends HarnessAgentAdapter<infer T> ? T : never;
1067
+ /**
1068
+ * Type-level merge of a harness's builtin tools with user-defined tools.
1069
+ * User tools override builtins on key collision.
1070
+ */
1071
+ type HarnessAllTools<THarness extends HarnessAgentAdapter<any>, TUserTools extends ToolSet> = Omit<HarnessBuiltinToolsOf<THarness>, keyof TUserTools> & TUserTools;
1072
+
1041
1073
  type HarnessAgentToolApprovalConfiguration = Readonly<Record<string, ToolApprovalStatus>>;
1042
1074
  type HarnessAgentSandboxConfig = {
1043
1075
  /**
@@ -1077,6 +1109,7 @@ type HarnessAgentSandboxConfig = {
1077
1109
  readonly abortSignal?: AbortSignal;
1078
1110
  }) => Promise<void>;
1079
1111
  };
1112
+ type HarnessTools<TOOLS extends ToolSet> = ActiveTools<NoInfer<TOOLS>>;
1080
1113
  /**
1081
1114
  * Construction-time settings for a `HarnessAgent`.
1082
1115
  *
@@ -1084,6 +1117,21 @@ type HarnessAgentSandboxConfig = {
1084
1117
  * `AgentCallParameters` / `AgentStreamParameters` passed to `generate` /
1085
1118
  * `stream` and are not duplicated here.
1086
1119
  */
1120
+ type HarnessAgentToolFilteringSettings<TOOLS extends ToolSet> = {
1121
+ /**
1122
+ * Limits the tools that are available for the harness to call without
1123
+ * changing the tool call and result types in the result.
1124
+ */
1125
+ readonly activeTools?: HarnessTools<TOOLS>;
1126
+ readonly inactiveTools?: never;
1127
+ } | {
1128
+ readonly activeTools?: never;
1129
+ /**
1130
+ * Excludes tools from the set that is available for the harness to call
1131
+ * without changing the tool call and result types in the result.
1132
+ */
1133
+ readonly inactiveTools?: HarnessTools<TOOLS>;
1134
+ };
1087
1135
  type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAgentAdapter, TUserTools extends ToolSet = {}> = {
1088
1136
  /**
1089
1137
  * The harness adapter driving the underlying agent runtime. Its
@@ -1165,7 +1213,7 @@ type HarnessAgentSettings<THarness extends HarnessAgentAdapter<any> = HarnessAge
1165
1213
  * stderr default — wire this to capture diagnostics in code.
1166
1214
  */
1167
1215
  readonly onLog?: (event: HarnessDiagnostic) => void;
1168
- };
1216
+ } & HarnessAgentToolFilteringSettings<HarnessAllTools<THarness, TUserTools>>;
1169
1217
 
1170
1218
  type HarnessAgentToolApprovalContinuation = {
1171
1219
  readonly approvalResponse: ToolApprovalResponse;
@@ -1250,7 +1298,9 @@ declare class HarnessAgentSession {
1250
1298
  prompt: HarnessAgentPrompt;
1251
1299
  instructions: string | undefined;
1252
1300
  tools: TOOLS;
1301
+ activeTools: ToolSet;
1253
1302
  toolSpecs: HarnessAgentToolSpec[];
1303
+ builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
1254
1304
  runtimeContext: RUNTIME_CONTEXT;
1255
1305
  abortSignal: AbortSignal | undefined;
1256
1306
  telemetry: TelemetryOptions | undefined;
@@ -1258,7 +1308,9 @@ declare class HarnessAgentSession {
1258
1308
  continueTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(options: {
1259
1309
  instructions: string | undefined;
1260
1310
  tools: TOOLS;
1311
+ activeTools: ToolSet;
1261
1312
  toolSpecs: HarnessAgentToolSpec[];
1313
+ builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
1262
1314
  runtimeContext: RUNTIME_CONTEXT;
1263
1315
  abortSignal: AbortSignal | undefined;
1264
1316
  telemetry: TelemetryOptions | undefined;
@@ -1322,13 +1374,6 @@ declare class HarnessAgentSession {
1322
1374
  private requireReusableSession;
1323
1375
  }
1324
1376
 
1325
- /** Extract the builtin tool set type from a harness adapter parameter. */
1326
- type BuiltinToolsOf<H> = H extends HarnessAgentAdapter<infer T> ? T : never;
1327
- /**
1328
- * Type-level merge of a harness's builtin tools with user-defined tools.
1329
- * User tools override builtins on key collision.
1330
- */
1331
- type HarnessAllTools<THarness extends HarnessAgentAdapter<any>, TUserTools extends ToolSet> = Omit<BuiltinToolsOf<THarness>, keyof TUserTools> & TUserTools;
1332
1377
  /**
1333
1378
  * Required `session` extension on every `HarnessAgent.generate` /
1334
1379
  * `HarnessAgent.stream` call. The agent operates exclusively on the
@@ -1387,7 +1432,8 @@ declare class HarnessAgent<THarness extends HarnessAgentAdapter<any> = HarnessAg
1387
1432
  readonly tools: HarnessAllTools<THarness, TUserTools>;
1388
1433
  private readonly settings;
1389
1434
  private readonly sandboxConfig;
1390
- private readonly userTools;
1435
+ private readonly activeUserTools;
1436
+ private readonly builtinToolFiltering;
1391
1437
  private readonly permissionMode;
1392
1438
  constructor(settings: HarnessAgentSettings<THarness, TUserTools>);
1393
1439
  /** Identifier of the harness backing this agent. */
@@ -1565,4 +1611,4 @@ interface TraceTreeReporterOptions {
1565
1611
  }
1566
1612
  declare function createTraceTreeReporter(options?: TraceTreeReporterOptions): Telemetry;
1567
1613
 
1568
- 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 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 HarnessAgentToolSpec, HarnessCapabilityUnsupportedError, type HarnessDebugConfig, type HarnessDebugLevel, type HarnessDiagnostic, type HarnessDiagnosticConsumer, HarnessError, type PrepareSandboxForHarnessResult, type TraceTreeReporterOptions, collectHarnessAgentToolApprovalContinuations, createFileReporter, createTraceTreeReporter, prepareHarnessSandboxTemplate, prepareSandboxForHarness, prewarmHarness };
1614
+ 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 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 HarnessAgentToolSpec, type HarnessAllTools, HarnessCapabilityUnsupportedError, type HarnessDebugConfig, type HarnessDebugLevel, type HarnessDiagnostic, type HarnessDiagnosticConsumer, HarnessError, type PrepareSandboxForHarnessResult, type TraceTreeReporterOptions, collectHarnessAgentToolApprovalContinuations, createFileReporter, createTraceTreeReporter, prepareHarnessSandboxTemplate, prepareSandboxForHarness, prewarmHarness };
@@ -131,6 +131,15 @@ async function validateLifecycleStateData(input) {
131
131
  };
132
132
  }
133
133
 
134
+ // src/v1/harness-v1-tool-filtering.ts
135
+ function isHarnessV1BuiltinToolIncluded(input) {
136
+ if (input.toolFiltering == null) return true;
137
+ return input.toolFiltering.mode === "allow" ? input.toolFiltering.toolNames.includes(input.toolName) : !input.toolFiltering.toolNames.includes(input.toolName);
138
+ }
139
+ function getHarnessV1BuiltinToolFilteringDenialReason(input) {
140
+ return `Tool '${input.toolName}' is inactive due to the HarnessAgent tool filtering policy.`;
141
+ }
142
+
134
143
  // src/agent/internal/to-harness-stream.ts
135
144
  async function toHarnessStream(options) {
136
145
  let controller;
@@ -1162,7 +1171,7 @@ function normalizeToolApprovalStatus(input) {
1162
1171
 
1163
1172
  // src/agent/internal/run-prompt.ts
1164
1173
  function runPrompt(input) {
1165
- var _a3, _b3, _c;
1174
+ var _a3, _b3, _c, _d;
1166
1175
  const result = new HarnessStreamTextResult({
1167
1176
  tools: input.tools,
1168
1177
  runtimeContext: input.runtimeContext,
@@ -1176,6 +1185,7 @@ function runPrompt(input) {
1176
1185
  });
1177
1186
  const onToolApprovalSettled = (_c = input.onToolApprovalSettled) != null ? _c : (() => {
1178
1187
  });
1188
+ const activeTools = (_d = input.activeTools) != null ? _d : input.tools;
1179
1189
  const telemetry = createTurnTelemetry({
1180
1190
  telemetry: input.telemetry,
1181
1191
  harnessId: input.harness.harnessId,
@@ -1185,7 +1195,7 @@ function runPrompt(input) {
1185
1195
  runtimeContext: input.runtimeContext
1186
1196
  });
1187
1197
  const done = (async () => {
1188
- var _a4, _b4, _c2, _d, _e, _f, _g;
1198
+ var _a4, _b4, _c2, _d2, _e, _f, _g, _h;
1189
1199
  let bridge;
1190
1200
  try {
1191
1201
  bridge = await toHarnessStream({
@@ -1348,7 +1358,7 @@ function runPrompt(input) {
1348
1358
  };
1349
1359
  const outcome = await maybeExecuteHostTool({
1350
1360
  event: rawToolCall,
1351
- tools: input.tools,
1361
+ tools: activeTools,
1352
1362
  sandboxSession: input.sandboxSession,
1353
1363
  abortSignal: input.abortSignal,
1354
1364
  control,
@@ -1396,6 +1406,36 @@ function runPrompt(input) {
1396
1406
  if (settledApprovalToolCallReplay) {
1397
1407
  continue;
1398
1408
  }
1409
+ if (displayValue.type === "tool-approval-request") {
1410
+ const toolCall = toolCallsByToolCallId.get(displayValue.toolCallId);
1411
+ if (toolCall == null) {
1412
+ throw new Error(
1413
+ `Harness '${input.harness.harnessId}' emitted approval request '${displayValue.approvalId}' for unknown tool call '${displayValue.toolCallId}'.`
1414
+ );
1415
+ }
1416
+ const rawToolCall = rawToolCallsByToolCallId.get(
1417
+ displayValue.toolCallId
1418
+ );
1419
+ const toolName = (_c2 = rawToolCall == null ? void 0 : rawToolCall.toolName) != null ? _c2 : toolCall.toolName;
1420
+ if (!isHarnessV1BuiltinToolIncluded({
1421
+ toolName,
1422
+ toolFiltering: input.builtinToolFiltering
1423
+ })) {
1424
+ if (control.submitToolApproval == null) {
1425
+ throw new Error(
1426
+ `Harness '${input.harness.harnessId}' emitted a built-in tool approval request but does not support approval responses.`
1427
+ );
1428
+ }
1429
+ await control.submitToolApproval({
1430
+ approvalId: displayValue.approvalId,
1431
+ approved: false,
1432
+ reason: getHarnessV1BuiltinToolFilteringDenialReason({
1433
+ toolName
1434
+ })
1435
+ });
1436
+ continue;
1437
+ }
1438
+ }
1399
1439
  for (const part of translateStreamPart(displayValue)) {
1400
1440
  result.enqueue(part);
1401
1441
  }
@@ -1441,13 +1481,13 @@ function runPrompt(input) {
1441
1481
  );
1442
1482
  }
1443
1483
  const rawToolCall = rawToolCallsByToolCallId.get(value.toolCallId);
1444
- const pendingApproval = (_e = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _e : {
1484
+ const pendingApproval = (_f = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _f : {
1445
1485
  approvalId: value.approvalId,
1446
1486
  toolCallId: value.toolCallId,
1447
1487
  toolName: toolCall.toolName,
1448
- input: (_c2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _c2 : JSON.stringify(toolCall.input),
1488
+ input: (_d2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _d2 : JSON.stringify(toolCall.input),
1449
1489
  kind: "builtin",
1450
- providerExecuted: (_d = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _d : true,
1490
+ providerExecuted: (_e = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _e : true,
1451
1491
  ...(rawToolCall == null ? void 0 : rawToolCall.nativeName) !== void 0 ? { nativeName: rawToolCall.nativeName } : {}
1452
1492
  };
1453
1493
  pendingApprovalsByApprovalId.set(
@@ -1506,6 +1546,20 @@ function runPrompt(input) {
1506
1546
  `Harness '${input.harness.harnessId}' could not find parsed tool call '${toolCall.toolCallId}' for custom tool approval.`
1507
1547
  );
1508
1548
  }
1549
+ if (!hasTool({ tools: activeTools, toolName: toolCall.toolName })) {
1550
+ const output = {
1551
+ type: "execution-denied",
1552
+ reason: getHarnessV1BuiltinToolFilteringDenialReason({
1553
+ toolName: toolCall.toolName
1554
+ })
1555
+ };
1556
+ await control.submitToolResult({
1557
+ toolCallId: toolCall.toolCallId,
1558
+ output
1559
+ });
1560
+ telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1561
+ continue;
1562
+ }
1509
1563
  const customToolApprovalDecision = resolveCustomToolApproval({
1510
1564
  toolName: toolCall.toolName,
1511
1565
  toolApproval: input.toolApproval
@@ -1535,7 +1589,7 @@ function runPrompt(input) {
1535
1589
  telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
1536
1590
  continue;
1537
1591
  }
1538
- const pendingApproval = (_f = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _f : customToolApprovalDecision.type === "request" ? {
1592
+ const pendingApproval = (_g = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _g : customToolApprovalDecision.type === "request" ? {
1539
1593
  approvalId: generateId4(),
1540
1594
  toolCallId: toolCall.toolCallId,
1541
1595
  toolName: toolCall.toolName,
@@ -1581,7 +1635,7 @@ function runPrompt(input) {
1581
1635
  }
1582
1636
  const outcome = await maybeExecuteHostTool({
1583
1637
  event: toolCall,
1584
- tools: input.tools,
1638
+ tools: activeTools,
1585
1639
  sandboxSession: input.sandboxSession,
1586
1640
  abortSignal: input.abortSignal,
1587
1641
  control,
@@ -1613,7 +1667,7 @@ function runPrompt(input) {
1613
1667
  return;
1614
1668
  }
1615
1669
  }
1616
- (_g = input.onTurnFinished) == null ? void 0 : _g.call(input);
1670
+ (_h = input.onTurnFinished) == null ? void 0 : _h.call(input);
1617
1671
  await result.finish(
1618
1672
  finalFinish ? {
1619
1673
  finishReason: finalFinish.finishReason,
@@ -1640,6 +1694,9 @@ function asToolCallTextStreamPart(input) {
1640
1694
  }
1641
1695
  return input.part;
1642
1696
  }
1697
+ function hasTool(input) {
1698
+ return Object.prototype.hasOwnProperty.call(input.tools, input.toolName);
1699
+ }
1643
1700
  async function maybeExecuteHostTool(input) {
1644
1701
  const tool = input.tools[input.event.toolName];
1645
1702
  if (!isExecutableTool(tool)) return { ok: true, output: void 0 };
@@ -1767,7 +1824,9 @@ var HarnessAgentSession = class {
1767
1824
  prompt: options.prompt,
1768
1825
  instructions: options.instructions,
1769
1826
  tools: options.tools,
1827
+ activeTools: options.activeTools,
1770
1828
  toolSpecs: options.toolSpecs,
1829
+ builtinToolFiltering: options.builtinToolFiltering,
1771
1830
  sandboxSession: sandboxSession.restricted(),
1772
1831
  sessionWorkDir: this.sessionWorkDir,
1773
1832
  runtimeContext: options.runtimeContext,
@@ -1805,7 +1864,9 @@ var HarnessAgentSession = class {
1805
1864
  mode: "continue",
1806
1865
  instructions: options.instructions,
1807
1866
  tools: options.tools,
1867
+ activeTools: options.activeTools,
1808
1868
  toolSpecs: options.toolSpecs,
1869
+ builtinToolFiltering: options.builtinToolFiltering,
1809
1870
  sandboxSession: sandboxSession.restricted(),
1810
1871
  sessionWorkDir: this.sessionWorkDir,
1811
1872
  runtimeContext: options.runtimeContext,
@@ -2458,6 +2519,66 @@ function buildObservability(options) {
2458
2519
  };
2459
2520
  }
2460
2521
 
2522
+ // src/agent/internal/tool-filtering.ts
2523
+ import { NoSuchToolError } from "ai";
2524
+ function resolveHarnessAgentToolFiltering(input) {
2525
+ if (input.activeTools !== void 0 && input.inactiveTools !== void 0) {
2526
+ throw new Error(
2527
+ "HarnessAgent: pass either `activeTools` or `inactiveTools`, not both."
2528
+ );
2529
+ }
2530
+ const allToolNames = Object.keys(input.allTools);
2531
+ const activeTools = dedupeToolNames({ toolNames: input.activeTools });
2532
+ const inactiveTools = dedupeToolNames({ toolNames: input.inactiveTools });
2533
+ validateToolNames({
2534
+ requestedToolNames: activeTools != null ? activeTools : inactiveTools,
2535
+ availableToolNames: allToolNames
2536
+ });
2537
+ const userToolNames = Object.keys(input.userTools);
2538
+ const activeUserToolNames = activeTools != null ? userToolNames.filter((name3) => activeTools.includes(name3)) : inactiveTools != null ? userToolNames.filter((name3) => !inactiveTools.includes(name3)) : userToolNames;
2539
+ const builtinToolNames = Object.keys(input.harness.builtinTools);
2540
+ const disabledBuiltinToolNames = activeTools != null ? builtinToolNames.filter((name3) => !activeTools.includes(name3)) : inactiveTools != null ? builtinToolNames.filter((name3) => inactiveTools.includes(name3)) : [];
2541
+ const builtinToolFiltering = disabledBuiltinToolNames.length > 0 ? activeTools != null ? {
2542
+ mode: "allow",
2543
+ toolNames: builtinToolNames.filter(
2544
+ (name3) => activeTools.includes(name3)
2545
+ )
2546
+ } : { mode: "deny", toolNames: disabledBuiltinToolNames } : void 0;
2547
+ if (builtinToolFiltering != null && input.harness.supportsBuiltinToolFiltering !== true && input.harness.supportsBuiltinToolApprovals !== true) {
2548
+ throw new HarnessCapabilityUnsupportedError({
2549
+ message: `Harness '${input.harness.harnessId}' does not support built-in tool filtering controls.`,
2550
+ harnessId: input.harness.harnessId
2551
+ });
2552
+ }
2553
+ return {
2554
+ activeUserTools: filterToolSet({
2555
+ tools: input.userTools,
2556
+ toolNames: activeUserToolNames
2557
+ }),
2558
+ ...builtinToolFiltering != null ? { builtinToolFiltering } : {}
2559
+ };
2560
+ }
2561
+ function dedupeToolNames(input) {
2562
+ return input.toolNames == null ? void 0 : Array.from(new Set(input.toolNames));
2563
+ }
2564
+ function validateToolNames(input) {
2565
+ if (input.requestedToolNames == null) return;
2566
+ for (const toolName of input.requestedToolNames) {
2567
+ if (!input.availableToolNames.includes(toolName)) {
2568
+ throw new NoSuchToolError({
2569
+ toolName,
2570
+ availableTools: [...input.availableToolNames]
2571
+ });
2572
+ }
2573
+ }
2574
+ }
2575
+ function filterToolSet(input) {
2576
+ const allowed = new Set(input.toolNames);
2577
+ return Object.fromEntries(
2578
+ Object.entries(input.tools).filter(([name3]) => allowed.has(name3))
2579
+ );
2580
+ }
2581
+
2461
2582
  // src/agent/harness-agent.ts
2462
2583
  var HarnessAgent = class {
2463
2584
  constructor(settings) {
@@ -2468,10 +2589,23 @@ var HarnessAgent = class {
2468
2589
  this.settings = settings;
2469
2590
  this.sandboxConfig = sandboxConfig;
2470
2591
  this.id = settings.id;
2471
- this.userTools = (_a3 = settings.tools) != null ? _a3 : {};
2592
+ const userTools = (_a3 = settings.tools) != null ? _a3 : {};
2472
2593
  this.permissionMode = resolvePermissionMode({
2473
2594
  permissionMode: settings.permissionMode
2474
2595
  });
2596
+ const tools = {
2597
+ ...settings.harness.builtinTools,
2598
+ ...userTools
2599
+ };
2600
+ const toolFiltering = resolveHarnessAgentToolFiltering({
2601
+ harness: settings.harness,
2602
+ userTools,
2603
+ allTools: tools,
2604
+ activeTools: settings.activeTools,
2605
+ inactiveTools: settings.inactiveTools
2606
+ });
2607
+ this.activeUserTools = toolFiltering.activeUserTools;
2608
+ this.builtinToolFiltering = toolFiltering.builtinToolFiltering;
2475
2609
  if (Object.keys(settings.harness.builtinTools).length > 0 && permissionModeNeedsBuiltinSupport({
2476
2610
  permissionMode: this.permissionMode
2477
2611
  }) && settings.harness.supportsBuiltinToolApprovals !== true) {
@@ -2480,10 +2614,7 @@ var HarnessAgent = class {
2480
2614
  harnessId: settings.harness.harnessId
2481
2615
  });
2482
2616
  }
2483
- this.tools = {
2484
- ...settings.harness.builtinTools,
2485
- ...this.userTools
2486
- };
2617
+ this.tools = tools;
2487
2618
  }
2488
2619
  /** Identifier of the harness backing this agent. */
2489
2620
  get harnessId() {
@@ -2592,6 +2723,7 @@ var HarnessAgent = class {
2592
2723
  resumeFrom: validatedResumeFrom,
2593
2724
  continueFrom: effectiveContinueFrom,
2594
2725
  permissionMode: this.permissionMode,
2726
+ builtinToolFiltering: this.builtinToolFiltering,
2595
2727
  abortSignal,
2596
2728
  observability: buildObservability({ settings: this.settings })
2597
2729
  };
@@ -2693,7 +2825,9 @@ var HarnessAgent = class {
2693
2825
  return input.session.continueTurn({
2694
2826
  instructions: this.settings.instructions,
2695
2827
  tools: this.tools,
2828
+ activeTools: this.activeUserTools,
2696
2829
  toolSpecs: this._toToolSpecs(),
2830
+ builtinToolFiltering: this.builtinToolFiltering,
2697
2831
  runtimeContext: input.runtimeContext,
2698
2832
  abortSignal: input.abortSignal,
2699
2833
  telemetry: this.settings.telemetry,
@@ -2704,7 +2838,9 @@ var HarnessAgent = class {
2704
2838
  prompt: input.turnInput.prompt,
2705
2839
  instructions: this.settings.instructions,
2706
2840
  tools: this.tools,
2841
+ activeTools: this.activeUserTools,
2707
2842
  toolSpecs: this._toToolSpecs(),
2843
+ builtinToolFiltering: this.builtinToolFiltering,
2708
2844
  runtimeContext: input.runtimeContext,
2709
2845
  abortSignal: input.abortSignal,
2710
2846
  telemetry: this.settings.telemetry
@@ -2771,7 +2907,7 @@ var HarnessAgent = class {
2771
2907
  _toToolSpecs() {
2772
2908
  const specs = [];
2773
2909
  for (const [name3, tool] of Object.entries(
2774
- this.userTools
2910
+ this.activeUserTools
2775
2911
  )) {
2776
2912
  const t = tool;
2777
2913
  let inputSchema;