donobu 5.54.0 → 5.56.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.
- package/dist/apis/FlowsApi.d.ts +95 -7
- package/dist/apis/FlowsApi.js +139 -11
- package/dist/apis/TestsApi.js +4 -3
- package/dist/codegen/CodeGenerator.js +4 -2
- package/dist/esm/apis/FlowsApi.d.ts +95 -7
- package/dist/esm/apis/FlowsApi.js +139 -11
- package/dist/esm/apis/TestsApi.js +4 -3
- package/dist/esm/codegen/CodeGenerator.js +4 -2
- package/dist/esm/managers/AdminApiController.js +4 -0
- package/dist/esm/managers/DonobuFlow.d.ts +111 -1
- package/dist/esm/managers/DonobuFlow.js +443 -24
- package/dist/esm/managers/DonobuFlowsManager.d.ts +14 -1
- package/dist/esm/managers/DonobuFlowsManager.js +28 -6
- package/dist/esm/models/ControlPanel.d.ts +30 -3
- package/dist/esm/models/CreateDonobuFlow.d.ts +1 -0
- package/dist/esm/models/CreateTest.d.ts +1 -0
- package/dist/esm/models/FlowMetadata.d.ts +6 -0
- package/dist/esm/models/FlowMetadata.js +3 -1
- package/dist/esm/models/RunMode.d.ts +1 -0
- package/dist/esm/models/RunMode.js +7 -1
- package/dist/esm/models/TestMetadata.d.ts +9 -0
- package/dist/esm/persistence/DonobuSqliteDb.js +3 -2
- package/dist/esm/tools/AcknowledgeUserInstruction.d.ts +6 -0
- package/dist/esm/tools/AcknowledgeUserInstruction.js +7 -0
- package/dist/esm/tools/ReplayableInteraction.d.ts +20 -0
- package/dist/esm/tools/ReplayableInteraction.js +63 -0
- package/dist/esm/tools/SetRunModeTool.d.ts +2 -0
- package/dist/esm/tools/Tool.d.ts +22 -3
- package/dist/esm/tools/Tool.js +21 -2
- package/dist/esm/tools/TriggerDonobuFlowTool.d.ts +2 -0
- package/dist/managers/AdminApiController.js +4 -0
- package/dist/managers/DonobuFlow.d.ts +111 -1
- package/dist/managers/DonobuFlow.js +443 -24
- package/dist/managers/DonobuFlowsManager.d.ts +14 -1
- package/dist/managers/DonobuFlowsManager.js +28 -6
- package/dist/models/ControlPanel.d.ts +30 -3
- package/dist/models/CreateDonobuFlow.d.ts +1 -0
- package/dist/models/CreateTest.d.ts +1 -0
- package/dist/models/FlowMetadata.d.ts +6 -0
- package/dist/models/FlowMetadata.js +3 -1
- package/dist/models/RunMode.d.ts +1 -0
- package/dist/models/RunMode.js +7 -1
- package/dist/models/TestMetadata.d.ts +9 -0
- package/dist/persistence/DonobuSqliteDb.js +3 -2
- package/dist/tools/AcknowledgeUserInstruction.d.ts +6 -0
- package/dist/tools/AcknowledgeUserInstruction.js +7 -0
- package/dist/tools/ReplayableInteraction.d.ts +20 -0
- package/dist/tools/ReplayableInteraction.js +63 -0
- package/dist/tools/SetRunModeTool.d.ts +2 -0
- package/dist/tools/Tool.d.ts +22 -3
- package/dist/tools/Tool.js +21 -2
- package/dist/tools/TriggerDonobuFlowTool.d.ts +2 -0
- package/package.json +1 -1
|
@@ -151,7 +151,8 @@ class DonobuFlowsManager {
|
|
|
151
151
|
? flowParams.toolCallsOnStart
|
|
152
152
|
: targetRuntime.getInitialToolCalls(flowParams);
|
|
153
153
|
let maxToolCalls = null;
|
|
154
|
-
if (initialRunMode === 'AUTONOMOUS'
|
|
154
|
+
if (initialRunMode === 'AUTONOMOUS' ||
|
|
155
|
+
initialRunMode === 'SUPERVISED') {
|
|
155
156
|
maxToolCalls =
|
|
156
157
|
flowParams.maxToolCalls ??
|
|
157
158
|
DonobuFlowsManager.DEFAULT_MAX_TOOL_CALLS;
|
|
@@ -352,7 +353,15 @@ class DonobuFlowsManager {
|
|
|
352
353
|
videoDisabled: config.videoDisabled,
|
|
353
354
|
};
|
|
354
355
|
}
|
|
355
|
-
/**
|
|
356
|
+
/**
|
|
357
|
+
* Add a proposed tool call to the tool call queue for the given flow by ID.
|
|
358
|
+
*
|
|
359
|
+
* This intentionally does NOT go through {@link DonobuFlow.submitUserAction}:
|
|
360
|
+
* it *appends* a step to `proposedToolCalls` and validates the tool name
|
|
361
|
+
* synchronously (throwing {@link UnknownToolException}), whereas the
|
|
362
|
+
* `RUN_TOOL` user action clears the queue, runs immediately, and validates
|
|
363
|
+
* only at run time. This is queue input, not a cooperative control interrupt.
|
|
364
|
+
*/
|
|
356
365
|
async proposeToolCall(flowId, toolName, parameters) {
|
|
357
366
|
const activeFlowHandle = this.isLocallyRunning()
|
|
358
367
|
? this.flowRuntime.get(flowId)
|
|
@@ -433,6 +442,11 @@ class DonobuFlowsManager {
|
|
|
433
442
|
* Attempts to cancel a flow by ID. If the flow is active, the flow is ended
|
|
434
443
|
* with a state of `FAILED`. If the flow is not active, this method has no
|
|
435
444
|
* effect.
|
|
445
|
+
*
|
|
446
|
+
* This intentionally does NOT go through {@link DonobuFlow.submitUserAction}:
|
|
447
|
+
* cancellation is a forceful lifecycle/teardown operation owned by the
|
|
448
|
+
* manager — it sets the terminal state and tears down the browser context —
|
|
449
|
+
* not a cooperative control interrupt handled by the run loop.
|
|
436
450
|
*/
|
|
437
451
|
async cancelFlow(flowId) {
|
|
438
452
|
const activeFlowHandle = this.isLocallyRunning()
|
|
@@ -813,12 +827,15 @@ async function validateFlowParams(flowParams, gptClient, initialRunMode, toolReg
|
|
|
813
827
|
validateFlowName(flowParams.name);
|
|
814
828
|
switch (initialRunMode) {
|
|
815
829
|
case 'AUTONOMOUS':
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
830
|
+
case 'SUPERVISED':
|
|
831
|
+
// Both modes are AI-driven, so both need a GPT client and an objective
|
|
832
|
+
// to pursue.
|
|
819
833
|
if (!gptClient) {
|
|
820
834
|
throw new InvalidParamValueException_1.InvalidParamValueException('initialRunMode', initialRunMode, `no GPT client is available`);
|
|
821
835
|
}
|
|
836
|
+
if ((flowParams.overallObjective?.trim().length ?? 0) === 0) {
|
|
837
|
+
throw new InvalidParamValueException_1.InvalidParamValueException('overallObjective', flowParams.overallObjective, 'an AI-driven flow needs an objective to pursue');
|
|
838
|
+
}
|
|
822
839
|
break;
|
|
823
840
|
case 'INSTRUCT':
|
|
824
841
|
break;
|
|
@@ -966,7 +983,12 @@ async function prepareToolCallsForRerun(toolCalls, options, toolRegistry) {
|
|
|
966
983
|
continue;
|
|
967
984
|
}
|
|
968
985
|
try {
|
|
969
|
-
|
|
986
|
+
// A tool returns null to exclude itself from replay (e.g. a recorded
|
|
987
|
+
// user instruction, which is a live artifact, not a replayable action).
|
|
988
|
+
const prepared = tool.prepareForRerun(toolCall, options);
|
|
989
|
+
if (prepared) {
|
|
990
|
+
proposedToolCalls.push(prepared);
|
|
991
|
+
}
|
|
970
992
|
}
|
|
971
993
|
catch (e) {
|
|
972
994
|
Logger_1.appLogger.warn(`Failed to prepare tool call for rerun: ${JSON.stringify(toolCall)}`, e);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { State } from '../models/FlowMetadata';
|
|
2
|
+
import type { ProposedToolCall } from '../models/ProposedToolCall';
|
|
3
|
+
import type { RunMode } from '../models/RunMode';
|
|
2
4
|
export type UserAction = {
|
|
3
5
|
type: 'PAUSE';
|
|
4
6
|
} | {
|
|
@@ -10,13 +12,38 @@ export type UserAction = {
|
|
|
10
12
|
type: 'RUN_TOOL';
|
|
11
13
|
toolName: string;
|
|
12
14
|
parameters: Record<string, unknown>;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'APPROVE';
|
|
17
|
+
} | {
|
|
18
|
+
type: 'REJECT';
|
|
19
|
+
feedback?: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'SET_RUN_MODE';
|
|
22
|
+
runMode: RunMode;
|
|
23
|
+
approvePending?: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'STEP';
|
|
26
|
+
instruction?: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: 'RUN';
|
|
29
|
+
instruction?: string;
|
|
13
30
|
};
|
|
14
31
|
export type ControlPanelDataUpdate = {
|
|
15
32
|
state: State;
|
|
33
|
+
runMode?: RunMode;
|
|
34
|
+
/** The flow's overall objective; the panel treats a non-empty value (or a
|
|
35
|
+
* typed instruction) as a "goal", which gates ⏩ Fast-forward and ▶ Play. */
|
|
36
|
+
overallObjective?: string | null;
|
|
37
|
+
/** The tools the flow can actually run (resolved from the ToolManager). */
|
|
38
|
+
allowedTools?: string[] | null;
|
|
16
39
|
headline?: string;
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
/** AI-proposed tool call(s) awaiting approval (SUPERVISED). */
|
|
41
|
+
pendingToolCalls?: ProposedToolCall[];
|
|
42
|
+
/** Whether the flow has a GPT client (AI available at all). Not cleanly a
|
|
43
|
+
* FlowMetadata field — a client can come from env/default config, not just a
|
|
44
|
+
* named gptConfig — so it's surfaced explicitly. Drives whether the compose
|
|
45
|
+
* surface and the ▶/⏩ transport are offered at all. */
|
|
46
|
+
hasGptClient?: boolean;
|
|
20
47
|
};
|
|
21
48
|
export interface ControlPanel {
|
|
22
49
|
/** Cheap, idempotent render update. */
|
|
@@ -141,6 +141,7 @@ export declare const CreateDonobuFlowSchema: z.ZodObject<{
|
|
|
141
141
|
gptConfigNameOverride: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
142
142
|
initialRunMode: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
143
143
|
AUTONOMOUS: "AUTONOMOUS";
|
|
144
|
+
SUPERVISED: "SUPERVISED";
|
|
144
145
|
INSTRUCT: "INSTRUCT";
|
|
145
146
|
DETERMINISTIC: "DETERMINISTIC";
|
|
146
147
|
}>>>;
|
|
@@ -143,6 +143,7 @@ export declare const CreateTestSchema: z.ZodObject<{
|
|
|
143
143
|
suiteId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
144
144
|
nextRunMode: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
145
145
|
AUTONOMOUS: "AUTONOMOUS";
|
|
146
|
+
SUPERVISED: "SUPERVISED";
|
|
146
147
|
INSTRUCT: "INSTRUCT";
|
|
147
148
|
DETERMINISTIC: "DETERMINISTIC";
|
|
148
149
|
}>>>;
|
|
@@ -4,6 +4,7 @@ export declare const StateSchema: z.ZodEnum<{
|
|
|
4
4
|
INITIALIZING: "INITIALIZING";
|
|
5
5
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
6
6
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
7
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
7
8
|
PAUSED: "PAUSED";
|
|
8
9
|
RESUMING: "RESUMING";
|
|
9
10
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -151,6 +152,7 @@ export declare const FlowMetadataSchema: z.ZodObject<{
|
|
|
151
152
|
defaultMessageDuration: z.ZodNullable<z.ZodNumber>;
|
|
152
153
|
runMode: z.ZodEnum<{
|
|
153
154
|
AUTONOMOUS: "AUTONOMOUS";
|
|
155
|
+
SUPERVISED: "SUPERVISED";
|
|
154
156
|
INSTRUCT: "INSTRUCT";
|
|
155
157
|
DETERMINISTIC: "DETERMINISTIC";
|
|
156
158
|
}>;
|
|
@@ -165,6 +167,7 @@ export declare const FlowMetadataSchema: z.ZodObject<{
|
|
|
165
167
|
INITIALIZING: "INITIALIZING";
|
|
166
168
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
167
169
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
170
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
168
171
|
PAUSED: "PAUSED";
|
|
169
172
|
RESUMING: "RESUMING";
|
|
170
173
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -176,6 +179,7 @@ export declare const FlowMetadataSchema: z.ZodObject<{
|
|
|
176
179
|
INITIALIZING: "INITIALIZING";
|
|
177
180
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
178
181
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
182
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
179
183
|
PAUSED: "PAUSED";
|
|
180
184
|
RESUMING: "RESUMING";
|
|
181
185
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -212,6 +216,7 @@ export declare const FlowsQuerySchema: z.ZodObject<{
|
|
|
212
216
|
startedBefore: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
213
217
|
runMode: z.ZodOptional<z.ZodEnum<{
|
|
214
218
|
AUTONOMOUS: "AUTONOMOUS";
|
|
219
|
+
SUPERVISED: "SUPERVISED";
|
|
215
220
|
INSTRUCT: "INSTRUCT";
|
|
216
221
|
DETERMINISTIC: "DETERMINISTIC";
|
|
217
222
|
}>>;
|
|
@@ -220,6 +225,7 @@ export declare const FlowsQuerySchema: z.ZodObject<{
|
|
|
220
225
|
INITIALIZING: "INITIALIZING";
|
|
221
226
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
222
227
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
228
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
223
229
|
PAUSED: "PAUSED";
|
|
224
230
|
RESUMING: "RESUMING";
|
|
225
231
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -12,6 +12,7 @@ exports.StateSchema = v4_1.z
|
|
|
12
12
|
'INITIALIZING',
|
|
13
13
|
'QUERYING_LLM_FOR_NEXT_ACTION',
|
|
14
14
|
'WAITING_ON_USER_FOR_NEXT_ACTION',
|
|
15
|
+
'WAITING_FOR_APPROVAL',
|
|
15
16
|
'PAUSED',
|
|
16
17
|
'RESUMING',
|
|
17
18
|
'RUNNING_ACTION',
|
|
@@ -22,8 +23,9 @@ exports.StateSchema = v4_1.z
|
|
|
22
23
|
- UNSTARTED: Flow created but not yet initialized.
|
|
23
24
|
- INITIALIZING: Setting up browser context and initial state.
|
|
24
25
|
- RUNNING_ACTION: Executing a tool call.
|
|
25
|
-
- QUERYING_LLM_FOR_NEXT_ACTION: AI determining next action (AUTONOMOUS mode).
|
|
26
|
+
- QUERYING_LLM_FOR_NEXT_ACTION: AI determining next action (AUTONOMOUS/SUPERVISED mode).
|
|
26
27
|
- WAITING_ON_USER_FOR_NEXT_ACTION: Waiting for user input (INSTRUCT mode).
|
|
28
|
+
- WAITING_FOR_APPROVAL: An AI-proposed action is waiting for the user to approve or reject it (SUPERVISED mode).
|
|
27
29
|
- PAUSED: Flow execution temporarily suspended.
|
|
28
30
|
- RESUMING: Transitioning from paused to active state.
|
|
29
31
|
- FAILED: Flow terminated unsuccessfully.
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RunModeSchema = void 0;
|
|
4
4
|
const v4_1 = require("zod/v4");
|
|
5
5
|
exports.RunModeSchema = v4_1.z
|
|
6
|
-
.enum(['AUTONOMOUS', 'INSTRUCT', 'DETERMINISTIC'])
|
|
6
|
+
.enum(['AUTONOMOUS', 'SUPERVISED', 'INSTRUCT', 'DETERMINISTIC'])
|
|
7
7
|
.describe(`The execution mode that determines how a flow operates and makes decisions:
|
|
8
8
|
|
|
9
9
|
- AUTONOMOUS: The flow is driven by an AI agent (GPT) that autonomously decides what actions to
|
|
@@ -11,6 +11,12 @@ exports.RunModeSchema = v4_1.z
|
|
|
11
11
|
determines the next appropriate action, and executes it without human intervention.
|
|
12
12
|
This mode requires a valid GPT client configuration.
|
|
13
13
|
|
|
14
|
+
- SUPERVISED: The flow is driven by an AI agent (GPT) that proposes the next action, but a human
|
|
15
|
+
supervises each proposal: every AI-proposed action waits for the user to approve it,
|
|
16
|
+
or reject it with optional feedback, before it executes. Rejecting discards the
|
|
17
|
+
proposal and lets the AI try again with the feedback in mind. Like AUTONOMOUS, this
|
|
18
|
+
mode pursues an overall objective and requires a valid GPT client configuration.
|
|
19
|
+
|
|
14
20
|
- INSTRUCT: The flow waits for explicit user instructions for each action. The user manually
|
|
15
21
|
directs what tools to call and when, making this mode suitable for guided walkthroughs
|
|
16
22
|
or when human oversight is required for each step. No GPT client is required.
|
|
@@ -141,6 +141,7 @@ export declare const TestMetadataSchema: z.ZodObject<{
|
|
|
141
141
|
suiteId: z.ZodNullable<z.ZodString>;
|
|
142
142
|
nextRunMode: z.ZodEnum<{
|
|
143
143
|
AUTONOMOUS: "AUTONOMOUS";
|
|
144
|
+
SUPERVISED: "SUPERVISED";
|
|
144
145
|
INSTRUCT: "INSTRUCT";
|
|
145
146
|
DETERMINISTIC: "DETERMINISTIC";
|
|
146
147
|
}>;
|
|
@@ -324,6 +325,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
324
325
|
suiteId: z.ZodNullable<z.ZodString>;
|
|
325
326
|
nextRunMode: z.ZodEnum<{
|
|
326
327
|
AUTONOMOUS: "AUTONOMOUS";
|
|
328
|
+
SUPERVISED: "SUPERVISED";
|
|
327
329
|
INSTRUCT: "INSTRUCT";
|
|
328
330
|
DETERMINISTIC: "DETERMINISTIC";
|
|
329
331
|
}>;
|
|
@@ -486,6 +488,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
486
488
|
defaultMessageDuration: z.ZodNullable<z.ZodNumber>;
|
|
487
489
|
runMode: z.ZodEnum<{
|
|
488
490
|
AUTONOMOUS: "AUTONOMOUS";
|
|
491
|
+
SUPERVISED: "SUPERVISED";
|
|
489
492
|
INSTRUCT: "INSTRUCT";
|
|
490
493
|
DETERMINISTIC: "DETERMINISTIC";
|
|
491
494
|
}>;
|
|
@@ -500,6 +503,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
500
503
|
INITIALIZING: "INITIALIZING";
|
|
501
504
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
502
505
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
506
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
503
507
|
PAUSED: "PAUSED";
|
|
504
508
|
RESUMING: "RESUMING";
|
|
505
509
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -511,6 +515,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
511
515
|
INITIALIZING: "INITIALIZING";
|
|
512
516
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
513
517
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
518
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
514
519
|
PAUSED: "PAUSED";
|
|
515
520
|
RESUMING: "RESUMING";
|
|
516
521
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -676,6 +681,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
676
681
|
suiteId: z.ZodNullable<z.ZodString>;
|
|
677
682
|
nextRunMode: z.ZodEnum<{
|
|
678
683
|
AUTONOMOUS: "AUTONOMOUS";
|
|
684
|
+
SUPERVISED: "SUPERVISED";
|
|
679
685
|
INSTRUCT: "INSTRUCT";
|
|
680
686
|
DETERMINISTIC: "DETERMINISTIC";
|
|
681
687
|
}>;
|
|
@@ -838,6 +844,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
838
844
|
defaultMessageDuration: z.ZodNullable<z.ZodNumber>;
|
|
839
845
|
runMode: z.ZodEnum<{
|
|
840
846
|
AUTONOMOUS: "AUTONOMOUS";
|
|
847
|
+
SUPERVISED: "SUPERVISED";
|
|
841
848
|
INSTRUCT: "INSTRUCT";
|
|
842
849
|
DETERMINISTIC: "DETERMINISTIC";
|
|
843
850
|
}>;
|
|
@@ -852,6 +859,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
852
859
|
INITIALIZING: "INITIALIZING";
|
|
853
860
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
854
861
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
862
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
855
863
|
PAUSED: "PAUSED";
|
|
856
864
|
RESUMING: "RESUMING";
|
|
857
865
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -863,6 +871,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
863
871
|
INITIALIZING: "INITIALIZING";
|
|
864
872
|
QUERYING_LLM_FOR_NEXT_ACTION: "QUERYING_LLM_FOR_NEXT_ACTION";
|
|
865
873
|
WAITING_ON_USER_FOR_NEXT_ACTION: "WAITING_ON_USER_FOR_NEXT_ACTION";
|
|
874
|
+
WAITING_FOR_APPROVAL: "WAITING_FOR_APPROVAL";
|
|
866
875
|
PAUSED: "PAUSED";
|
|
867
876
|
RESUMING: "RESUMING";
|
|
868
877
|
RUNNING_ACTION: "RUNNING_ACTION";
|
|
@@ -471,7 +471,8 @@ CREATE INDEX IF NOT EXISTS idx_ai_queries_flow_id_started_at ON ai_queries(flow_
|
|
|
471
471
|
// value (likely null) if no autonomous flow exists in the group.
|
|
472
472
|
const newestAutonomous = [...group.flows]
|
|
473
473
|
.reverse()
|
|
474
|
-
.find((f) => f.metadata.runMode === 'AUTONOMOUS'
|
|
474
|
+
.find((f) => f.metadata.runMode === 'AUTONOMOUS' ||
|
|
475
|
+
f.metadata.runMode === 'SUPERVISED')?.metadata;
|
|
475
476
|
const maxToolCalls = newestAutonomous?.maxToolCalls ?? newest.maxToolCalls;
|
|
476
477
|
const testName = (0, displayName_1.getDisplayName)(newest, 'Untitled Test');
|
|
477
478
|
const testMetadata = {
|
|
@@ -649,7 +650,7 @@ CREATE INDEX IF NOT EXISTS idx_ai_queries_flow_id_started_at ON ai_queries(flow_
|
|
|
649
650
|
// value (likely null) if no autonomous flow exists in the group.
|
|
650
651
|
const newestAutonomous = [...flows]
|
|
651
652
|
.reverse()
|
|
652
|
-
.find((f) => f.runMode === 'AUTONOMOUS');
|
|
653
|
+
.find((f) => f.runMode === 'AUTONOMOUS' || f.runMode === 'SUPERVISED');
|
|
653
654
|
const maxToolCalls = newestAutonomous?.maxToolCalls ?? newestFlow.maxToolCalls;
|
|
654
655
|
const testName = (0, displayName_1.getDisplayName)(newestFlow, 'Untitled Test');
|
|
655
656
|
const testMetadata = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
|
+
import type { ProposedToolCall } from '../models/ProposedToolCall';
|
|
2
3
|
import type { ToolCallContext } from '../models/ToolCallContext';
|
|
3
4
|
import type { ToolCallResult } from '../models/ToolCallResult';
|
|
4
5
|
import { Tool } from './Tool';
|
|
@@ -20,5 +21,10 @@ export declare class AcknowledgeUserInstructionTool extends Tool<typeof Acknowle
|
|
|
20
21
|
constructor();
|
|
21
22
|
call(_context: ToolCallContext, parameters: z.infer<typeof AcknowledgeUserInstructionCoreSchema>): Promise<ToolCallResult>;
|
|
22
23
|
callFromGpt(context: ToolCallContext, parameters: z.infer<typeof AcknowledgeUserInstructionGptSchema>): Promise<ToolCallResult>;
|
|
24
|
+
/**
|
|
25
|
+
* A user instruction is a live-interaction artifact, not a replayable action,
|
|
26
|
+
* so it is excluded from reruns and generated scripts.
|
|
27
|
+
*/
|
|
28
|
+
prepareForRerun(): ProposedToolCall | null;
|
|
23
29
|
}
|
|
24
30
|
//# sourceMappingURL=AcknowledgeUserInstruction.d.ts.map
|
|
@@ -33,6 +33,13 @@ class AcknowledgeUserInstructionTool extends Tool_1.Tool {
|
|
|
33
33
|
async callFromGpt(context, parameters) {
|
|
34
34
|
return this.call(context, parameters);
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A user instruction is a live-interaction artifact, not a replayable action,
|
|
38
|
+
* so it is excluded from reruns and generated scripts.
|
|
39
|
+
*/
|
|
40
|
+
prepareForRerun() {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
36
43
|
}
|
|
37
44
|
exports.AcknowledgeUserInstructionTool = AcknowledgeUserInstructionTool;
|
|
38
45
|
AcknowledgeUserInstructionTool.NAME = 'acknowledgeUserInstruction';
|
|
@@ -84,8 +84,28 @@ export declare abstract class ReplayableInteraction<CoreSchema extends z.ZodObje
|
|
|
84
84
|
readonly coreSchema: CoreSchema;
|
|
85
85
|
static readonly MAX_SELECTOR_FAILOVERS = 3;
|
|
86
86
|
static readonly MAX_LOCATOR_MATCH_COUNT = 3;
|
|
87
|
+
/**
|
|
88
|
+
* How long the preview cursor takes to glide to (and dwell on) the proposed
|
|
89
|
+
* target, in milliseconds. {@link InteractionVisualizer.pointAt} animates the
|
|
90
|
+
* move over half this duration.
|
|
91
|
+
*/
|
|
92
|
+
static readonly PREVIEW_CURSOR_DURATION_MILLIS = 900;
|
|
87
93
|
constructor(name: string, description: string, coreSchema: CoreSchema, inputSchema: NonGptSchema, inputSchemaForGpt: GptSchema, requiresGpt?: boolean);
|
|
88
94
|
call(context: ToolCallContext, parameters: z.infer<NonGptSchema>): Promise<ToolCallResult>;
|
|
95
|
+
/**
|
|
96
|
+
* SUPERVISED-mode preview: resolve the element this interaction *would*
|
|
97
|
+
* target (from either an annotation- or selector-based proposal) and move the
|
|
98
|
+
* on-screen cursor to it, without performing the interaction. Best-effort —
|
|
99
|
+
* an unresolvable element simply leaves the cursor where it is.
|
|
100
|
+
*/
|
|
101
|
+
previewInteraction(context: ToolCallContext, parameters: Record<string, unknown>): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Resolve a proposed interaction's parameters to the {@link Locator} it would
|
|
104
|
+
* act on, mirroring the resolution in {@link call} (selector-based) and
|
|
105
|
+
* {@link callFromGpt} (annotation-based) but returning only the locator and
|
|
106
|
+
* never invoking anything. Returns `null` if the element can't be found.
|
|
107
|
+
*/
|
|
108
|
+
private resolvePreviewLocator;
|
|
89
109
|
callFromGpt(context: ToolCallContext, parameters: z.infer<GptSchema>): Promise<ToolCallResult>;
|
|
90
110
|
/**
|
|
91
111
|
* **Concrete subclasses implement the actual user action here.**
|
|
@@ -96,6 +96,63 @@ class ReplayableInteraction extends Tool_1.Tool {
|
|
|
96
96
|
}
|
|
97
97
|
return this.callCore(context, parameters, locators, parameters.selector);
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* SUPERVISED-mode preview: resolve the element this interaction *would*
|
|
101
|
+
* target (from either an annotation- or selector-based proposal) and move the
|
|
102
|
+
* on-screen cursor to it, without performing the interaction. Best-effort —
|
|
103
|
+
* an unresolvable element simply leaves the cursor where it is.
|
|
104
|
+
*/
|
|
105
|
+
async previewInteraction(context, parameters) {
|
|
106
|
+
const page = (0, TargetUtils_1.webPage)(context);
|
|
107
|
+
const locator = await this.resolvePreviewLocator(context, page, parameters);
|
|
108
|
+
if (!locator) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Point at the same visible target (or its label) the real interaction
|
|
112
|
+
// would, so the preview matches what approval will touch.
|
|
113
|
+
const pointTarget = await ReplayableInteraction.getLocatorOrItsLabel(locator.first());
|
|
114
|
+
// Only reveal the cursor now that we have a real target to point at, so a
|
|
115
|
+
// non-interactive proposal never pops a stationary cursor.
|
|
116
|
+
await context.targetInspector.showInteractionCursor();
|
|
117
|
+
await context.interactionVisualizer.pointAt(page, pointTarget.first(), undefined, ReplayableInteraction.PREVIEW_CURSOR_DURATION_MILLIS);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a proposed interaction's parameters to the {@link Locator} it would
|
|
121
|
+
* act on, mirroring the resolution in {@link call} (selector-based) and
|
|
122
|
+
* {@link callFromGpt} (annotation-based) but returning only the locator and
|
|
123
|
+
* never invoking anything. Returns `null` if the element can't be found.
|
|
124
|
+
*/
|
|
125
|
+
async resolvePreviewLocator(context, page, parameters) {
|
|
126
|
+
// Selector-based (deterministic) proposal.
|
|
127
|
+
if (parameters.selector) {
|
|
128
|
+
const parsed = ElementSelector_1.ElementSelectorSchema.safeParse(parameters.selector);
|
|
129
|
+
if (!parsed.success) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const locators = await ReplayableInteraction.getLocatorsOrderedByMatchCount(page, parsed.data, ReplayableInteraction.MAX_LOCATOR_MATCH_COUNT, ReplayableInteraction.MAX_SELECTOR_FAILOVERS);
|
|
133
|
+
return locators[0]?.locator ?? null;
|
|
134
|
+
}
|
|
135
|
+
// Annotation-based (LLM-driven) proposal.
|
|
136
|
+
if (typeof parameters.annotation === 'string' ||
|
|
137
|
+
typeof parameters.annotation === 'number') {
|
|
138
|
+
const elementSelector = `[${context.targetInspector.interactableElementAttribute}="${parameters.annotation}"]`;
|
|
139
|
+
for (const frame of page.frames()) {
|
|
140
|
+
if (frame.isDetached()) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const candidate = frame.locator(elementSelector);
|
|
145
|
+
if ((await candidate.count()) > 0) {
|
|
146
|
+
return candidate;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Detached or cross-origin frame — skip it.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
99
156
|
async callFromGpt(context, parameters) {
|
|
100
157
|
const page = (0, TargetUtils_1.webPage)(context);
|
|
101
158
|
const elementSelector = `[${context.targetInspector.interactableElementAttribute}="${parameters.annotation}"]`;
|
|
@@ -655,4 +712,10 @@ class ReplayableInteraction extends Tool_1.Tool {
|
|
|
655
712
|
exports.ReplayableInteraction = ReplayableInteraction;
|
|
656
713
|
ReplayableInteraction.MAX_SELECTOR_FAILOVERS = 3;
|
|
657
714
|
ReplayableInteraction.MAX_LOCATOR_MATCH_COUNT = 3;
|
|
715
|
+
/**
|
|
716
|
+
* How long the preview cursor takes to glide to (and dwell on) the proposed
|
|
717
|
+
* target, in milliseconds. {@link InteractionVisualizer.pointAt} animates the
|
|
718
|
+
* move over half this duration.
|
|
719
|
+
*/
|
|
720
|
+
ReplayableInteraction.PREVIEW_CURSOR_DURATION_MILLIS = 900;
|
|
658
721
|
//# sourceMappingURL=ReplayableInteraction.js.map
|
|
@@ -5,6 +5,7 @@ import { Tool } from './Tool';
|
|
|
5
5
|
export declare const SetRunModeCoreSchema: z.ZodObject<{
|
|
6
6
|
runMode: z.ZodEnum<{
|
|
7
7
|
AUTONOMOUS: "AUTONOMOUS";
|
|
8
|
+
SUPERVISED: "SUPERVISED";
|
|
8
9
|
INSTRUCT: "INSTRUCT";
|
|
9
10
|
DETERMINISTIC: "DETERMINISTIC";
|
|
10
11
|
}>;
|
|
@@ -12,6 +13,7 @@ export declare const SetRunModeCoreSchema: z.ZodObject<{
|
|
|
12
13
|
export declare const SetRunModeGptSchema: z.ZodObject<{
|
|
13
14
|
runMode: z.ZodEnum<{
|
|
14
15
|
AUTONOMOUS: "AUTONOMOUS";
|
|
16
|
+
SUPERVISED: "SUPERVISED";
|
|
15
17
|
INSTRUCT: "INSTRUCT";
|
|
16
18
|
DETERMINISTIC: "DETERMINISTIC";
|
|
17
19
|
}>;
|
package/dist/esm/tools/Tool.d.ts
CHANGED
|
@@ -36,16 +36,35 @@ export declare abstract class Tool<CallSchema extends z.ZodObject, CallFromGptSc
|
|
|
36
36
|
* Invoke the tool as made from a GPT with the given context and parameters.
|
|
37
37
|
*/
|
|
38
38
|
abstract callFromGpt(context: ToolCallContext, parameters: z.infer<CallFromGptSchema>): Promise<ToolCallResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Move the on-screen cursor to where this tool *would* interact, WITHOUT
|
|
41
|
+
* performing the action. Used by SUPERVISED mode to show the user what an
|
|
42
|
+
* AI-proposed action would touch while it awaits their approval.
|
|
43
|
+
*
|
|
44
|
+
* The default is a no-op — only tools with a visible interaction target
|
|
45
|
+
* (e.g. {@link ReplayableInteraction}-derived click/type/hover tools)
|
|
46
|
+
* override it. Implementations must be best-effort and side-effect-free with
|
|
47
|
+
* respect to the page: they may move the visual cursor but must never click,
|
|
48
|
+
* type, navigate, or otherwise mutate page state.
|
|
49
|
+
*
|
|
50
|
+
* @param context - The active {@link ToolCallContext}.
|
|
51
|
+
* @param parameters - The proposed tool call's raw parameters (as proposed by
|
|
52
|
+
* the LLM or a user); implementations validate/resolve these themselves.
|
|
53
|
+
*/
|
|
54
|
+
previewInteraction(_context: ToolCallContext, _parameters: Record<string, unknown>): Promise<void>;
|
|
39
55
|
/**
|
|
40
56
|
* Transform a completed tool call into a {@link ProposedToolCall} suitable
|
|
41
|
-
* for deterministic replay / code generation
|
|
57
|
+
* for deterministic replay / code generation, or `null` to exclude the call
|
|
58
|
+
* from replay entirely.
|
|
42
59
|
*
|
|
43
60
|
* The default implementation is a passthrough — `{ name, parameters }` —
|
|
44
61
|
* which is correct for tools that have no replay-specific logic
|
|
45
62
|
* (waits, assertions, markers, etc.). Tools that need to hoist
|
|
46
63
|
* selector metadata out of their outcome, strip LLM-only fields, or
|
|
47
|
-
* otherwise rewrite themselves override this method.
|
|
64
|
+
* otherwise rewrite themselves override this method. Tools that record
|
|
65
|
+
* live-interaction artifacts rather than replayable actions (e.g. a user
|
|
66
|
+
* instruction) return `null` so they don't reappear on reruns.
|
|
48
67
|
*/
|
|
49
|
-
prepareForRerun(toolCall: ToolCall, _options: CodeGenerationOptions): ProposedToolCall;
|
|
68
|
+
prepareForRerun(toolCall: ToolCall, _options: CodeGenerationOptions): ProposedToolCall | null;
|
|
50
69
|
}
|
|
51
70
|
//# sourceMappingURL=Tool.d.ts.map
|
package/dist/esm/tools/Tool.js
CHANGED
|
@@ -27,15 +27,34 @@ class Tool {
|
|
|
27
27
|
this.controlPanelMessage = controlPanelMessage;
|
|
28
28
|
this.supportedTargets = supportedTargets;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Move the on-screen cursor to where this tool *would* interact, WITHOUT
|
|
32
|
+
* performing the action. Used by SUPERVISED mode to show the user what an
|
|
33
|
+
* AI-proposed action would touch while it awaits their approval.
|
|
34
|
+
*
|
|
35
|
+
* The default is a no-op — only tools with a visible interaction target
|
|
36
|
+
* (e.g. {@link ReplayableInteraction}-derived click/type/hover tools)
|
|
37
|
+
* override it. Implementations must be best-effort and side-effect-free with
|
|
38
|
+
* respect to the page: they may move the visual cursor but must never click,
|
|
39
|
+
* type, navigate, or otherwise mutate page state.
|
|
40
|
+
*
|
|
41
|
+
* @param context - The active {@link ToolCallContext}.
|
|
42
|
+
* @param parameters - The proposed tool call's raw parameters (as proposed by
|
|
43
|
+
* the LLM or a user); implementations validate/resolve these themselves.
|
|
44
|
+
*/
|
|
45
|
+
async previewInteraction(_context, _parameters) { }
|
|
30
46
|
/**
|
|
31
47
|
* Transform a completed tool call into a {@link ProposedToolCall} suitable
|
|
32
|
-
* for deterministic replay / code generation
|
|
48
|
+
* for deterministic replay / code generation, or `null` to exclude the call
|
|
49
|
+
* from replay entirely.
|
|
33
50
|
*
|
|
34
51
|
* The default implementation is a passthrough — `{ name, parameters }` —
|
|
35
52
|
* which is correct for tools that have no replay-specific logic
|
|
36
53
|
* (waits, assertions, markers, etc.). Tools that need to hoist
|
|
37
54
|
* selector metadata out of their outcome, strip LLM-only fields, or
|
|
38
|
-
* otherwise rewrite themselves override this method.
|
|
55
|
+
* otherwise rewrite themselves override this method. Tools that record
|
|
56
|
+
* live-interaction artifacts rather than replayable actions (e.g. a user
|
|
57
|
+
* instruction) return `null` so they don't reappear on reruns.
|
|
39
58
|
*/
|
|
40
59
|
prepareForRerun(toolCall, _options) {
|
|
41
60
|
return {
|
|
@@ -138,6 +138,7 @@ export declare const TriggerDonobuFlowCoreSchema: z.ZodObject<{
|
|
|
138
138
|
gptConfigNameOverride: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
139
139
|
initialRunMode: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
140
140
|
AUTONOMOUS: "AUTONOMOUS";
|
|
141
|
+
SUPERVISED: "SUPERVISED";
|
|
141
142
|
INSTRUCT: "INSTRUCT";
|
|
142
143
|
DETERMINISTIC: "DETERMINISTIC";
|
|
143
144
|
}>>>;
|
|
@@ -286,6 +287,7 @@ export declare const TriggerDonobuFlowGptSchema: z.ZodObject<{
|
|
|
286
287
|
gptConfigNameOverride: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
287
288
|
initialRunMode: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
288
289
|
AUTONOMOUS: "AUTONOMOUS";
|
|
290
|
+
SUPERVISED: "SUPERVISED";
|
|
289
291
|
INSTRUCT: "INSTRUCT";
|
|
290
292
|
DETERMINISTIC: "DETERMINISTIC";
|
|
291
293
|
}>>>;
|
|
@@ -287,6 +287,10 @@ class AdminApiController {
|
|
|
287
287
|
app.post('/api/flows/:flowId/cancel', this.asyncHandler(apis.flowsApi.cancelFlow.bind(apis.flowsApi)));
|
|
288
288
|
app.post('/api/flows/:flowId/pause', this.asyncHandler(apis.flowsApi.pauseFlow.bind(apis.flowsApi)));
|
|
289
289
|
app.post('/api/flows/:flowId/resume', this.asyncHandler(apis.flowsApi.resumeFlow.bind(apis.flowsApi)));
|
|
290
|
+
app.post('/api/flows/:flowId/approve', this.asyncHandler(apis.flowsApi.approveProposal.bind(apis.flowsApi)));
|
|
291
|
+
app.post('/api/flows/:flowId/reject', this.asyncHandler(apis.flowsApi.rejectProposal.bind(apis.flowsApi)));
|
|
292
|
+
app.get('/api/flows/:flowId/pending-tool-calls', this.asyncHandler(apis.flowsApi.getPendingToolCalls.bind(apis.flowsApi)));
|
|
293
|
+
app.post('/api/flows/:flowId/run-mode', this.asyncHandler(apis.flowsApi.setRunMode.bind(apis.flowsApi)));
|
|
290
294
|
app.post('/api/flows/:flowId/tool-calls', this.asyncHandler(apis.flowsToolCallsApi.postToolCalls.bind(apis.flowsToolCallsApi)));
|
|
291
295
|
}
|
|
292
296
|
/**
|