@ai-sdk/harness 1.0.92 → 1.0.94

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.
@@ -42,7 +42,8 @@ var HarnessCapabilityUnsupportedError = class extends (_b2 = HarnessError, _a2 =
42
42
  import {
43
43
  asArray as asArray2,
44
44
  asSchema,
45
- generateId as generateId5
45
+ generateId as generateId5,
46
+ validateTypes
46
47
  } from "@ai-sdk/provider-utils";
47
48
 
48
49
  // src/agent/internal/lifecycle-state-validation.ts
@@ -107,7 +108,8 @@ async function validateLifecycleStateData(input) {
107
108
  specificationVersion: state.specificationVersion,
108
109
  data,
109
110
  ...state.pendingToolApprovals !== void 0 ? { pendingToolApprovals: state.pendingToolApprovals } : {},
110
- ...state.pendingToolResults !== void 0 ? { pendingToolResults: state.pendingToolResults } : {}
111
+ ...state.pendingToolResults !== void 0 ? { pendingToolResults: state.pendingToolResults } : {},
112
+ ...state.turnSettings !== void 0 ? { turnSettings: state.turnSettings } : {}
111
113
  };
112
114
  }
113
115
 
@@ -887,6 +889,10 @@ function translateStreamPart(event, options = {}) {
887
889
  providerMetadata: event.harnessMetadata
888
890
  }
889
891
  ];
892
+ case "tool-input-start":
893
+ case "tool-input-delta":
894
+ case "tool-input-end":
895
+ return [event];
890
896
  case "tool-call":
891
897
  return [];
892
898
  case "tool-approval-request":
@@ -986,9 +992,45 @@ function translateStreamPart(event, options = {}) {
986
992
  }
987
993
 
988
994
  // src/agent/internal/strip-work-dir.ts
