donobu 5.25.5 → 5.26.0

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.
@@ -139,6 +139,7 @@ export declare class PageAiRunner {
139
139
  export declare class PageAi {
140
140
  private readonly cache;
141
141
  private readonly runner;
142
+ private readonly donobu;
142
143
  /**
143
144
  * @param donobu Donobu stack providing flow managers and shared services.
144
145
  * @param gptClient GPT client used for autonomous reasoning.
@@ -133,6 +133,7 @@ class PageAi {
133
133
  */
134
134
  constructor(donobu, gptClient, cache) {
135
135
  this.cache = cache;
136
+ this.donobu = donobu;
136
137
  this.runner = new PageAiRunner(donobu, gptClient);
137
138
  }
138
139
  /**
@@ -188,7 +189,7 @@ class PageAi {
188
189
  }), {
189
190
  areElementIdsVolatile: options?.volatileElementIds,
190
191
  disableSelectorFailover: options?.noSelectorFailover,
191
- });
192
+ }, this.donobu.toolRegistry);
192
193
  const cacheEntry = cacheEntryBuilder_1.PageAiCacheEntryBuilder.fromMetadata(descriptor.key.pageUrl, runResult.donobuFlow.metadata, preparedToolCalls);
193
194
  await this.cache.put(cacheEntry);
194
195
  }
@@ -22,7 +22,7 @@ export * from './lib/test/fixtures/gptClients';
22
22
  export * from './lib/test/testExtension';
23
23
  export * from './lib/test/utils/donobuTestStack';
24
24
  export { DonobuFlow } from './managers/DonobuFlow';
25
- export { distillAllowedEnvVariableNames, DonobuFlowsManager, } from './managers/DonobuFlowsManager';
25
+ export { distillAllowedEnvVariableNames, DonobuFlowsManager, prepareToolCallsForRerun, } from './managers/DonobuFlowsManager';
26
26
  export { type DonobuStack, setupDonobuStack } from './managers/DonobuStack';
27
27
  export { InteractionVisualizer } from './managers/InteractionVisualizer';
28
28
  export { type LoadedPlugins, type PluginDependencies, PluginLoader, } from './managers/PluginLoader';
package/dist/esm/main.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.createDefaultToolRegistry = exports.ToolManager = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.env = exports.runGenerateSiteTests = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
17
+ exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.createDefaultToolRegistry = exports.ToolManager = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.prepareToolCallsForRerun = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.env = exports.runGenerateSiteTests = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
18
18
  exports.startDonobuServer = startDonobuServer;
19
19
  const commander_1 = require("commander");
20
20
  const v4_1 = require("zod/v4");
@@ -61,6 +61,7 @@ Object.defineProperty(exports, "DonobuFlow", { enumerable: true, get: function (
61
61
  var DonobuFlowsManager_1 = require("./managers/DonobuFlowsManager");
62
62
  Object.defineProperty(exports, "distillAllowedEnvVariableNames", { enumerable: true, get: function () { return DonobuFlowsManager_1.distillAllowedEnvVariableNames; } });
63
63
  Object.defineProperty(exports, "DonobuFlowsManager", { enumerable: true, get: function () { return DonobuFlowsManager_1.DonobuFlowsManager; } });
64
+ Object.defineProperty(exports, "prepareToolCallsForRerun", { enumerable: true, get: function () { return DonobuFlowsManager_1.prepareToolCallsForRerun; } });
64
65
  var DonobuStack_1 = require("./managers/DonobuStack");
65
66
  Object.defineProperty(exports, "setupDonobuStack", { enumerable: true, get: function () { return DonobuStack_1.setupDonobuStack; } });
66
67
  var InteractionVisualizer_1 = require("./managers/InteractionVisualizer");
@@ -209,32 +209,20 @@ export declare function distillAllowedEnvVariableNames(overallObjective: string
209
209
  export declare function setupAllowedTools(flowParams: Pick<CreateDonobuFlow, 'customTools' | 'toolCallsOnStart' | 'allowedTools'>, hasGptClient: boolean, targetType: string, toolRegistry: ToolRegistry): Promise<Tool<z.ZodObject, z.ZodObject>[]>;
210
210
  /**
211
211
  * Convert an *executed* list of {@link ToolCall}s into a list of
212
- * {@link ProposedToolCall}s that can be replayed in a fresh browser
213
- * session.
212
+ * {@link ProposedToolCall}s that can be replayed in a fresh session.
214
213
  *
215
- * ### What happens?
216
- * 1. **Identify calls that need selector-remapping.**
217
- * Some tools (e.g. {@link ClickTool}, {@link InputTextTool}) rely on a
218
- * deterministic selector captured at runtime. These tool names live in
219
- * `toolsThatNeedRemappedInputs`.
220
- * 2. **Attempt remap with {@link ReplayableInteraction.prepareToolCallForRerun}.**
221
- * If a call belongs to that set, we delegate to
222
- * `ReplayableInteraction.remapForRerun` to inject the stored selector and
223
- * strip any transient GPT-only fields.
224
- * 3. **Pass through other calls unchanged.**
225
- * Tools that have no UI footprint (e.g. navigation helpers, assertions)
226
- * are copied verbatim.
227
- * 4. **Error handling.**
228
- * When remapping fails (e.g. selector metadata missing) we *skip* the
229
- * offending call, emit a warning via `appLogger`, and continue processing
230
- * the remainder so that a “best-effort” replay is still possible.
214
+ * For each call, we look up the {@link Tool} in the given registry and
215
+ * delegate to its `prepareForRerun` override. Tools that need selector
216
+ * remapping (e.g. {@link ClickTool}, {@link InputTextTool}, the mobile
217
+ * interaction tools) each own their own logic; tools with no replay-
218
+ * specific needs inherit the passthrough default from {@link Tool}.
231
219
  *
232
- * The resulting array preserves the original order **minus** any calls that
233
- * could not be remapped.
220
+ * When a tool is not registered (stale flow, removed tool) or
221
+ * `prepareForRerun` throws (e.g. missing selector metadata), the call
222
+ * is passed through or skipped with an `appLogger.warn` — "best-effort
223
+ * replay" behavior is preserved.
234
224
  *
235
- * @param toolCalls – The chronologically ordered list of executed
236
- * {@link ToolCall}s.
237
225
  * @returns A list of {@link ProposedToolCall}s ready for replay.
238
226
  */
239
- export declare function prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions): Promise<ProposedToolCall[]>;
227
+ export declare function prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions, toolRegistry: ToolRegistry): Promise<ProposedToolCall[]>;
240
228
  //# sourceMappingURL=DonobuFlowsManager.d.ts.map
