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
|
@@ -5,6 +5,16 @@ const v4_1 = require("zod/v4");
|
|
|
5
5
|
const CodeGenerationOptions_1 = require("../models/CodeGenerationOptions");
|
|
6
6
|
const CreateDonobuFlow_1 = require("../models/CreateDonobuFlow");
|
|
7
7
|
const FlowMetadata_1 = require("../models/FlowMetadata");
|
|
8
|
+
const RunMode_1 = require("../models/RunMode");
|
|
9
|
+
/** Body schema for the reject-proposal endpoint (SUPERVISED mode). */
|
|
10
|
+
const RejectProposalBodySchema = v4_1.z.object({
|
|
11
|
+
feedback: v4_1.z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
/** Body schema for the set-run-mode endpoint (live autonomy switching). */
|
|
14
|
+
const SetRunModeBodySchema = v4_1.z.object({
|
|
15
|
+
runMode: RunMode_1.RunModeSchema,
|
|
16
|
+
approvePending: v4_1.z.boolean().optional(),
|
|
17
|
+
});
|
|
8
18
|
/**
|
|
9
19
|
* API controller for managing Donobu flows throughout their lifecycle.
|
|
10
20
|
*
|
|
@@ -284,9 +294,11 @@ class FlowsApi {
|
|
|
284
294
|
/**
|
|
285
295
|
* Pauses execution of an active flow.
|
|
286
296
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
297
|
+
* Submits a PAUSE user action; the flow's run loop applies it after its
|
|
298
|
+
* current action, transitioning to PAUSED. Like the desktop control panel's
|
|
299
|
+
* pause, this goes through the single user-action channel
|
|
300
|
+
* ({@link DonobuFlow.submitUserAction}) rather than mutating flow state
|
|
301
|
+
* directly.
|
|
290
302
|
*
|
|
291
303
|
* @throws {@link ActiveFlowNotFoundException} When the flow is not active locally
|
|
292
304
|
*
|
|
@@ -294,7 +306,8 @@ class FlowsApi {
|
|
|
294
306
|
* ```http
|
|
295
307
|
* POST /api/flows/abc123/pause
|
|
296
308
|
*
|
|
297
|
-
* Response: FlowMetadata
|
|
309
|
+
* Response: the flow's FlowMetadata (the pause is applied asynchronously by
|
|
310
|
+
* the run loop, so the returned metadata may not yet show PAUSED)
|
|
298
311
|
* ```
|
|
299
312
|
*
|
|
300
313
|
* @remarks
|
|
@@ -304,16 +317,18 @@ class FlowsApi {
|
|
|
304
317
|
async pauseFlow(req, res) {
|
|
305
318
|
const flowId = String(req.params.flowId);
|
|
306
319
|
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
307
|
-
if
|
|
308
|
-
|
|
309
|
-
}
|
|
320
|
+
// The PAUSE handler in onUserInterruption no-ops if the flow is already
|
|
321
|
+
// complete, so no guard is needed here.
|
|
322
|
+
flow.submitUserAction({ type: 'PAUSE' });
|
|
310
323
|
res.json(flow.metadata);
|
|
311
324
|
}
|
|
312
325
|
/**
|
|
313
326
|
* Resumes execution of a paused flow.
|
|
314
327
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
328
|
+
* Submits a RESUME user action (only when the flow is currently PAUSED); the
|
|
329
|
+
* run loop transitions PAUSED → RESUMING → normal operation. Like the desktop
|
|
330
|
+
* control panel's resume, this goes through the single user-action channel
|
|
331
|
+
* ({@link DonobuFlow.submitUserAction}).
|
|
317
332
|
*
|
|
318
333
|
* @throws {@link ActiveFlowNotFoundException} When the flow is not active locally
|
|
319
334
|
*
|
|
@@ -321,7 +336,8 @@ class FlowsApi {
|
|
|
321
336
|
* ```http
|
|
322
337
|
* POST /api/flows/abc123/resume
|
|
323
338
|
*
|
|
324
|
-
* Response: FlowMetadata
|
|
339
|
+
* Response: the flow's FlowMetadata (the resume is applied asynchronously by
|
|
340
|
+
* the run loop, so the returned metadata may not yet show RESUMING)
|
|
325
341
|
* ```
|
|
326
342
|
*
|
|
327
343
|
* @remarks
|
|
@@ -332,8 +348,120 @@ class FlowsApi {
|
|
|
332
348
|
const flowId = String(req.params.flowId);
|
|
333
349
|
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
334
350
|
if (flow.metadata.state === 'PAUSED') {
|
|
335
|
-
flow.
|
|
351
|
+
flow.submitUserAction({ type: 'RESUME' });
|
|
352
|
+
}
|
|
353
|
+
res.json(flow.metadata);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Approves the AI-proposed action(s) a SUPERVISED flow is waiting on.
|
|
357
|
+
*
|
|
358
|
+
* The flow leaves WAITING_FOR_APPROVAL and executes the proposed action(s)
|
|
359
|
+
* as-is, then continues (proposing its next action for approval).
|
|
360
|
+
*
|
|
361
|
+
* @throws {@link ActiveFlowNotFoundException} When the flow is not active locally
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```http
|
|
365
|
+
* POST /api/flows/abc123/approve
|
|
366
|
+
*
|
|
367
|
+
* Response: FlowMetadata
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* This endpoint is only available in LOCAL deployment environments for security.
|
|
372
|
+
* Only has an effect on flows currently in WAITING_FOR_APPROVAL state.
|
|
373
|
+
*/
|
|
374
|
+
async approveProposal(req, res) {
|
|
375
|
+
const flowId = String(req.params.flowId);
|
|
376
|
+
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
377
|
+
flow.submitUserAction({ type: 'APPROVE' });
|
|
378
|
+
res.json(flow.metadata);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Rejects the AI-proposed action(s) a SUPERVISED flow is waiting on.
|
|
382
|
+
*
|
|
383
|
+
* The proposed action(s) are discarded and the optional feedback is fed back
|
|
384
|
+
* to the AI, which then proposes a different next action (again awaiting
|
|
385
|
+
* approval).
|
|
386
|
+
*
|
|
387
|
+
* @throws {@link ActiveFlowNotFoundException} When the flow is not active locally
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```http
|
|
391
|
+
* POST /api/flows/abc123/reject
|
|
392
|
+
* { "feedback": "Use the search box instead of the menu." }
|
|
393
|
+
*
|
|
394
|
+
* Response: FlowMetadata
|
|
395
|
+
* ```
|
|
396
|
+
*
|
|
397
|
+
* @remarks
|
|
398
|
+
* This endpoint is only available in LOCAL deployment environments for security.
|
|
399
|
+
* Only has an effect on flows currently in WAITING_FOR_APPROVAL state.
|
|
400
|
+
*/
|
|
401
|
+
async rejectProposal(req, res) {
|
|
402
|
+
const flowId = String(req.params.flowId);
|
|
403
|
+
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
404
|
+
const { feedback } = RejectProposalBodySchema.parse(req.body ?? {});
|
|
405
|
+
flow.submitUserAction({ type: 'REJECT', feedback });
|
|
406
|
+
res.json(flow.metadata);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Returns the AI-proposed tool call(s) a SUPERVISED flow is currently waiting
|
|
410
|
+
* on for approval. Empty when the flow is not active locally or is not in the
|
|
411
|
+
* WAITING_FOR_APPROVAL state. Polled by the UI to render what the user is
|
|
412
|
+
* being asked to approve or reject.
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```http
|
|
416
|
+
* GET /api/flows/abc123/pending-tool-calls
|
|
417
|
+
*
|
|
418
|
+
* Response: { pendingToolCalls: ProposedToolCall[] }
|
|
419
|
+
* ```
|
|
420
|
+
*
|
|
421
|
+
* @remarks
|
|
422
|
+
* This endpoint is only available in LOCAL deployment environments.
|
|
423
|
+
*/
|
|
424
|
+
async getPendingToolCalls(req, res) {
|
|
425
|
+
const flowId = String(req.params.flowId);
|
|
426
|
+
let pendingToolCalls = [];
|
|
427
|
+
try {
|
|
428
|
+
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
429
|
+
if (flow.metadata.state === 'WAITING_FOR_APPROVAL') {
|
|
430
|
+
pendingToolCalls = [...flow.proposedToolCalls];
|
|
431
|
+
}
|
|
336
432
|
}
|
|
433
|
+
catch {
|
|
434
|
+
// Flow isn't active locally — nothing is pending approval.
|
|
435
|
+
}
|
|
436
|
+
res.json({ pendingToolCalls });
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Changes the run mode of an active flow at runtime — the autonomy selector.
|
|
440
|
+
*
|
|
441
|
+
* This is the general primitive behind "start asking me each step"
|
|
442
|
+
* (→ SUPERVISED), "go fully autonomous" (→ AUTONOMOUS), and "I'll take over"
|
|
443
|
+
* (→ INSTRUCT). When switching to AUTONOMOUS with `approvePending: true` and a
|
|
444
|
+
* proposal is awaiting approval, that proposal is approved and run as part of
|
|
445
|
+
* the switch (the "approve & let it run" shortcut).
|
|
446
|
+
*
|
|
447
|
+
* @throws {@link ActiveFlowNotFoundException} When the flow is not active locally
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```http
|
|
451
|
+
* POST /api/flows/abc123/run-mode
|
|
452
|
+
* { "runMode": "AUTONOMOUS", "approvePending": true }
|
|
453
|
+
*
|
|
454
|
+
* Response: FlowMetadata
|
|
455
|
+
* ```
|
|
456
|
+
*
|
|
457
|
+
* @remarks
|
|
458
|
+
* This endpoint is only available in LOCAL deployment environments.
|
|
459
|
+
*/
|
|
460
|
+
async setRunMode(req, res) {
|
|
461
|
+
const flowId = String(req.params.flowId);
|
|
462
|
+
const flow = this.donobuFlowsManager.getActiveFlow(flowId);
|
|
463
|
+
const { runMode, approvePending } = SetRunModeBodySchema.parse(req.body ?? {});
|
|
464
|
+
flow.submitUserAction({ type: 'SET_RUN_MODE', runMode, approvePending });
|
|
337
465
|
res.json(flow.metadata);
|
|
338
466
|
}
|
|
339
467
|
/**
|
|
@@ -70,9 +70,10 @@ class TestsApi {
|
|
|
70
70
|
const testId = String(req.params.testId);
|
|
71
71
|
const newFlowConfig = await this.testsManager.getNewFlowFromTest(testId);
|
|
72
72
|
const flowHandle = await this.flowsManager.createFlow(newFlowConfig);
|
|
73
|
-
// After starting an autonomous
|
|
74
|
-
// so subsequent runs replay the recorded actions.
|
|
75
|
-
if (newFlowConfig.initialRunMode === 'AUTONOMOUS'
|
|
73
|
+
// After starting an AI-driven run (autonomous or supervised), switch the
|
|
74
|
+
// test to deterministic so subsequent runs replay the recorded actions.
|
|
75
|
+
if (newFlowConfig.initialRunMode === 'AUTONOMOUS' ||
|
|
76
|
+
newFlowConfig.initialRunMode === 'SUPERVISED') {
|
|
76
77
|
const test = await this.testsManager.getTestById(testId);
|
|
77
78
|
await this.testsManager.updateTest({
|
|
78
79
|
...test,
|
|
@@ -480,7 +480,9 @@ async function buildCacheContents(flowsWithToolCalls, toolRegistry) {
|
|
|
480
480
|
const minimalToolNames = new Set(toolRegistry.minimalTools().map((t) => t.name));
|
|
481
481
|
const entries = flowsWithToolCalls
|
|
482
482
|
// We can only create page.ai caches for flows that have an objective.
|
|
483
|
-
.filter(({ metadata }) => metadata.overallObjective?.trim() &&
|
|
483
|
+
.filter(({ metadata }) => metadata.overallObjective?.trim() &&
|
|
484
|
+
(metadata.runMode === 'AUTONOMOUS' ||
|
|
485
|
+
metadata.runMode === 'SUPERVISED'))
|
|
484
486
|
.map(({ metadata, toolCalls }) => {
|
|
485
487
|
const [firstToolCall, ...remaingToolCalls] = toolCalls;
|
|
486
488
|
// If the first tool call is "GoToWebpage", then we peel it off and treat it
|
|
@@ -805,7 +807,7 @@ async function generateTestFiles(flowsWithToolCalls, options, toolRegistry) {
|
|
|
805
807
|
const fileName = getTestFileName(metadata);
|
|
806
808
|
const content = scriptVariant === 'classic' ||
|
|
807
809
|
!metadata.overallObjective?.trim() ||
|
|
808
|
-
metadata.runMode !== 'AUTONOMOUS'
|
|
810
|
+
(metadata.runMode !== 'AUTONOMOUS' && metadata.runMode !== 'SUPERVISED')
|
|
809
811
|
? await getFlowAsPlaywrightScript(metadata, toolCalls, options, toolRegistry)
|
|
810
812
|
: await getFlowAsAiPlaywrightScript(metadata, toolCalls, options, toolRegistry);
|
|
811
813
|
files.push({
|
|
@@ -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
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { z } from 'zod/v4';
|
|
2
2
|
import type { GptClient } from '../clients/GptClient';
|
|
3
3
|
import type { AiQuery } from '../models/AiQuery';
|
|
4
|
-
import type { ControlPanel } from '../models/ControlPanel';
|
|
4
|
+
import type { ControlPanel, UserAction } from '../models/ControlPanel';
|
|
5
5
|
import type { FlowMetadata } from '../models/FlowMetadata';
|
|
6
6
|
import type { GptMessage, StructuredOutputMessage, TextItem } from '../models/GptMessage';
|
|
7
7
|
import type { SystemMessage } from '../models/GptMessage';
|
|
@@ -40,8 +40,23 @@ export declare class DonobuFlow {
|
|
|
40
40
|
readonly controlPanel: ControlPanel;
|
|
41
41
|
private static readonly MAIN_MESSAGE_ELEMENT_LIST_MARKER;
|
|
42
42
|
static readonly USER_INTERRUPT_MARKER = "[User interruption while flow was paused, this MUST be acknowledged]";
|
|
43
|
+
static readonly REJECTION_MARKER = "[The user rejected your previously proposed action(s). Do NOT repeat them. Propose a different next action, taking the following feedback into account]";
|
|
43
44
|
inProgressToolCall: ToolCall | null;
|
|
44
45
|
readonly aiQueries: AiQuery[];
|
|
46
|
+
/**
|
|
47
|
+
* In SUPERVISED mode, the set of `toolCallId`s the user has explicitly
|
|
48
|
+
* approved. A proposed tool call only executes once its id is in this set;
|
|
49
|
+
* AI-proposed calls whose id is absent park the flow in
|
|
50
|
+
* `WAITING_FOR_APPROVAL`. Ids are removed as their calls run, so the set only
|
|
51
|
+
* ever holds currently-pending approvals.
|
|
52
|
+
*/
|
|
53
|
+
private readonly approvedToolCallIds;
|
|
54
|
+
/**
|
|
55
|
+
* User actions submitted out-of-band (e.g. via REST endpoints rather than the
|
|
56
|
+
* desktop control panel). Drained by the run loop alongside the control
|
|
57
|
+
* panel, so both surfaces drive the flow through the same code path.
|
|
58
|
+
*/
|
|
59
|
+
private readonly userActionInbox;
|
|
45
60
|
constructor(flowsManager: DonobuFlowsManager, envData: Record<string, string>, persistence: FlowsPersistence, gptClient: GptClient | null, toolManager: ToolManager, interactionVisualizer: InteractionVisualizer, proposedToolCalls: ProposedToolCall[], invokedToolCalls: ToolCall[], gptMessages: GptMessage[], targetInspector: TargetInspector, metadata: FlowMetadata, controlPanel: ControlPanel);
|
|
46
61
|
/**
|
|
47
62
|
* Drives the entire Donobu flow state-machine until it reaches a
|
|
@@ -78,6 +93,25 @@ export declare class DonobuFlow {
|
|
|
78
93
|
* explicit result.
|
|
79
94
|
*/
|
|
80
95
|
run(): Promise<FlowMetadata['result']>;
|
|
96
|
+
/**
|
|
97
|
+
* The single entry point for external user imperatives. Every cooperative
|
|
98
|
+
* control interrupt — pause, resume, end, approve, reject, run-mode change —
|
|
99
|
+
* arrives here as a {@link UserAction}, whether it came from a REST endpoint
|
|
100
|
+
* (web frontend / SDK) or the desktop control panel. The action is queued and
|
|
101
|
+
* drained by the run loop ({@link popUserAction}) and handled uniformly by
|
|
102
|
+
* {@link onUserInterruption}, so all transports drive the flow identically.
|
|
103
|
+
*
|
|
104
|
+
* (The forceful `cancelFlow` and the queue-injecting `proposeToolCall` on
|
|
105
|
+
* {@link DonobuFlowsManager} intentionally do NOT use this path — see their
|
|
106
|
+
* docs.)
|
|
107
|
+
*/
|
|
108
|
+
submitUserAction(action: UserAction): void;
|
|
109
|
+
/**
|
|
110
|
+
* Returns and clears the next pending user action, preferring out-of-band
|
|
111
|
+
* actions (REST) over the control panel. Both sources feed the same
|
|
112
|
+
* intervention path so the desktop and web surfaces behave identically.
|
|
113
|
+
*/
|
|
114
|
+
private popUserAction;
|
|
81
115
|
/**
|
|
82
116
|
* Delegates to the inspector to attempt recovery after the target is
|
|
83
117
|
* closed. If recovery fails, the flow is marked as failed.
|
|
@@ -100,6 +134,53 @@ export declare class DonobuFlow {
|
|
|
100
134
|
* Note that this *bypasses* the normal state transition logic!
|
|
101
135
|
*/
|
|
102
136
|
private onUserInterruption;
|
|
137
|
+
/**
|
|
138
|
+
* Incorporates the compose-field text from a ▶/⏩ action: if the flow has no
|
|
139
|
+
* standing goal yet, the text becomes the `overallObjective`; otherwise it's
|
|
140
|
+
* added as extra guidance. Either way it's injected into the LLM history (the
|
|
141
|
+
* system prompt was built at init, possibly before any objective existed) and
|
|
142
|
+
* recorded in the timeline. No-op for empty text.
|
|
143
|
+
*/
|
|
144
|
+
private applyComposeInstruction;
|
|
145
|
+
/**
|
|
146
|
+
* Closes out the currently-proposed AI tool call(s) without executing them:
|
|
147
|
+
* emits a `tool_call_result` for each (so the LLM message history stays
|
|
148
|
+
* well-formed — every tool call needs a matching result) and clears the
|
|
149
|
+
* proposal queue and any pending approvals. Shared by REJECT and manual
|
|
150
|
+
* takeover.
|
|
151
|
+
*/
|
|
152
|
+
private closeOutPendingProposals;
|
|
153
|
+
/**
|
|
154
|
+
* Records a synthetic {@link AcknowledgeUserInstructionTool} tool call so a
|
|
155
|
+
* user-driven event (rejection, mode change) shows up in the flow timeline.
|
|
156
|
+
* Mirrors how RESUME records a user instruction.
|
|
157
|
+
*/
|
|
158
|
+
private recordAdHocToolCall;
|
|
159
|
+
/**
|
|
160
|
+
* Moves the flow along the autonomy axis at runtime — the primitive behind
|
|
161
|
+
* "start asking me" (→ SUPERVISED), "go fully autonomous" (→ AUTONOMOUS),
|
|
162
|
+
* and "I'll take over" (→ INSTRUCT). After adjusting `runMode` and the
|
|
163
|
+
* pending proposal as appropriate, it routes through RESUMING so the next
|
|
164
|
+
* {@link transitionState} recomputes the correct state under the new mode.
|
|
165
|
+
*
|
|
166
|
+
* @param runMode - The target live mode. DETERMINISTIC is not a live mode and
|
|
167
|
+
* is ignored. AI modes (AUTONOMOUS/SUPERVISED) require a GPT client.
|
|
168
|
+
* @param approvePending - When switching to AUTONOMOUS with an AI proposal
|
|
169
|
+
* awaiting approval, approve and run it as part of the switch.
|
|
170
|
+
*/
|
|
171
|
+
private applyRunModeChange;
|
|
172
|
+
/**
|
|
173
|
+
* Whether the flow can hand control to the AI: it needs both a GPT client and
|
|
174
|
+
* a goal to pursue.
|
|
175
|
+
*/
|
|
176
|
+
private canHandOffToAi;
|
|
177
|
+
/**
|
|
178
|
+
* Whether there is a standing goal for the AI to pursue (a non-empty
|
|
179
|
+
* `overallObjective`). Surfaced to the UI as `hasGoal` to drive the
|
|
180
|
+
* transport: ⏩ Fast-forward (autonomous run) is only offered with a goal,
|
|
181
|
+
* and ▶ Play needs either a goal or a typed instruction.
|
|
182
|
+
*/
|
|
183
|
+
private hasGoal;
|
|
103
184
|
/**
|
|
104
185
|
* This method is called if there is an unhandled unexpected exception. This
|
|
105
186
|
* method will mark the flow as a failure.
|
|
@@ -171,9 +252,38 @@ export declare class DonobuFlow {
|
|
|
171
252
|
* initializes the GPT message history.
|
|
172
253
|
*/
|
|
173
254
|
private onInitializing;
|
|
255
|
+
/**
|
|
256
|
+
* Assembles the {@link ToolCallContext} handed to a tool. Shared by actual
|
|
257
|
+
* execution ({@link onRunningAction}) and the SUPERVISED-mode cursor preview
|
|
258
|
+
* ({@link previewProposedInteraction}) so both see an identical environment.
|
|
259
|
+
*/
|
|
260
|
+
private buildToolCallContext;
|
|
261
|
+
/**
|
|
262
|
+
* SUPERVISED mode: move the on-screen cursor to where the head proposed
|
|
263
|
+
* action *would* interact, so the user can see the target while deciding
|
|
264
|
+
* whether to approve it. This never executes the action — it only previews
|
|
265
|
+
* the interaction point. Best-effort: tools without a visible target (and any
|
|
266
|
+
* resolution failure) are simply skipped.
|
|
267
|
+
*/
|
|
268
|
+
private previewProposedInteraction;
|
|
174
269
|
private onRunningAction;
|
|
175
270
|
private onQueryingLlmForNextAction;
|
|
176
271
|
private onWaitingForUserForNextAction;
|
|
272
|
+
/**
|
|
273
|
+
* SUPERVISED mode: an AI-proposed action is parked awaiting the user's
|
|
274
|
+
* decision. We idle here until an APPROVE/REJECT (or other intervention)
|
|
275
|
+
* arrives via the control panel or a REST endpoint, which the run loop picks
|
|
276
|
+
* up as a {@link UserInterruptException}. Mirrors
|
|
277
|
+
* {@link onWaitingForUserForNextAction}.
|
|
278
|
+
*
|
|
279
|
+
* Unlike {@link onPaused}, we must NOT pin `nextState` here: the proposal
|
|
280
|
+
* still sits in `proposedToolCalls`, so the approval gate in
|
|
281
|
+
* {@link transitionState} re-parks us each poll on its own. Pinning it would
|
|
282
|
+
* also leave a stale `nextState` that survives an APPROVE interrupt (which
|
|
283
|
+
* sets `state` directly), causing the next transition to skip querying the
|
|
284
|
+
* LLM and park forever with an empty proposal queue.
|
|
285
|
+
*/
|
|
286
|
+
private onWaitingForApproval;
|
|
177
287
|
private onPaused;
|
|
178
288
|
private onResuming;
|
|
179
289
|
private onFailed;
|