995
+ function createToolInputWorkDirStripper({
996
+ sessionWorkDir
997
+ }) {
998
+ const pendingByToolCallId = /* @__PURE__ */ new Map();
999
+ return (part) => {
1000
+ var _a4;
1001
+ if (sessionWorkDir.length === 0) return [part];
1002
+ if (part.type === "tool-input-start") {
1003
+ pendingByToolCallId.set(part.id, "");
1004
+ return [part];
1005
+ }
1006
+ if (part.type === "tool-input-delta") {
1007
+ const stripped2 = stripStreamingString({
1008
+ value: ((_a4 = pendingByToolCallId.get(part.id)) != null ? _a4 : "") + part.delta,
1009
+ workDir: sessionWorkDir,
1010
+ final: false
1011
+ });
1012
+ pendingByToolCallId.set(part.id, stripped2.pending);
1013
+ return stripped2.output.length === 0 ? [] : [{ ...part, delta: stripped2.output }];
1014
+ }
1015
+ const pending = pendingByToolCallId.get(part.id);
1016
+ pendingByToolCallId.delete(part.id);
1017
+ if (pending == null || pending.length === 0) return [part];
1018
+ const stripped = stripStreamingString({
1019
+ value: pending,
1020
+ workDir: sessionWorkDir,
1021
+ final: true
1022
+ });
1023
+ return stripped.output.length === 0 ? [part] : [
1024
+ { type: "tool-input-delta", id: part.id, delta: stripped.output },
1025
+ part
1026
+ ];
1027
+ };
1028
+ }
989
1029
  function stripWorkDir(part, sessionWorkDir) {
990
1030
  if (sessionWorkDir.length === 0) return part;
991
1031
  switch (part.type) {
1032
+ case "tool-input-delta":
1033
+ return { ...part, delta: stripString(part.delta, sessionWorkDir) };
992
1034
  case "tool-call":
993
1035
  return { ...part, input: stripString(part.input, sessionWorkDir) };
994
1036
  case "tool-result":
@@ -1005,6 +1047,42 @@ function stripWorkDir(part, sessionWorkDir) {
1005
1047
  function stripString(value, workDir) {
1006
1048
  return value.split(`${workDir}/`).join("").split(workDir).join(".");
1007
1049
  }
1050
+ function stripStreamingString({
1051
+ value,
1052
+ workDir,
1053
+ final
1054
+ }) {
1055
+ let remaining = value;
1056
+ let output = "";
1057
+ while (remaining.length > 0) {
1058
+ const matchIndex = remaining.indexOf(workDir);
1059
+ if (matchIndex >= 0) {
1060
+ output += remaining.slice(0, matchIndex);
1061
+ const followingIndex = matchIndex + workDir.length;
1062
+ if (followingIndex === remaining.length && !final) {
1063
+ return { output, pending: remaining.slice(matchIndex) };
1064
+ }
1065
+ if (remaining[followingIndex] === "/") {
1066
+ remaining = remaining.slice(followingIndex + 1);
1067
+ } else {
1068
+ output += ".";
1069
+ remaining = remaining.slice(followingIndex);
1070
+ }
1071
+ continue;
1072
+ }
1073
+ if (final) return { output: output + remaining, pending: "" };
1074
+ let pendingLength = Math.min(remaining.length, workDir.length - 1);
1075
+ while (pendingLength > 0 && !workDir.startsWith(remaining.slice(-pendingLength))) {
1076
+ pendingLength -= 1;
1077
+ }
1078
+ const outputLength = remaining.length - pendingLength;
1079
+ return {
1080
+ output: output + remaining.slice(0, outputLength),
1081
+ pending: remaining.slice(outputLength)
1082
+ };
1083
+ }
1084
+ return { output, pending: "" };
1085
+ }
1008
1086
  function stripDeep(value, workDir) {
1009
1087
  if (typeof value === "string") return stripString(value, workDir);
1010
1088
  if (Array.isArray(value)) return value.map((item) => stripDeep(item, workDir));
@@ -1491,7 +1569,7 @@ function runPrompt(input) {
1491
1569
  const telemetry = createTurnTelemetry({
1492
1570
  telemetry: input.telemetry,
1493
1571
  harnessId: input.harness.harnessId,
1494
- modelId: input.session.modelId,
1572
+ modelId: input.model,
1495
1573
  instructions: input.instructions,
1496
1574
  promptText: input.prompt != null ? promptToText(input.prompt) : "",
1497
1575
  runtimeContext: input.runtimeContext
@@ -1511,17 +1589,23 @@ function runPrompt(input) {
1511
1589
  result.fail(err);
1512
1590
  };
1513
1591
  const done = (async () => {
1514
- var _a5, _b5, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l, _m;
1592
+ var _a5, _b5, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l, _m, _n;
1515
1593
  let bridge;
1516
1594
  try {
1517
1595
  bridge = await toHarnessStream({
1518
- invoke: input.mode === "continue" ? (emit) => input.session.doContinueTurn({
1519
- responseFormat: input.responseFormat,
1520
- tools: input.toolSpecs,
1521
- instructions: input.instructions,
1522
- abortSignal: input.abortSignal,
1523
- emit
1524
- }) : (emit) => {
1596
+ invoke: input.mode === "continue" ? (emit) => {
1597
+ var _a6;
1598
+ return input.session.doContinueTurn({
1599
+ model: input.model,
1600
+ skills: (_a6 = input.skills) != null ? _a6 : [],
1601
+ responseFormat: input.responseFormat,
1602
+ tools: input.toolSpecs,
1603
+ instructions: input.instructions,
1604
+ abortSignal: input.abortSignal,
1605
+ emit
1606
+ });
1607
+ } : (emit) => {
1608
+ var _a6;
1525
1609
  if (input.prompt == null) {
1526
1610
  throw new Error(
1527
1611
  'runPrompt: `prompt` is required for mode "prompt".'
@@ -1529,6 +1613,8 @@ function runPrompt(input) {
1529
1613
  }
1530
1614
  return input.session.doPromptTurn({
1531
1615
  prompt: input.prompt,
1616
+ model: input.model,
1617
+ skills: (_a6 = input.skills) != null ? _a6 : [],
1532
1618
  responseFormat: input.responseFormat,
1533
1619
  tools: input.toolSpecs,
1534
1620
  instructions: input.instructions,
@@ -1551,6 +1637,9 @@ function runPrompt(input) {
1551
1637
  const { stream, control } = bridge;
1552
1638
  (_a5 = input.onPromptControlAvailable) == null ? void 0 : _a5.call(input, control);
1553
1639
  const reader = stream.getReader();
1640
+ const stripToolInputWorkDir = createToolInputWorkDirStripper({
1641
+ sessionWorkDir: input.sessionWorkDir
1642
+ });
1554
1643
  const toolCallsByToolCallId = /* @__PURE__ */ new Map();
1555
1644
  const rawToolCallsByToolCallId = /* @__PURE__ */ new Map();
1556
1645
  const translateOptions = {
@@ -1795,7 +1884,7 @@ function runPrompt(input) {
1795
1884
  toolName: approval.toolName,
1796
1885
  input: approval.input
1797
1886
  };
1798
- await telemetry.start(input.session.modelId);
1887
+ await telemetry.start(input.model);
1799
1888
  await telemetry.toolStart({
1800
1889
  toolCallId: rawToolCall.toolCallId,
1801
1890
  toolName: rawToolCall.toolName,
@@ -1894,11 +1983,25 @@ function runPrompt(input) {
1894
1983
  }
1895
1984
  }
1896
1985
  if (value.type === "stream-start") {
1897
- await telemetry.start((_e2 = value.modelId) != null ? _e2 : input.session.modelId);
1986
+ await telemetry.start((_e2 = value.modelId) != null ? _e2 : input.model);
1898
1987
  }
1899
1988
  if (value.type !== "stream-start" && value.type !== "finish-step" && value.type !== "finish" && value.type !== "error") {
1900
1989
  await telemetry.ensureStepOpen();
1901
1990
  }
1991
+ if (value.type === "tool-input-start" || value.type === "tool-input-delta" || value.type === "tool-input-end") {
1992
+ if (settledHostToolCallIds.has(value.id) || settledBuiltinApprovalToolCallIds.has(value.id)) {
1993
+ continue;
1994
+ }
1995
+ for (const displayValue2 of stripToolInputWorkDir(value)) {
1996
+ for (const part of translateStreamPart(
1997
+ displayValue2,
1998
+ translateOptions
1999
+ )) {
2000
+ result.enqueue(part);
2001
+ }
2002
+ }
2003
+ continue;
2004
+ }
1902
2005
  const displayValue = stripWorkDir(value, input.sessionWorkDir);
1903
2006
  const settledHostInputReplay = (displayValue.type === "tool-call" || displayValue.type === "tool-result" || displayValue.type === "tool-approval-request") && settledHostToolCallIds.has(displayValue.toolCallId);
1904
2007
  const settledBuiltinApprovalReplay = (displayValue.type === "tool-call" || displayValue.type === "tool-approval-request") && settledBuiltinApprovalToolCallIds.has(displayValue.toolCallId);
@@ -1944,12 +2047,14 @@ function runPrompt(input) {
1944
2047
  if (value.type === "error" && displayValue.type === "error") {
1945
2048
  await waitForOutstandingHostToolExecutions();
1946
2049
  await telemetry.error(value.error);
1947
- logBridgeError({
1948
- harnessId: input.harness.harnessId,
1949
- sessionId: input.session.sessionId,
1950
- context: "harness stream error",
1951
- error: value.error
1952
- });
2050
+ if (!((_g2 = input.abortSignal) == null ? void 0 : _g2.aborted)) {
2051
+ logBridgeError({
2052
+ harnessId: input.harness.harnessId,
2053
+ sessionId: input.session.sessionId,
2054
+ context: "harness stream error",
2055
+ error: value.error
2056
+ });
2057
+ }
1953
2058
  settleFailure(displayValue.error);
1954
2059
  return;
1955
2060
  }
@@ -2003,13 +2108,13 @@ function runPrompt(input) {
2003
2108
  );
2004
2109
  }
2005
2110
  const rawToolCall = rawToolCallsByToolCallId.get(value.toolCallId);
2006
- const pendingApproval = (_i = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _i : {
2111
+ const pendingApproval = (_j = pendingApprovalsByApprovalId.get(value.approvalId)) != null ? _j : {
2007
2112
  approvalId: value.approvalId,
2008
2113
  toolCallId: value.toolCallId,
2009
2114
  toolName: toolCall.toolName,
2010
- input: (_g2 = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _g2 : JSON.stringify(toolCall.input),
2115
+ input: (_h = rawToolCall == null ? void 0 : rawToolCall.input) != null ? _h : JSON.stringify(toolCall.input),
2011
2116
  kind: "builtin",
2012
- providerExecuted: (_h = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _h : true,
2117
+ providerExecuted: (_i = rawToolCall == null ? void 0 : rawToolCall.providerExecuted) != null ? _i : true,
2013
2118
  ...(rawToolCall == null ? void 0 : rawToolCall.nativeName) !== void 0 ? { nativeName: rawToolCall.nativeName } : {}
2014
2119
  };
2015
2120
  pendingApprovalsByApprovalId.set(
@@ -2114,7 +2219,7 @@ function runPrompt(input) {
2114
2219
  await telemetry.toolEnd(toolCall.toolCallId, { ok: true, output });
2115
2220
  continue;
2116
2221
  }
2117
- const pendingApproval = (_j = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _j : customToolApprovalDecision.type === "request" ? {
2222
+ const pendingApproval = (_k = pendingApprovalsByToolCallId.get(toolCall.toolCallId)) != null ? _k : customToolApprovalDecision.type === "request" ? {
2118
2223
  approvalId: generateId4(),
2119
2224
  toolCallId: toolCall.toolCallId,
2120
2225
  toolName: toolCall.toolName,
@@ -2213,15 +2318,15 @@ function runPrompt(input) {
2213
2318
  }
2214
2319
  }
2215
2320
  await waitForOutstandingHostToolExecutions();
2216
- const isTurnSuspending = ((_k = input.isTurnSuspending) == null ? void 0 : _k.call(input)) === true;
2321
+ const isTurnSuspending = ((_l = input.isTurnSuspending) == null ? void 0 : _l.call(input)) === true;
2217
2322
  if (isTurnSuspending) {
2218
2323
  if (finalFinish == null) {
2219
2324
  result.discardCurrentStepContent();
2220
2325
  }
2221
2326
  } else if (finalFinish != null) {
2222
- (_l = input.onTurnFinished) == null ? void 0 : _l.call(input);
2327
+ (_m = input.onTurnFinished) == null ? void 0 : _m.call(input);
2223
2328
  } else {
2224
- (_m = input.onTurnFailed) == null ? void 0 : _m.call(input);
2329
+ (_n = input.onTurnFailed) == null ? void 0 : _n.call(input);
2225
2330
  }
2226
2331
  await result.finish(
2227
2332
  finalFinish ? {
@@ -2368,6 +2473,7 @@ var HarnessAgentSession = class {
2368
2473
  for (const pendingResult of (_c = options.pendingToolResults) != null ? _c : []) {
2369
2474
  this.pendingToolResults.set(pendingResult.toolCallId, pendingResult);
2370
2475
  }
2476
+ this.persistedTurnSettings = options.turnSettings;
2371
2477
  this.turnState = (_d = options.turnState) != null ? _d : this.pendingToolApprovals.size > 0 ? "awaiting-approval" : this.pendingToolResults.size > 0 ? "awaiting-tool-result" : "idle";
2372
2478
  this.isResume = options.underlyingSession.isResume;
2373
2479
  }
@@ -2399,6 +2505,18 @@ var HarnessAgentSession = class {
2399
2505
  promptTurn(options) {
2400
2506
  const session = this.requireReusableSession();
2401
2507
  this.requirePromptableTurn();
2508
+ this.persistedTurnSettings = {
2509
+ ...options.model == null ? {} : { model: options.model },
2510
+ skills: options.skills,
2511
+ ...options.instructions == null ? {} : { instructions: options.instructions },
2512
+ tools: options.toolSpecs
2513
+ };
2514
+ this.activeTurnSettings = {
2515
+ persisted: this.persistedTurnSettings,
2516
+ tools: options.tools,
2517
+ activeTools: options.activeTools,
2518
+ builtinToolFiltering: options.builtinToolFiltering
2519
+ };
2402
2520
  const sandboxSession = this.getSandboxSession();
2403
2521
  const turnId = this.startTrackedTurn();
2404
2522
  try {
@@ -2406,6 +2524,8 @@ var HarnessAgentSession = class {
2406
2524
  harness: this.harness,
2407
2525
  session,
2408
2526
  prompt: options.prompt,
2527
+ model: options.model,
2528
+ skills: options.skills,
2409
2529
  instructions: options.instructions,
2410
2530
  tools: options.tools,
2411
2531
  activeTools: options.activeTools,
@@ -2448,7 +2568,10 @@ var HarnessAgentSession = class {
2448
2568
  isTurnSuspending: () => this.activeTurnSequence === turnId && this.suspendedTurnState != null,
2449
2569
  onStopConditionMet: () => this.captureStopConditionBoundary({ session, turnId })
2450
2570
  });
2451
- return turn;
2571
+ return {
2572
+ ...turn,
2573
+ ready: this.waitForPromptControl({ turnId })
2574
+ };
2452
2575
  } catch (error) {
2453
2576
  this.finishTrackedTurn({ turnId });
2454
2577
  throw error;
@@ -2457,6 +2580,15 @@ var HarnessAgentSession = class {
2457
2580
  continueTurn(options) {
2458
2581
  const session = this.requireReusableSession();
2459
2582
  this.requireContinuableTurn();
2583
+ const turnSettings = this.resolveActiveTurnSettings({
2584
+ model: options.model,
2585
+ skills: options.skills,
2586
+ instructions: options.instructions,
2587
+ tools: options.tools,
2588
+ activeTools: options.activeTools,
2589
+ toolSpecs: options.toolSpecs,
2590
+ builtinToolFiltering: options.builtinToolFiltering
2591
+ });
2460
2592
  const sandboxSession = this.getSandboxSession();
2461
2593
  const turnId = this.startTrackedTurn();
2462
2594
  try {
@@ -2464,11 +2596,13 @@ var HarnessAgentSession = class {
2464
2596
  harness: this.harness,
2465
2597
  session,
2466
2598
  mode: "continue",
2467
- instructions: options.instructions,
2468
- tools: options.tools,
2469
- activeTools: options.activeTools,
2470
- toolSpecs: options.toolSpecs,
2471
- builtinToolFiltering: options.builtinToolFiltering,
2599
+ model: turnSettings.persisted.model,
2600
+ skills: turnSettings.persisted.skills,
2601
+ instructions: turnSettings.persisted.instructions,
2602
+ tools: turnSettings.tools,
2603
+ activeTools: turnSettings.activeTools,
2604
+ toolSpecs: [...turnSettings.persisted.tools],
2605
+ builtinToolFiltering: turnSettings.builtinToolFiltering,
2472
2606
  sandboxSession: getRestrictedSandboxSession(sandboxSession),
2473
2607
  sessionWorkDir: this.sessionWorkDir,
2474
2608
  runtimeContext: options.runtimeContext,
@@ -2508,7 +2642,10 @@ var HarnessAgentSession = class {
2508
2642
  isTurnSuspending: () => this.activeTurnSequence === turnId && this.suspendedTurnState != null,
2509
2643
  onStopConditionMet: () => this.captureStopConditionBoundary({ session, turnId })
2510
2644
  });
2511
- return turn;
2645
+ return {
2646
+ ...turn,
2647
+ ready: this.waitForPromptControl({ turnId })
2648
+ };
2512
2649
  } catch (error) {
2513
2650
  this.finishTrackedTurn({ turnId });
2514
2651
  throw error;
@@ -2676,6 +2813,7 @@ var HarnessAgentSession = class {
2676
2813
  return Array.from(this.pendingToolResults.values());
2677
2814
  }
2678
2815
  addPendingToolState(state) {
2816
+ const turnSettings = this.persistedTurnSettings;
2679
2817
  const pendingToolApprovals = this.getPendingToolApprovals();
2680
2818
  const pendingToolResults = this.getPendingToolResults();
2681
2819
  if (pendingToolApprovals.length === 0 && pendingToolResults.length === 0) {
@@ -2683,11 +2821,13 @@ var HarnessAgentSession = class {
2683
2821
  type: state.type,
2684
2822
  harnessId: state.harnessId,
2685
2823
  specificationVersion: state.specificationVersion,
2686
- data: state.data
2824
+ data: state.data,
2825
+ ...turnSettings == null ? {} : { turnSettings }
2687
2826
  };
2688
2827
  }
2689
2828
  return {
2690
2829
  ...state,
2830
+ ...turnSettings == null ? {} : { turnSettings },
2691
2831
  ...pendingToolApprovals.length > 0 ? { pendingToolApprovals } : {},
2692
2832
  ...pendingToolResults.length > 0 ? { pendingToolResults } : {}
2693
2833
  };
@@ -2787,6 +2927,11 @@ var HarnessAgentSession = class {
2787
2927
  }
2788
2928
  this.settleActivePromptControl(options.control);
2789
2929
  }
2930
+ async waitForPromptControl(options) {
2931
+ const activePromptControl = this.activePromptControl;
2932
+ if ((activePromptControl == null ? void 0 : activePromptControl.turnId) !== options.turnId) return;
2933
+ await activePromptControl.promise;
2934
+ }
2790
2935
  settleActivePromptControl(control) {
2791
2936
  const activePromptControl = this.activePromptControl;
2792
2937
  if (activePromptControl == null || activePromptControl.settled) return;
@@ -2807,8 +2952,40 @@ var HarnessAgentSession = class {
2807
2952
  this.pendingToolApprovals.clear();
2808
2953
  this.pendingToolResults.clear();
2809
2954
  this.suspendedTurnState = void 0;
2955
+ this.activeTurnSettings = void 0;
2956
+ this.persistedTurnSettings = void 0;
2810
2957
  this.turnState = "idle";
2811
2958
  }
2959
+ resolveActiveTurnSettings(options) {
2960
+ var _a4;
2961
+ if (this.activeTurnSettings != null) {
2962
+ return this.activeTurnSettings;
2963
+ }
2964
+ const persisted = (_a4 = this.persistedTurnSettings) != null ? _a4 : {
2965
+ ...options.model == null ? {} : { model: options.model },
2966
+ skills: options.skills,
2967
+ ...options.instructions == null ? {} : { instructions: options.instructions },
2968
+ tools: options.toolSpecs
2969
+ };
2970
+ this.persistedTurnSettings = persisted;
2971
+ const activeTools = {};
2972
+ for (const toolSpec of persisted.tools) {
2973
+ const tool = options.activeTools[toolSpec.name];
2974
+ if (tool == null) {
2975
+ throw new Error(
2976
+ `HarnessAgent cannot continue turn because tool '${toolSpec.name}' is no longer available.`
2977
+ );
2978
+ }
2979
+ activeTools[toolSpec.name] = tool;
2980
+ }
2981
+ this.activeTurnSettings = {
2982
+ persisted,
2983
+ tools: options.tools,
2984
+ activeTools,
2985
+ builtinToolFiltering: options.builtinToolFiltering
2986
+ };
2987
+ return this.activeTurnSettings;
2988
+ }
2812
2989
  endLocalHandle(options) {
2813
2990
  this.clearActivePromptControl();
2814
2991
  this.sessionState = options.sessionState;
@@ -3395,7 +3572,6 @@ var HarnessAgent = class {
3395
3572
  activeTools: settings.activeTools,
3396
3573
  inactiveTools: settings.inactiveTools
3397
3574
  });
3398
- this.activeUserTools = toolFiltering.activeUserTools;
3399
3575
  this.builtinToolFiltering = toolFiltering.builtinToolFiltering;
3400
3576
  if (Object.keys(settings.harness.builtinTools).length > 0 && permissionModeNeedsBuiltinSupport({
3401
3577
  permissionMode: this.permissionMode
@@ -3419,7 +3595,7 @@ var HarnessAgent = class {
3419
3595
  * `session.destroy()`.
3420
3596
  */
3421
3597
  async createSession(options) {
3422
- var _a4, _b4;
3598
+ var _a4, _b4, _c;
3423
3599
  const sessionId = (_a4 = options == null ? void 0 : options.sessionId) != null ? _a4 : generateId5();
3424
3600
  const resumeFrom = options == null ? void 0 : options.resumeFrom;
3425
3601
  const continueFrom = options == null ? void 0 : options.continueFrom;
@@ -3491,6 +3667,7 @@ var HarnessAgent = class {
3491
3667
  "HarnessAgent.createSession: configure `sandbox` on HarnessAgent or pass `sandboxSession` to createSession()."
3492
3668
  );
3493
3669
  }
3670
+ const recipe = await ((_c = harness.getBootstrap) == null ? void 0 : _c.call(harness, { abortSignal }));
3494
3671
  if (isResumedSession) {
3495
3672
  if (sandboxProvider.resumeSession == null) {
3496
3673
  throw new HarnessCapabilityUnsupportedError({
@@ -3509,11 +3686,25 @@ var HarnessAgent = class {
3509
3686
  sessionId,
3510
3687
  workDir: this.sandboxConfig.workDir
3511
3688
  });
3512
- } else {
3513
- let recipe;
3514
- if (harness.getBootstrap != null) {
3515
- recipe = await harness.getBootstrap({ abortSignal });
3689
+ if (recipe != null) {
3690
+ const recipeIdentity = await hashHarnessBootstrap(recipe);
3691
+ try {
3692
+ await applyBootstrapRecipe({
3693
+ session: resumedSandboxSession.restricted(),
3694
+ recipe,
3695
+ identity: recipeIdentity,
3696
+ defaultWorkingDirectory: resumedSandboxSession.defaultWorkingDirectory,
3697
+ abortSignal
3698
+ });
3699
+ } catch (err) {
3700
+ await cleanupAfterStartFailure({
3701
+ sandboxSession,
3702
+ ownsSandboxLifecycle
3703
+ });
3704
+ throw err;
3705
+ }
3516
3706
  }
3707
+ } else {
3517
3708
  const sandboxBootstrapPlan = await createSandboxBootstrapPlan({
3518
3709
  recipe,
3519
3710
  settings: this.sandboxConfig
@@ -3573,7 +3764,6 @@ var HarnessAgent = class {
3573
3764
  try {
3574
3765
  const baseStartOptions = {
3575
3766
  sessionId,
3576
- skills: this.settings.skills,
3577
3767
  resumeFrom: validatedResumeFrom,
3578
3768
  continueFrom: effectiveContinueFrom,
3579
3769
  permissionMode: this.permissionMode,
@@ -3596,6 +3786,7 @@ var HarnessAgent = class {
3596
3786
  toolApproval: this.settings.toolApproval,
3597
3787
  pendingToolApprovals: effectiveContinueFrom == null ? void 0 : effectiveContinueFrom.pendingToolApprovals,
3598
3788
  pendingToolResults: effectiveContinueFrom == null ? void 0 : effectiveContinueFrom.pendingToolResults,
3789
+ turnSettings: effectiveContinueFrom == null ? void 0 : effectiveContinueFrom.turnSettings,
3599
3790
  turnState: effectiveContinueFrom == null ? "idle" : effectiveContinueFrom.pendingToolApprovals != null && effectiveContinueFrom.pendingToolApprovals.length > 0 ? "awaiting-approval" : effectiveContinueFrom.pendingToolResults != null && effectiveContinueFrom.pendingToolResults.length > 0 ? "awaiting-tool-result" : "suspended"
3600
3791
  });
3601
3792
  } catch (error) {
@@ -3607,30 +3798,27 @@ var HarnessAgent = class {
3607
3798
  }
3608
3799
  }
3609
3800
  async generate(options) {
3610
- const turnInput = this._resolveTurnInput(options);
3801
+ const continueTurnInput = this._resolveContinueTurnInput(options);
3611
3802
  const runtimeContext = {};
3612
- const responseFormat = await this._resolveResponseFormat();
3613
- const { result, done } = this._startTurn({
3803
+ const { result, done } = continueTurnInput == null ? await this._startPromptTurn({ options, runtimeContext }) : await this._startContinueTurn({
3614
3804
  session: options.session,
3615
- turnInput,
3805
+ turnInput: continueTurnInput,
3616
3806
  runtimeContext,
3617
- abortSignal: options.abortSignal,
3618
- responseFormat
3807
+ abortSignal: options.abortSignal
3619
3808
  });
3620
3809
  await done;
3621
3810
  return this._toGenerateResult(result);
3622
3811
  }
3623
3812
  async stream(options) {
3624
- const turnInput = this._resolveTurnInput(options);
3813
+ const continueTurnInput = this._resolveContinueTurnInput(options);
3625
3814
  const runtimeContext = {};
3626
- const responseFormat = await this._resolveResponseFormat();
3627
- const { result } = this._startTurn({
3815
+ const { result, ready } = continueTurnInput == null ? await this._startPromptTurn({ options, runtimeContext }) : await this._startContinueTurn({
3628
3816
  session: options.session,
3629
- turnInput,
3817
+ turnInput: continueTurnInput,
3630
3818
  runtimeContext,
3631
- abortSignal: options.abortSignal,
3632
- responseFormat
3819
+ abortSignal: options.abortSignal
3633
3820
  });
3821
+ await ready;
3634
3822
  return result;
3635
3823
  }
3636
3824
  /**
@@ -3641,17 +3829,14 @@ var HarnessAgent = class {
3641
3829
  async continueGenerate(options) {
3642
3830
  var _a4, _b4;
3643
3831
  const runtimeContext = {};
3644
- const responseFormat = await this._resolveResponseFormat();
3645
- const { result, done } = this._startTurn({
3832
+ const { result, done } = await this._startContinueTurn({
3646
3833
  session: options.session,
3647
3834
  turnInput: {
3648
- mode: "continue",
3649
3835
  toolApprovalContinuations: (_a4 = options.toolApprovalContinuations) != null ? _a4 : [],
3650
3836
  toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
3651
3837
  },
3652
3838
  runtimeContext,
3653
- abortSignal: options.abortSignal,
3654
- responseFormat
3839
+ abortSignal: options.abortSignal
3655
3840
  });
3656
3841
  await done;
3657
3842
  return this._toGenerateResult(result);
@@ -3667,18 +3852,16 @@ var HarnessAgent = class {
3667
3852
  async continueStream(options) {
3668
3853
  var _a4, _b4;
3669
3854
  const runtimeContext = {};
3670
- const responseFormat = await this._resolveResponseFormat();
3671
- const { result } = this._startTurn({
3855
+ const { result, ready } = await this._startContinueTurn({
3672
3856
  session: options.session,
3673
3857
  turnInput: {
3674
- mode: "continue",
3675
3858
  toolApprovalContinuations: (_a4 = options.toolApprovalContinuations) != null ? _a4 : [],
3676
3859
  toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
3677
3860
  },
3678
3861
  runtimeContext,
3679
- abortSignal: options.abortSignal,
3680
- responseFormat
3862
+ abortSignal: options.abortSignal
3681
3863
  });
3864
+ await ready;
3682
3865
  return result;
3683
3866
  }
3684
3867
  /**
@@ -3692,50 +3875,53 @@ var HarnessAgent = class {
3692
3875
  await options.session.experimental_steerTurn(options.text);
3693
3876
  }
3694
3877
  // ─── Internals ──────────────────────────────────────────────────────
3695
- _startTurn(input) {
3696
- if (input.turnInput.mode === "continue") {
3697
- return input.session.continueTurn({
3698
- instructions: this.settings.instructions,
3699
- tools: this.tools,
3700
- activeTools: this.activeUserTools,
3701
- toolSpecs: this._toToolSpecs(),
3702
- builtinToolFiltering: this.builtinToolFiltering,
3878
+ async _startPromptTurn(input) {
3879
+ const turnInput = await this._preparePromptTurnInput(input.options);
3880
+ const responseFormat = await this._resolveResponseFormat();
3881
+ return input.options.session.promptTurn({
3882
+ ...this._buildTurnOptions({
3883
+ turnSettings: turnInput,
3884
+ runtimeContext: input.runtimeContext,
3885
+ abortSignal: input.options.abortSignal,
3886
+ responseFormat
3887
+ }),
3888
+ prompt: turnInput.prompt
3889
+ });
3890
+ }
3891
+ async _startContinueTurn(input) {
3892
+ const turnInput = this._prepareContinueTurnInput(input.turnInput);
3893
+ const responseFormat = await this._resolveResponseFormat();
3894
+ return input.session.continueTurn({
3895
+ ...this._buildTurnOptions({
3896
+ turnSettings: turnInput,
3703
3897
  runtimeContext: input.runtimeContext,
3704
3898
  abortSignal: input.abortSignal,
3705
- responseFormat: input.responseFormat,
3706
- output: this.settings.output,
3707
- telemetry: this.settings.telemetry,
3708
- stopConditions: this.stopConditions,
3709
- toolApprovalContinuations: input.turnInput.toolApprovalContinuations,
3710
- toolResultContinuations: input.turnInput.toolResultContinuations
3711
- });
3712
- }
3713
- return input.session.promptTurn({
3714
- prompt: input.turnInput.prompt,
3715
- instructions: this.settings.instructions,
3716
- tools: this.tools,
3717
- activeTools: this.activeUserTools,
3718
- toolSpecs: this._toToolSpecs(),
3719
- builtinToolFiltering: this.builtinToolFiltering,
3899
+ responseFormat
3900
+ }),
3901
+ toolApprovalContinuations: turnInput.toolApprovalContinuations,
3902
+ toolResultContinuations: turnInput.toolResultContinuations
3903
+ });
3904
+ }
3905
+ _buildTurnOptions(input) {
3906
+ return {
3907
+ model: input.turnSettings.model,
3908
+ skills: input.turnSettings.skills,
3909
+ instructions: input.turnSettings.instructions,
3910
+ tools: input.turnSettings.tools,
3911
+ activeTools: input.turnSettings.activeTools,
3912
+ toolSpecs: input.turnSettings.toolSpecs,
3913
+ builtinToolFiltering: input.turnSettings.builtinToolFiltering,
3720
3914
  runtimeContext: input.runtimeContext,
3721
3915
  abortSignal: input.abortSignal,
3722
3916
  responseFormat: input.responseFormat,
3723
3917
  output: this.settings.output,
3724
3918
  telemetry: this.settings.telemetry,
3725
3919
  stopConditions: this.stopConditions
3726
- });
3920
+ };
3727
3921
  }
3728
- /*
3729
- * Reduce AI SDK input to the single user message the harness should run
3730
- * for this turn. The harness session owns prior-turn state (system
3731
- * prompt, assistant turns, tool results) — we never replay it. A bare
3732
- * string is forwarded as-is; a message array is collapsed to its last
3733
- * `role: 'user'` entry. Inputs whose only messages are non-user (system,
3734
- * assistant, tool) have no fresh user input and are rejected.
3735
- */
3736
- _resolveTurnInput(options) {
3922
+ _resolveContinueTurnInput(options) {
3737
3923
  if (typeof options.prompt === "string") {
3738
- return { mode: "prompt", prompt: options.prompt };
3924
+ return void 0;
3739
3925
  }
3740
3926
  const messages = Array.isArray(options.prompt) ? options.prompt : options.messages;
3741
3927
  if (Array.isArray(messages)) {
@@ -3743,15 +3929,30 @@ var HarnessAgent = class {
3743
3929
  const toolResultContinuations = collectHarnessAgentToolResultContinuations({ messages });
3744
3930
  if (toolApprovalContinuations.length > 0 || toolResultContinuations.length > 0) {
3745
3931
  return {
3746
- mode: "continue",
3747
3932
  toolApprovalContinuations,
3748
3933
  toolResultContinuations
3749
3934
  };
3750
3935
  }
3936
+ }
3937
+ return void 0;
3938
+ }
3939
+ /*
3940
+ * Reduce AI SDK input to the single user message the harness should run
3941
+ * for this turn. The harness session owns prior-turn state (system
3942
+ * prompt, assistant turns, tool results) — we never replay it. A bare
3943
+ * string is forwarded as-is; a message array is collapsed to its last
3944
+ * `role: 'user'` entry. Inputs whose only messages are non-user (system,
3945
+ * assistant, tool) have no fresh user input and are rejected.
3946
+ */
3947
+ _resolvePromptTurnInput(options) {
3948
+ if (typeof options.prompt === "string") {
3949
+ return options.prompt;
3950
+ }
3951
+ const messages = Array.isArray(options.prompt) ? options.prompt : options.messages;
3952
+ if (Array.isArray(messages)) {
3751
3953
  for (let i = messages.length - 1; i >= 0; i--) {
3752
3954
  const message = messages[i];
3753
- if ((message == null ? void 0 : message.role) === "user")
3754
- return { mode: "prompt", prompt: message };
3955
+ if ((message == null ? void 0 : message.role) === "user") return message;
3755
3956
  }
3756
3957
  throw new Error(
3757
3958
  'HarnessAgent: messages must contain at least one `role: "user"` entry.'
@@ -3759,15 +3960,111 @@ var HarnessAgent = class {
3759
3960
  }
3760
3961
  throw new Error("HarnessAgent: either `prompt` or `messages` is required.");
3761
3962
  }
3963
+ async _preparePromptTurnInput(options) {
3964
+ var _a4, _b4, _c;
3965
+ let callOptions = options;
3966
+ if (this.settings.callOptionsSchema != null && options.options !== void 0) {
3967
+ const validatedOptions = await validateTypes({
3968
+ value: options.options,
3969
+ schema: this.settings.callOptionsSchema,
3970
+ context: { field: "options" }
3971
+ });
3972
+ callOptions = {
3973
+ ...options,
3974
+ options: validatedOptions
3975
+ };
3976
+ }
3977
+ const {
3978
+ session: _session,
3979
+ abortSignal: _abortSignal,
3980
+ timeout: _timeout,
3981
+ onStart: _onStart,
3982
+ experimental_onStart: _experimentalOnStart,
3983
+ onStepStart: _onStepStart,
3984
+ experimental_onStepStart: _experimentalOnStepStart,
3985
+ onToolExecutionStart: _onToolExecutionStart,
3986
+ experimental_onToolCallStart: _experimentalOnToolCallStart,
3987
+ onToolExecutionEnd: _onToolExecutionEnd,
3988
+ experimental_onToolCallFinish: _experimentalOnToolCallFinish,
3989
+ onStepEnd: _onStepEnd,
3990
+ onStepFinish: _onStepFinish,
3991
+ onEnd: _onEnd,
3992
+ onFinish: _onFinish,
3993
+ experimental_sandbox: _experimentalSandbox,
3994
+ ...promptOptions
3995
+ } = callOptions;
3996
+ const baseCallArgs = {
3997
+ model: this.settings.model,
3998
+ skills: this.settings.skills,
3999
+ instructions: this.settings.instructions,
4000
+ tools: this.settings.tools,
4001
+ ...promptOptions
4002
+ };
4003
+ const preparedCallArgs = (_c = await ((_b4 = (_a4 = this.settings).prepareCall) == null ? void 0 : _b4.call(
4004
+ _a4,
4005
+ baseCallArgs
4006
+ ))) != null ? _c : baseCallArgs;
4007
+ if (this._resolveContinueTurnInput(preparedCallArgs) != null) {
4008
+ throw new Error(
4009
+ "HarnessAgent.prepareCall must return a fresh user prompt, not a tool continuation."
4010
+ );
4011
+ }
4012
+ return {
4013
+ prompt: this._resolvePromptTurnInput(preparedCallArgs),
4014
+ ...this._prepareTurnSettings({
4015
+ model: preparedCallArgs.model,
4016
+ skills: preparedCallArgs.skills,
4017
+ instructions: preparedCallArgs.instructions,
4018
+ tools: preparedCallArgs.tools
4019
+ })
4020
+ };
4021
+ }
4022
+ _prepareContinueTurnInput(options) {
4023
+ var _a4, _b4;
4024
+ return {
4025
+ ...this._prepareTurnSettings({
4026
+ model: this.settings.model,
4027
+ skills: this.settings.skills,
4028
+ instructions: this.settings.instructions,
4029
+ tools: this.settings.tools
4030
+ }),
4031
+ toolApprovalContinuations: (_a4 = options.toolApprovalContinuations) != null ? _a4 : [],
4032
+ toolResultContinuations: (_b4 = options.toolResultContinuations) != null ? _b4 : []
4033
+ };
4034
+ }
4035
+ _prepareTurnSettings(options) {
4036
+ var _a4, _b4;
4037
+ const userTools = (_a4 = options.tools) != null ? _a4 : {};
4038
+ const tools = {
4039
+ ...this.settings.harness.builtinTools,
4040
+ ...userTools
4041
+ };
4042
+ const toolFiltering = resolveHarnessAgentToolFiltering({
4043
+ harness: this.settings.harness,
4044
+ userTools,
4045
+ allTools: tools,
4046
+ activeTools: this.settings.activeTools,
4047
+ inactiveTools: this.settings.inactiveTools
4048
+ });
4049
+ return {
4050
+ model: options.model,
4051
+ skills: (_b4 = options.skills) != null ? _b4 : [],
4052
+ instructions: options.instructions,
4053
+ tools,
4054
+ activeTools: toolFiltering.activeUserTools,
4055
+ toolSpecs: this._toToolSpecs(toolFiltering.activeUserTools),
4056
+ builtinToolFiltering: toolFiltering.builtinToolFiltering
4057
+ };
4058
+ }
3762
4059
  /*
3763
4060
  * Wire-format projection of user-defined tools only. Harness builtins are
3764
4061
  * executed by the runtime and the bridge already knows about them — we
3765
4062
  * never re-declare them over the wire.
3766
4063
  */
3767
- _toToolSpecs() {
4064
+ _toToolSpecs(activeUserTools) {
3768
4065
  const specs = [];
3769
4066
  for (const [name4, tool] of Object.entries(
3770
- this.activeUserTools
4067
+ activeUserTools
3771
4068
  )) {
3772
4069
  const t = tool;
3773
4070
  let inputSchema;