@@ -51,18 +51,7 @@ const ToolRequiresGptException_1 = require("../exceptions/ToolRequiresGptExcepti
51
51
  const UnknownToolException_1 = require("../exceptions/UnknownToolException");
52
52
  const GptConfig_1 = require("../models/GptConfig");
53
53
  const resolveTargetRuntime_1 = require("../targets/resolveTargetRuntime");
54
- const ChooseSelectOptionTool_1 = require("../tools/ChooseSelectOptionTool");
55
- const ClickTool_1 = require("../tools/ClickTool");
56
54
  const CustomToolRunnerTool_1 = require("../tools/CustomToolRunnerTool");
57
- const DoubleClickTool_1 = require("../tools/DoubleClickTool");
58
- const HoverOverElementTool_1 = require("../tools/HoverOverElementTool");
59
- const InputFakerTool_1 = require("../tools/InputFakerTool");
60
- const InputRandomizedEmailAddressTool_1 = require("../tools/InputRandomizedEmailAddressTool");
61
- const InputTextTool_1 = require("../tools/InputTextTool");
62
- const PressKeyTool_1 = require("../tools/PressKeyTool");
63
- const RememberPageTextTool_1 = require("../tools/RememberPageTextTool");
64
- const ReplayableInteraction_1 = require("../tools/ReplayableInteraction");
65
- const ScrollPageTool_1 = require("../tools/ScrollPageTool");
66
55
  const FlowLogBuffer_1 = require("../utils/FlowLogBuffer");
67
56
  const Logger_1 = require("../utils/Logger");
68
57
  const MiscUtils_1 = require("../utils/MiscUtils");
@@ -291,11 +280,9 @@ class DonobuFlowsManager {
291
280
  async getToolCallsForRerun(priorFlowMetadata, options) {
292
281
  const originalToolCalls = await this.getToolCalls(priorFlowMetadata.id);
293
282
  // Delegate tool call preparation to the target runtime plugin so that
294
- // rerun logic is fully target-agnostic.
295
- const plugin = this.targetRuntimePlugins.get(priorFlowMetadata.target);
296
- return plugin
297
- ? await plugin.prepareToolCallsForRerun(originalToolCalls, options)
298
- : await prepareToolCallsForRerun(originalToolCalls, options);
283
+ // rerun logic is fully target-agnostic — it dispatches per tool via
284
+ // the tool's own `prepareForRerun` override.
285
+ return prepareToolCallsForRerun(originalToolCalls, options, this.toolRegistry);
299
286
  }
300
287
  /**
301
288
  * Loads the given flow by ID and returns a `CreateDonobuFlow` object that can be passed to `createFlow`
@@ -460,7 +447,7 @@ class DonobuFlowsManager {
460
447
  const flowMetadata = await this.getFlowById(flowId);
461
448
  // Do not generate code for non-successful tool calls.
462
449
  const originalToolCalls = (await this.getToolCalls(flowId)).filter((tc) => tc.outcome?.isSuccessful);
463
- const proposedToolCalls = await prepareToolCallsForRerun(originalToolCalls, effectiveOptions);
450
+ const proposedToolCalls = await prepareToolCallsForRerun(originalToolCalls, effectiveOptions, this.toolRegistry);
464
451
  const scriptVariant = effectiveOptions.playwrightScriptVariant === 'classic' ? 'classic' : 'ai';
465
452
  if (scriptVariant === 'classic') {
466
453
  return (0, CodeGenerator_1.getFlowAsPlaywrightScript)(flowMetadata, proposedToolCalls, effectiveOptions, this.toolRegistry);
@@ -478,9 +465,9 @@ class DonobuFlowsManager {
478
465
  const flowsWithToolCalls = await Promise.all(flowIds.map(async (flowId) => {
479
466
  const metadata = await this.getFlowById(flowId);
480
467
  const toolCalls = await this.getToolCalls(flowId);
481
- // Filter out unsuccessful tool calls and prepare them for replay
468
+ // Filter out unsuccessful tool calls and prepare them for replay.
482
469
  const successfulToolCalls = toolCalls.filter((tc) => tc.outcome?.isSuccessful);
483
- const proposedToolCalls = await prepareToolCallsForRerun(successfulToolCalls, options);
470
+ const proposedToolCalls = await prepareToolCallsForRerun(successfulToolCalls, options, this.toolRegistry);
484
471
  return {
485
472
  metadata,
486
473
  toolCalls: proposedToolCalls,
@@ -866,65 +853,40 @@ async function throwIfAnyToolsRequireGpt(requestedTools, toolCallsOnStart, toolR
866
853
  }
867
854
  /**
868
855
  * Convert an *executed* list of {@link ToolCall}s into a list of
869
- * {@link ProposedToolCall}s that can be replayed in a fresh browser
870
- * session.
856
+ * {@link ProposedToolCall}s that can be replayed in a fresh session.
871
857
  *
872
- * ### What happens?
873
- * 1. **Identify calls that need selector-remapping.**
874
- * Some tools (e.g. {@link ClickTool}, {@link InputTextTool}) rely on a
875
- * deterministic selector captured at runtime. These tool names live in
876
- * `toolsThatNeedRemappedInputs`.
877
- * 2. **Attempt remap with {@link ReplayableInteraction.prepareToolCallForRerun}.**
878
- * If a call belongs to that set, we delegate to
879
- * `ReplayableInteraction.remapForRerun` to inject the stored selector and
880
- * strip any transient GPT-only fields.
881
- * 3. **Pass through other calls unchanged.**
882
- * Tools that have no UI footprint (e.g. navigation helpers, assertions)
883
- * are copied verbatim.
884
- * 4. **Error handling.**
885
- * When remapping fails (e.g. selector metadata missing) we *skip* the
886
- * offending call, emit a warning via `appLogger`, and continue processing
887
- * the remainder so that a “best-effort” replay is still possible.
858
+ * For each call, we look up the {@link Tool} in the given registry and
859
+ * delegate to its `prepareForRerun` override. Tools that need selector
860
+ * remapping (e.g. {@link ClickTool}, {@link InputTextTool}, the mobile
861
+ * interaction tools) each own their own logic; tools with no replay-
862
+ * specific needs inherit the passthrough default from {@link Tool}.
888
863
  *
889
- * The resulting array preserves the original order **minus** any calls that
890
- * could not be remapped.
864
+ * When a tool is not registered (stale flow, removed tool) or
865
+ * `prepareForRerun` throws (e.g. missing selector metadata), the call
866
+ * is passed through or skipped with an `appLogger.warn` — "best-effort
867
+ * replay" behavior is preserved.
891
868
  *
892
- * @param toolCalls – The chronologically ordered list of executed
893
- * {@link ToolCall}s.
894
869
  * @returns A list of {@link ProposedToolCall}s ready for replay.
895
870
  */
896
- async function prepareToolCallsForRerun(toolCalls, options) {
897
- // Tools that need special handling for their inputs during replay.
898
- const toolsThatNeedRemappedInputs = new Set([
899
- ChooseSelectOptionTool_1.ChooseSelectOptionTool.NAME,
900
- ClickTool_1.ClickTool.NAME,
901
- DoubleClickTool_1.DoubleClickTool.NAME,
902
- HoverOverElementTool_1.HoverOverElementTool.NAME,
903
- InputFakerTool_1.InputFakerTool.NAME,
904
- InputRandomizedEmailAddressTool_1.InputRandomizedEmailAddressTool.NAME,
905
- InputTextTool_1.InputTextTool.NAME,
906
- PressKeyTool_1.PressKeyTool.NAME,
907
- RememberPageTextTool_1.RememberPageTextTool.NAME,
908
- ScrollPageTool_1.ScrollPageTool.NAME,
909
- ]);
871
+ async function prepareToolCallsForRerun(toolCalls, options, toolRegistry) {
872
+ const toolsByName = new Map(toolRegistry.allTools().map((tool) => [tool.name, tool]));
910
873
  const proposedToolCalls = [];
911
874
  for (const toolCall of toolCalls) {
912
- const needsRemappedInputs = toolsThatNeedRemappedInputs.has(toolCall.toolName);
913
- if (needsRemappedInputs) {
914
- try {
915
- proposedToolCalls.push(ReplayableInteraction_1.ReplayableInteraction.prepareToolCallForRerun(toolCall, options));
916
- }
917
- catch (e) {
918
- // Skip.
919
- Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
920
- }
921
- }
922
- else {
923
- // This is just a normal tool then.
875
+ const tool = toolsByName.get(toolCall.toolName);
876
+ if (!tool) {
877
+ // Tool no longer registered (e.g. stale flow referencing a removed
878
+ // tool). Passthrough so we don't silently drop the call.
924
879
  proposedToolCalls.push({
925
880
  name: toolCall.toolName,
926
881
  parameters: toolCall.parameters,
927
882
  });
883
+ continue;
884
+ }
885
+ try {
886
+ proposedToolCalls.push(tool.prepareForRerun(toolCall, options));
887
+ }
888
+ catch (e) {
889
+ Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
928
890
  }
929
891
  }
930
892
  return proposedToolCalls;
@@ -4,11 +4,8 @@ import type { env } from '../envVars';
4
4
  import type { InteractionVisualizer } from '../managers/InteractionVisualizer';
5
5
  import type { BrowserStateReference } from '../models/BrowserStateFlowReference';
6
6
  import type { BrowserStorageState } from '../models/BrowserStorageState';
7
- import type { CodeGenerationOptions } from '../models/CodeGenerationOptions';
8
7
  import type { ControlPanelFactory } from '../models/ControlPanel';
9
8
  import type { CreateDonobuFlow } from '../models/CreateDonobuFlow';
10
- import type { ProposedToolCall } from '../models/ProposedToolCall';
11
- import type { ToolCall } from '../models/ToolCall';
12
9
  import type { TargetRuntime } from './TargetRuntime';
13
10
  /**
14
11
  * Parameters passed to {@link TargetRuntimePlugin.createRuntime}.
@@ -80,13 +77,6 @@ export interface TargetRuntimePlugin {
80
77
  * the flow completes or errors.
81
78
  */
82
79
  createRuntime(params: TargetRuntimeParams): Promise<TargetRuntime>;
83
- /**
84
- * Prepare tool calls from a prior flow for deterministic replay.
85
- *
86
- * Each target provides its own replay logic. For example, the web target
87
- * uses {@link ReplayableInteraction.prepareToolCallForRerun}.
88
- */
89
- prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions): Promise<ProposedToolCall[]>;
90
80
  }
91
81
  /**
92
82
  * An immutable registry of target runtime plugins, keyed by target type.
@@ -40,18 +40,7 @@ const path = __importStar(require("path"));
40
40
  const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException");
41
41
  const WebTargetInspector_1 = require("../managers/WebTargetInspector");
42
42
  const ControlPanel_1 = require("../models/ControlPanel");
43
- const ChooseSelectOptionTool_1 = require("../tools/ChooseSelectOptionTool");
44
- const ClickTool_1 = require("../tools/ClickTool");
45
- const DoubleClickTool_1 = require("../tools/DoubleClickTool");
46
43
  const GoToWebpageTool_1 = require("../tools/GoToWebpageTool");
47
- const HoverOverElementTool_1 = require("../tools/HoverOverElementTool");
48
- const InputFakerTool_1 = require("../tools/InputFakerTool");
49
- const InputRandomizedEmailAddressTool_1 = require("../tools/InputRandomizedEmailAddressTool");
50
- const InputTextTool_1 = require("../tools/InputTextTool");
51
- const PressKeyTool_1 = require("../tools/PressKeyTool");
52
- const RememberPageTextTool_1 = require("../tools/RememberPageTextTool");
53
- const ReplayableInteraction_1 = require("../tools/ReplayableInteraction");
54
- const ScrollPageTool_1 = require("../tools/ScrollPageTool");
55
44
  const BrowserUtils_1 = require("../utils/BrowserUtils");
56
45
  const Logger_1 = require("../utils/Logger");
57
46
  const PlaywrightUtils_1 = require("../utils/PlaywrightUtils");
@@ -263,37 +252,5 @@ exports.webTargetRuntimePlugin = {
263
252
  async createRuntime(params) {
264
253
  return WebTargetRuntime.create(params);
265
254
  },
266
- async prepareToolCallsForRerun(toolCalls, options) {
267
- const toolsThatNeedRemappedInputs = new Set([
268
- ChooseSelectOptionTool_1.ChooseSelectOptionTool.NAME,
269
- ClickTool_1.ClickTool.NAME,
270
- DoubleClickTool_1.DoubleClickTool.NAME,
271
- HoverOverElementTool_1.HoverOverElementTool.NAME,
272
- InputFakerTool_1.InputFakerTool.NAME,
273
- InputRandomizedEmailAddressTool_1.InputRandomizedEmailAddressTool.NAME,
274
- InputTextTool_1.InputTextTool.NAME,
275
- PressKeyTool_1.PressKeyTool.NAME,
276
- RememberPageTextTool_1.RememberPageTextTool.NAME,
277
- ScrollPageTool_1.ScrollPageTool.NAME,
278
- ]);
279
- const proposedToolCalls = [];
280
- for (const toolCall of toolCalls) {
281
- if (toolsThatNeedRemappedInputs.has(toolCall.toolName)) {
282
- try {
283
- proposedToolCalls.push(ReplayableInteraction_1.ReplayableInteraction.prepareToolCallForRerun(toolCall, options));
284
- }
285
- catch (e) {
286
- Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
287
- }
288
- }
289
- else {
290
- proposedToolCalls.push({
291
- name: toolCall.toolName,
292
- parameters: toolCall.parameters,
293
- });
294
- }
295
- }
296
- return proposedToolCalls;
297
- },
298
255
  };
299
256
  //# sourceMappingURL=WebTargetRuntime.js.map
@@ -180,7 +180,7 @@ export declare abstract class ReplayableInteraction<CoreSchema extends z.ZodObje
180
180
  * @returns A {@link ProposedToolCall} that can be executed in a fresh run.
181
181
  * @throws Error if the original call lacks selector metadata.
182
182
  */
183
- static prepareToolCallForRerun(toolCall: ToolCall, options: CodeGenerationOptions): ProposedToolCall;
183
+ prepareForRerun(toolCall: ToolCall, options: CodeGenerationOptions): ProposedToolCall;
184
184
  }
185
185
  export {};
186
186
  //# sourceMappingURL=ReplayableInteraction.d.ts.map
@@ -590,7 +590,7 @@ class ReplayableInteraction extends Tool_1.Tool {
590
590
  * @returns A {@link ProposedToolCall} that can be executed in a fresh run.
591
591
  * @throws Error if the original call lacks selector metadata.
592
592
  */
593
- static prepareToolCallForRerun(toolCall, options) {
593
+ prepareForRerun(toolCall, options) {
594
594
  // If the tool call is from a GPT, we need the result metadata to continue.
595
595
  if (!('selector' in toolCall.parameters)) {
596
596
  if (!toolCall.outcome.metadata) {
@@ -1,4 +1,7 @@
1
1
  import type { z } from 'zod/v4';
2
+ import type { CodeGenerationOptions } from '../models/CodeGenerationOptions';
3
+ import type { ProposedToolCall } from '../models/ProposedToolCall';
4
+ import type { ToolCall } from '../models/ToolCall';
2
5
  import type { ToolCallContext } from '../models/ToolCallContext';
3
6
  import type { ToolCallResult } from '../models/ToolCallResult';
4
7
  /** Represents a tool call to be invoked by a DonobuFlow. */
@@ -33,5 +36,16 @@ export declare abstract class Tool<CallSchema extends z.ZodObject, CallFromGptSc
33
36
  * Invoke the tool as made from a GPT with the given context and parameters.
34
37
  */
35
38
  abstract callFromGpt(context: ToolCallContext, parameters: z.infer<CallFromGptSchema>): Promise<ToolCallResult>;
39
+ /**
40
+ * Transform a completed tool call into a {@link ProposedToolCall} suitable
41
+ * for deterministic replay / code generation.
42
+ *
43
+ * The default implementation is a passthrough — `{ name, parameters }` —
44
+ * which is correct for tools that have no replay-specific logic
45
+ * (waits, assertions, markers, etc.). Tools that need to hoist
46
+ * selector metadata out of their outcome, strip LLM-only fields, or
47
+ * otherwise rewrite themselves override this method.
48
+ */
49
+ prepareForRerun(toolCall: ToolCall, _options: CodeGenerationOptions): ProposedToolCall;
36
50
  }
37
51
  //# sourceMappingURL=Tool.d.ts.map
@@ -27,6 +27,22 @@ class Tool {
27
27
  this.controlPanelMessage = controlPanelMessage;
28
28
  this.supportedTargets = supportedTargets;
29
29
  }
30
+ /**
31
+ * Transform a completed tool call into a {@link ProposedToolCall} suitable
32
+ * for deterministic replay / code generation.
33
+ *
34
+ * The default implementation is a passthrough — `{ name, parameters }` —
35
+ * which is correct for tools that have no replay-specific logic
36
+ * (waits, assertions, markers, etc.). Tools that need to hoist
37
+ * selector metadata out of their outcome, strip LLM-only fields, or
38
+ * otherwise rewrite themselves override this method.
39
+ */
40
+ prepareForRerun(toolCall, _options) {
41
+ return {
42
+ name: toolCall.toolName,
43
+ parameters: toolCall.parameters,
44
+ };
45
+ }
30
46
  }
31
47
  exports.Tool = Tool;
32
48
  //# sourceMappingURL=Tool.js.map
@@ -139,6 +139,7 @@ export declare class PageAiRunner {
139
139
  export declare class PageAi {
140
140
  private readonly cache;
141
141
  private readonly runner;
142
+ private readonly donobu;
142
143
  /**
143
144
  * @param donobu Donobu stack providing flow managers and shared services.
144
145
  * @param gptClient GPT client used for autonomous reasoning.
@@ -133,6 +133,7 @@ class PageAi {
133
133
  */
134
134
  constructor(donobu, gptClient, cache) {
135
135
  this.cache = cache;
136
+ this.donobu = donobu;
136
137
  this.runner = new PageAiRunner(donobu, gptClient);
137
138
  }
138
139
  /**
@@ -188,7 +189,7 @@ class PageAi {
188
189
  }), {
189
190
  areElementIdsVolatile: options?.volatileElementIds,
190
191
  disableSelectorFailover: options?.noSelectorFailover,
191
- });
192
+ }, this.donobu.toolRegistry);
192
193
  const cacheEntry = cacheEntryBuilder_1.PageAiCacheEntryBuilder.fromMetadata(descriptor.key.pageUrl, runResult.donobuFlow.metadata, preparedToolCalls);
193
194
  await this.cache.put(cacheEntry);
194
195
  }
package/dist/main.d.ts CHANGED
@@ -22,7 +22,7 @@ export * from './lib/test/fixtures/gptClients';
22
22
  export * from './lib/test/testExtension';
23
23
  export * from './lib/test/utils/donobuTestStack';
24
24
  export { DonobuFlow } from './managers/DonobuFlow';
25
- export { distillAllowedEnvVariableNames, DonobuFlowsManager, } from './managers/DonobuFlowsManager';
25
+ export { distillAllowedEnvVariableNames, DonobuFlowsManager, prepareToolCallsForRerun, } from './managers/DonobuFlowsManager';
26
26
  export { type DonobuStack, setupDonobuStack } from './managers/DonobuStack';
27
27
  export { InteractionVisualizer } from './managers/InteractionVisualizer';
28
28
  export { type LoadedPlugins, type PluginDependencies, PluginLoader, } from './managers/PluginLoader';
package/dist/main.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.createDefaultToolRegistry = exports.ToolManager = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.env = exports.runGenerateSiteTests = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
17
+ exports.TargetRuntimePluginRegistry = exports.PersistencePluginRegistry = exports.createDefaultToolRegistry = exports.ToolManager = exports.PluginLoader = exports.InteractionVisualizer = exports.setupDonobuStack = exports.prepareToolCallsForRerun = exports.DonobuFlowsManager = exports.distillAllowedEnvVariableNames = exports.DonobuFlow = exports.env = exports.runGenerateSiteTests = exports.VercelAiGptClient = exports.OpenAiGptClient = exports.GptClientPluginRegistry = exports.GptClient = exports.GoogleGenerativeAiGptClient = exports.fixAssertFields = exports.DonobuGptClient = exports.AnthropicGptClient = void 0;
18
18
  exports.startDonobuServer = startDonobuServer;
19
19
  const commander_1 = require("commander");
20
20
  const v4_1 = require("zod/v4");
@@ -61,6 +61,7 @@ Object.defineProperty(exports, "DonobuFlow", { enumerable: true, get: function (
61
61
  var DonobuFlowsManager_1 = require("./managers/DonobuFlowsManager");
62
62
  Object.defineProperty(exports, "distillAllowedEnvVariableNames", { enumerable: true, get: function () { return DonobuFlowsManager_1.distillAllowedEnvVariableNames; } });
63
63
  Object.defineProperty(exports, "DonobuFlowsManager", { enumerable: true, get: function () { return DonobuFlowsManager_1.DonobuFlowsManager; } });
64
+ Object.defineProperty(exports, "prepareToolCallsForRerun", { enumerable: true, get: function () { return DonobuFlowsManager_1.prepareToolCallsForRerun; } });
64
65
  var DonobuStack_1 = require("./managers/DonobuStack");
65
66
  Object.defineProperty(exports, "setupDonobuStack", { enumerable: true, get: function () { return DonobuStack_1.setupDonobuStack; } });
66
67
  var InteractionVisualizer_1 = require("./managers/InteractionVisualizer");
@@ -209,32 +209,20 @@ export declare function distillAllowedEnvVariableNames(overallObjective: string
209
209
  export declare function setupAllowedTools(flowParams: Pick<CreateDonobuFlow, 'customTools' | 'toolCallsOnStart' | 'allowedTools'>, hasGptClient: boolean, targetType: string, toolRegistry: ToolRegistry): Promise<Tool<z.ZodObject, z.ZodObject>[]>;
210
210
  /**
211
211
  * Convert an *executed* list of {@link ToolCall}s into a list of
212
- * {@link ProposedToolCall}s that can be replayed in a fresh browser
213
- * session.
212
+ * {@link ProposedToolCall}s that can be replayed in a fresh session.
214
213
  *
215
- * ### What happens?
216
- * 1. **Identify calls that need selector-remapping.**
217
- * Some tools (e.g. {@link ClickTool}, {@link InputTextTool}) rely on a
218
- * deterministic selector captured at runtime. These tool names live in
219
- * `toolsThatNeedRemappedInputs`.
220
- * 2. **Attempt remap with {@link ReplayableInteraction.prepareToolCallForRerun}.**
221
- * If a call belongs to that set, we delegate to
222
- * `ReplayableInteraction.remapForRerun` to inject the stored selector and
223
- * strip any transient GPT-only fields.
224
- * 3. **Pass through other calls unchanged.**
225
- * Tools that have no UI footprint (e.g. navigation helpers, assertions)
226
- * are copied verbatim.
227
- * 4. **Error handling.**
228
- * When remapping fails (e.g. selector metadata missing) we *skip* the
229
- * offending call, emit a warning via `appLogger`, and continue processing
230
- * the remainder so that a “best-effort” replay is still possible.
214
+ * For each call, we look up the {@link Tool} in the given registry and
215
+ * delegate to its `prepareForRerun` override. Tools that need selector
216
+ * remapping (e.g. {@link ClickTool}, {@link InputTextTool}, the mobile
217
+ * interaction tools) each own their own logic; tools with no replay-
218
+ * specific needs inherit the passthrough default from {@link Tool}.
231
219
  *
232
- * The resulting array preserves the original order **minus** any calls that
233
- * could not be remapped.
220
+ * When a tool is not registered (stale flow, removed tool) or
221
+ * `prepareForRerun` throws (e.g. missing selector metadata), the call
222
+ * is passed through or skipped with an `appLogger.warn` — "best-effort
223
+ * replay" behavior is preserved.
234
224
  *
235
- * @param toolCalls – The chronologically ordered list of executed
236
- * {@link ToolCall}s.
237
225
  * @returns A list of {@link ProposedToolCall}s ready for replay.
238
226
  */
239
- export declare function prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions): Promise<ProposedToolCall[]>;
227
+ export declare function prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions, toolRegistry: ToolRegistry): Promise<ProposedToolCall[]>;
240
228
  //# sourceMappingURL=DonobuFlowsManager.d.ts.map
@@ -51,18 +51,7 @@ const ToolRequiresGptException_1 = require("../exceptions/ToolRequiresGptExcepti
51
51
  const UnknownToolException_1 = require("../exceptions/UnknownToolException");
52
52
  const GptConfig_1 = require("../models/GptConfig");
53
53
  const resolveTargetRuntime_1 = require("../targets/resolveTargetRuntime");
54
- const ChooseSelectOptionTool_1 = require("../tools/ChooseSelectOptionTool");
55
- const ClickTool_1 = require("../tools/ClickTool");
56
54
  const CustomToolRunnerTool_1 = require("../tools/CustomToolRunnerTool");
57
- const DoubleClickTool_1 = require("../tools/DoubleClickTool");
58
- const HoverOverElementTool_1 = require("../tools/HoverOverElementTool");
59
- const InputFakerTool_1 = require("../tools/InputFakerTool");
60
- const InputRandomizedEmailAddressTool_1 = require("../tools/InputRandomizedEmailAddressTool");
61
- const InputTextTool_1 = require("../tools/InputTextTool");
62
- const PressKeyTool_1 = require("../tools/PressKeyTool");
63
- const RememberPageTextTool_1 = require("../tools/RememberPageTextTool");
64
- const ReplayableInteraction_1 = require("../tools/ReplayableInteraction");
65
- const ScrollPageTool_1 = require("../tools/ScrollPageTool");
66
55
  const FlowLogBuffer_1 = require("../utils/FlowLogBuffer");
67
56
  const Logger_1 = require("../utils/Logger");
68
57
  const MiscUtils_1 = require("../utils/MiscUtils");
@@ -291,11 +280,9 @@ class DonobuFlowsManager {
291
280
  async getToolCallsForRerun(priorFlowMetadata, options) {
292
281
  const originalToolCalls = await this.getToolCalls(priorFlowMetadata.id);
293
282
  // Delegate tool call preparation to the target runtime plugin so that
294
- // rerun logic is fully target-agnostic.
295
- const plugin = this.targetRuntimePlugins.get(priorFlowMetadata.target);
296
- return plugin
297
- ? await plugin.prepareToolCallsForRerun(originalToolCalls, options)
298
- : await prepareToolCallsForRerun(originalToolCalls, options);
283
+ // rerun logic is fully target-agnostic — it dispatches per tool via
284
+ // the tool's own `prepareForRerun` override.
285
+ return prepareToolCallsForRerun(originalToolCalls, options, this.toolRegistry);
299
286
  }
300
287
  /**
301
288
  * Loads the given flow by ID and returns a `CreateDonobuFlow` object that can be passed to `createFlow`
@@ -460,7 +447,7 @@ class DonobuFlowsManager {
460
447
  const flowMetadata = await this.getFlowById(flowId);
461
448
  // Do not generate code for non-successful tool calls.
462
449
  const originalToolCalls = (await this.getToolCalls(flowId)).filter((tc) => tc.outcome?.isSuccessful);
463
- const proposedToolCalls = await prepareToolCallsForRerun(originalToolCalls, effectiveOptions);
450
+ const proposedToolCalls = await prepareToolCallsForRerun(originalToolCalls, effectiveOptions, this.toolRegistry);
464
451
  const scriptVariant = effectiveOptions.playwrightScriptVariant === 'classic' ? 'classic' : 'ai';
465
452
  if (scriptVariant === 'classic') {
466
453
  return (0, CodeGenerator_1.getFlowAsPlaywrightScript)(flowMetadata, proposedToolCalls, effectiveOptions, this.toolRegistry);
@@ -478,9 +465,9 @@ class DonobuFlowsManager {
478
465
  const flowsWithToolCalls = await Promise.all(flowIds.map(async (flowId) => {
479
466
  const metadata = await this.getFlowById(flowId);
480
467
  const toolCalls = await this.getToolCalls(flowId);
481
- // Filter out unsuccessful tool calls and prepare them for replay
468
+ // Filter out unsuccessful tool calls and prepare them for replay.
482
469
  const successfulToolCalls = toolCalls.filter((tc) => tc.outcome?.isSuccessful);
483
- const proposedToolCalls = await prepareToolCallsForRerun(successfulToolCalls, options);
470
+ const proposedToolCalls = await prepareToolCallsForRerun(successfulToolCalls, options, this.toolRegistry);
484
471
  return {
485
472
  metadata,
486
473
  toolCalls: proposedToolCalls,
@@ -866,65 +853,40 @@ async function throwIfAnyToolsRequireGpt(requestedTools, toolCallsOnStart, toolR
866
853
  }
867
854
  /**
868
855
  * Convert an *executed* list of {@link ToolCall}s into a list of
869
- * {@link ProposedToolCall}s that can be replayed in a fresh browser
870
- * session.
856
+ * {@link ProposedToolCall}s that can be replayed in a fresh session.
871
857
  *
872
- * ### What happens?
873
- * 1. **Identify calls that need selector-remapping.**
874
- * Some tools (e.g. {@link ClickTool}, {@link InputTextTool}) rely on a
875
- * deterministic selector captured at runtime. These tool names live in
876
- * `toolsThatNeedRemappedInputs`.
877
- * 2. **Attempt remap with {@link ReplayableInteraction.prepareToolCallForRerun}.**
878
- * If a call belongs to that set, we delegate to
879
- * `ReplayableInteraction.remapForRerun` to inject the stored selector and
880
- * strip any transient GPT-only fields.
881
- * 3. **Pass through other calls unchanged.**
882
- * Tools that have no UI footprint (e.g. navigation helpers, assertions)
883
- * are copied verbatim.
884
- * 4. **Error handling.**
885
- * When remapping fails (e.g. selector metadata missing) we *skip* the
886
- * offending call, emit a warning via `appLogger`, and continue processing
887
- * the remainder so that a “best-effort” replay is still possible.
858
+ * For each call, we look up the {@link Tool} in the given registry and
859
+ * delegate to its `prepareForRerun` override. Tools that need selector
860
+ * remapping (e.g. {@link ClickTool}, {@link InputTextTool}, the mobile
861
+ * interaction tools) each own their own logic; tools with no replay-
862
+ * specific needs inherit the passthrough default from {@link Tool}.
888
863
  *
889
- * The resulting array preserves the original order **minus** any calls that
890
- * could not be remapped.
864
+ * When a tool is not registered (stale flow, removed tool) or
865
+ * `prepareForRerun` throws (e.g. missing selector metadata), the call
866
+ * is passed through or skipped with an `appLogger.warn` — "best-effort
867
+ * replay" behavior is preserved.
891
868
  *
892
- * @param toolCalls – The chronologically ordered list of executed
893
- * {@link ToolCall}s.
894
869
  * @returns A list of {@link ProposedToolCall}s ready for replay.
895
870
  */
896
- async function prepareToolCallsForRerun(toolCalls, options) {
897
- // Tools that need special handling for their inputs during replay.
898
- const toolsThatNeedRemappedInputs = new Set([
899
- ChooseSelectOptionTool_1.ChooseSelectOptionTool.NAME,
900
- ClickTool_1.ClickTool.NAME,
901
- DoubleClickTool_1.DoubleClickTool.NAME,
902
- HoverOverElementTool_1.HoverOverElementTool.NAME,
903
- InputFakerTool_1.InputFakerTool.NAME,
904
- InputRandomizedEmailAddressTool_1.InputRandomizedEmailAddressTool.NAME,
905
- InputTextTool_1.InputTextTool.NAME,
906
- PressKeyTool_1.PressKeyTool.NAME,
907
- RememberPageTextTool_1.RememberPageTextTool.NAME,
908
- ScrollPageTool_1.ScrollPageTool.NAME,
909
- ]);
871
+ async function prepareToolCallsForRerun(toolCalls, options, toolRegistry) {
872
+ const toolsByName = new Map(toolRegistry.allTools().map((tool) => [tool.name, tool]));
910
873
  const proposedToolCalls = [];
911
874
  for (const toolCall of toolCalls) {
912
- const needsRemappedInputs = toolsThatNeedRemappedInputs.has(toolCall.toolName);
913
- if (needsRemappedInputs) {
914
- try {
915
- proposedToolCalls.push(ReplayableInteraction_1.ReplayableInteraction.prepareToolCallForRerun(toolCall, options));
916
- }
917
- catch (e) {
918
- // Skip.
919
- Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
920
- }
921
- }
922
- else {
923
- // This is just a normal tool then.
875
+ const tool = toolsByName.get(toolCall.toolName);
876
+ if (!tool) {
877
+ // Tool no longer registered (e.g. stale flow referencing a removed
878
+ // tool). Passthrough so we don't silently drop the call.
924
879
  proposedToolCalls.push({
925
880
  name: toolCall.toolName,
926
881
  parameters: toolCall.parameters,
927
882
  });
883
+ continue;
884
+ }
885
+ try {
886
+ proposedToolCalls.push(tool.prepareForRerun(toolCall, options));
887
+ }
888
+ catch (e) {
889
+ Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
928
890
  }
929
891
  }
930
892
  return proposedToolCalls;
@@ -4,11 +4,8 @@ import type { env } from '../envVars';
4
4
  import type { InteractionVisualizer } from '../managers/InteractionVisualizer';
5
5
  import type { BrowserStateReference } from '../models/BrowserStateFlowReference';
6
6
  import type { BrowserStorageState } from '../models/BrowserStorageState';
7
- import type { CodeGenerationOptions } from '../models/CodeGenerationOptions';
8
7
  import type { ControlPanelFactory } from '../models/ControlPanel';
9
8
  import type { CreateDonobuFlow } from '../models/CreateDonobuFlow';
10
- import type { ProposedToolCall } from '../models/ProposedToolCall';
11
- import type { ToolCall } from '../models/ToolCall';
12
9
  import type { TargetRuntime } from './TargetRuntime';
13
10
  /**
14
11
  * Parameters passed to {@link TargetRuntimePlugin.createRuntime}.
@@ -80,13 +77,6 @@ export interface TargetRuntimePlugin {
80
77
  * the flow completes or errors.
81
78
  */
82
79
  createRuntime(params: TargetRuntimeParams): Promise<TargetRuntime>;
83
- /**
84
- * Prepare tool calls from a prior flow for deterministic replay.
85
- *
86
- * Each target provides its own replay logic. For example, the web target
87
- * uses {@link ReplayableInteraction.prepareToolCallForRerun}.
88
- */
89
- prepareToolCallsForRerun(toolCalls: ToolCall[], options: CodeGenerationOptions): Promise<ProposedToolCall[]>;
90
80
  }
91
81
  /**
92
82
  * An immutable registry of target runtime plugins, keyed by target type.
@@ -40,18 +40,7 @@ const path = __importStar(require("path"));
40
40
  const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException");
41
41
  const WebTargetInspector_1 = require("../managers/WebTargetInspector");
42
42
  const ControlPanel_1 = require("../models/ControlPanel");
43
- const ChooseSelectOptionTool_1 = require("../tools/ChooseSelectOptionTool");
44
- const ClickTool_1 = require("../tools/ClickTool");
45
- const DoubleClickTool_1 = require("../tools/DoubleClickTool");
46
43
  const GoToWebpageTool_1 = require("../tools/GoToWebpageTool");
47
- const HoverOverElementTool_1 = require("../tools/HoverOverElementTool");
48
- const InputFakerTool_1 = require("../tools/InputFakerTool");
49
- const InputRandomizedEmailAddressTool_1 = require("../tools/InputRandomizedEmailAddressTool");
50
- const InputTextTool_1 = require("../tools/InputTextTool");
51
- const PressKeyTool_1 = require("../tools/PressKeyTool");
52
- const RememberPageTextTool_1 = require("../tools/RememberPageTextTool");
53
- const ReplayableInteraction_1 = require("../tools/ReplayableInteraction");
54
- const ScrollPageTool_1 = require("../tools/ScrollPageTool");
55
44
  const BrowserUtils_1 = require("../utils/BrowserUtils");
56
45
  const Logger_1 = require("../utils/Logger");
57
46
  const PlaywrightUtils_1 = require("../utils/PlaywrightUtils");
@@ -263,37 +252,5 @@ exports.webTargetRuntimePlugin = {
263
252
  async createRuntime(params) {
264
253
  return WebTargetRuntime.create(params);
265
254
  },
266
- async prepareToolCallsForRerun(toolCalls, options) {
267
- const toolsThatNeedRemappedInputs = new Set([
268
- ChooseSelectOptionTool_1.ChooseSelectOptionTool.NAME,
269
- ClickTool_1.ClickTool.NAME,
270
- DoubleClickTool_1.DoubleClickTool.NAME,
271
- HoverOverElementTool_1.HoverOverElementTool.NAME,
272
- InputFakerTool_1.InputFakerTool.NAME,
273
- InputRandomizedEmailAddressTool_1.InputRandomizedEmailAddressTool.NAME,
274
- InputTextTool_1.InputTextTool.NAME,
275
- PressKeyTool_1.PressKeyTool.NAME,
276
- RememberPageTextTool_1.RememberPageTextTool.NAME,
277
- ScrollPageTool_1.ScrollPageTool.NAME,
278
- ]);
279
- const proposedToolCalls = [];
280
- for (const toolCall of toolCalls) {
281
- if (toolsThatNeedRemappedInputs.has(toolCall.toolName)) {
282
- try {
283
- proposedToolCalls.push(ReplayableInteraction_1.ReplayableInteraction.prepareToolCallForRerun(toolCall, options));
284
- }
285
- catch (e) {
286
- Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
287
- }
288
- }
289
- else {
290
- proposedToolCalls.push({
291
- name: toolCall.toolName,
292
- parameters: toolCall.parameters,
293
- });
294
- }
295
- }
296
- return proposedToolCalls;
297
- },
298
255
  };
299
256
  //# sourceMappingURL=WebTargetRuntime.js.map
@@ -180,7 +180,7 @@ export declare abstract class ReplayableInteraction<CoreSchema extends z.ZodObje
180
180
  * @returns A {@link ProposedToolCall} that can be executed in a fresh run.
181
181
  * @throws Error if the original call lacks selector metadata.
182
182
  */
183
- static prepareToolCallForRerun(toolCall: ToolCall, options: CodeGenerationOptions): ProposedToolCall;
183
+ prepareForRerun(toolCall: ToolCall, options: CodeGenerationOptions): ProposedToolCall;
184
184
  }
185
185
  export {};
186
186
  //# sourceMappingURL=ReplayableInteraction.d.ts.map
@@ -590,7 +590,7 @@ class ReplayableInteraction extends Tool_1.Tool {
590
590
  * @returns A {@link ProposedToolCall} that can be executed in a fresh run.
591
591
  * @throws Error if the original call lacks selector metadata.
592
592
  */
593
- static prepareToolCallForRerun(toolCall, options) {
593
+ prepareForRerun(toolCall, options) {
594
594
  // If the tool call is from a GPT, we need the result metadata to continue.
595
595
  if (!('selector' in toolCall.parameters)) {
596
596
  if (!toolCall.outcome.metadata) {
@@ -1,4 +1,7 @@
1
1
  import type { z } from 'zod/v4';
2
+ import type { CodeGenerationOptions } from '../models/CodeGenerationOptions';
3
+ import type { ProposedToolCall } from '../models/ProposedToolCall';
4
+ import type { ToolCall } from '../models/ToolCall';
2
5
  import type { ToolCallContext } from '../models/ToolCallContext';
3
6
  import type { ToolCallResult } from '../models/ToolCallResult';
4
7
  /** Represents a tool call to be invoked by a DonobuFlow. */
@@ -33,5 +36,16 @@ export declare abstract class Tool<CallSchema extends z.ZodObject, CallFromGptSc
33
36
  * Invoke the tool as made from a GPT with the given context and parameters.
34
37
  */
35
38
  abstract callFromGpt(context: ToolCallContext, parameters: z.infer<CallFromGptSchema>): Promise<ToolCallResult>;
39
+ /**
40
+ * Transform a completed tool call into a {@link ProposedToolCall} suitable
41
+ * for deterministic replay / code generation.
42
+ *
43
+ * The default implementation is a passthrough — `{ name, parameters }` —
44
+ * which is correct for tools that have no replay-specific logic
45
+ * (waits, assertions, markers, etc.). Tools that need to hoist
46
+ * selector metadata out of their outcome, strip LLM-only fields, or
47
+ * otherwise rewrite themselves override this method.
48
+ */
49
+ prepareForRerun(toolCall: ToolCall, _options: CodeGenerationOptions): ProposedToolCall;
36
50
  }
37
51
  //# sourceMappingURL=Tool.d.ts.map
@@ -27,6 +27,22 @@ class Tool {
27
27
  this.controlPanelMessage = controlPanelMessage;
28
28
  this.supportedTargets = supportedTargets;
29
29
  }
30
+ /**
31
+ * Transform a completed tool call into a {@link ProposedToolCall} suitable
32
+ * for deterministic replay / code generation.
33
+ *
34
+ * The default implementation is a passthrough — `{ name, parameters }` —
35
+ * which is correct for tools that have no replay-specific logic
36
+ * (waits, assertions, markers, etc.). Tools that need to hoist
37
+ * selector metadata out of their outcome, strip LLM-only fields, or
38
+ * otherwise rewrite themselves override this method.
39
+ */
40
+ prepareForRerun(toolCall, _options) {
41
+ return {
42
+ name: toolCall.toolName,
43
+ parameters: toolCall.parameters,
44
+ };
45
+ }
30
46
  }
31
47
  exports.Tool = Tool;
32
48
  //# sourceMappingURL=Tool.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.25.5",
3
+ "version": "5.26.0",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",