@loadmill/droid-cua 2.17.0 → 2.19.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/README.md +18 -0
- package/build/index.js +16 -1
- package/build/src/browser/playwright/actions.js +17 -8
- package/build/src/cli/headless-execution-config.js +6 -0
- package/build/src/cli/headless-report.js +113 -0
- package/build/src/cli/headless-runner.js +40 -6
- package/build/src/commands/help.js +1 -0
- package/build/src/commands/run.js +2 -1
- package/build/src/core/execution-engine.js +125 -2
- package/build/src/core/prompts.js +1 -1
- package/build/src/core/report/generate-html-report.js +88 -0
- package/build/src/core/report/parse-run-report.js +222 -0
- package/build/src/core/report/render-html-report.js +317 -0
- package/build/src/core/report/report-file-name.js +31 -0
- package/build/src/core/report/run-log-model.js +146 -0
- package/build/src/core/report/run-report-model.js +143 -0
- package/build/src/core/secrets.js +116 -0
- package/build/src/core/tool-handlers/close-current-tab.js +3 -3
- package/build/src/core/tool-handlers/navigate.js +3 -3
- package/build/src/core/tool-handlers/preview-click.js +6 -6
- package/build/src/core/tool-handlers/type-sensitive-text.js +62 -0
- package/build/src/core/tool-handlers/utils.js +3 -3
- package/build/src/device/android/actions.js +13 -8
- package/build/src/device/cloud/actions.js +17 -8
- package/build/src/device/ios/actions.js +17 -8
- package/build/src/device/openai.js +17 -0
- package/build/src/device/sensitive-action.js +61 -0
- package/build/src/modes/design-mode-ink.js +3 -1
- package/build/src/modes/design-mode.js +3 -1
- package/build/src/modes/execution-mode.js +17 -0
- package/build/src/prompts/design.js +4 -3
- package/build/src/prompts/execution.js +15 -1
- package/package.json +1 -1
|
@@ -70,6 +70,7 @@ export class ExecutionMode {
|
|
|
70
70
|
navigate: this.isBrowserSession,
|
|
71
71
|
closeCurrentTab: this.isBrowserSession,
|
|
72
72
|
loadmill: true,
|
|
73
|
+
typeSensitiveText: this.engine.hasSecrets(),
|
|
73
74
|
};
|
|
74
75
|
this.engine.baseSystemPrompt = this.initialSystemText;
|
|
75
76
|
this.engine.isHeadlessMode = this.isHeadlessMode;
|
|
@@ -144,6 +145,19 @@ export class ExecutionMode {
|
|
|
144
145
|
});
|
|
145
146
|
return { success: false, error: 'Stopped by user' };
|
|
146
147
|
}
|
|
148
|
+
if (runContext.waitIfPaused) {
|
|
149
|
+
const pauseResult = await runContext.waitIfPaused({
|
|
150
|
+
boundary: 'before_instruction',
|
|
151
|
+
refreshScreenOnResume: false,
|
|
152
|
+
stepContext,
|
|
153
|
+
});
|
|
154
|
+
if (pauseResult?.status === 'stopped' || this.shouldStop) {
|
|
155
|
+
this.emit(addOutput, 'info', 'Test execution stopped by user.', runContext, stepContext, {
|
|
156
|
+
eventType: 'system_message'
|
|
157
|
+
});
|
|
158
|
+
return { success: false, error: 'Stopped by user' };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
147
161
|
const instruction = this.instructions[i];
|
|
148
162
|
this.emit(addOutput, 'user', instruction, runContext, stepContext, {
|
|
149
163
|
eventType: 'instruction_started',
|
|
@@ -255,6 +269,9 @@ export class ExecutionMode {
|
|
|
255
269
|
return this.shouldStop;
|
|
256
270
|
};
|
|
257
271
|
const { responseId: newResponseId, summary: turnSummary } = await this.engine.runFullTurn(response, trackAction, instructionContext, stepContext, { currentScreenshot });
|
|
272
|
+
if (turnSummary.stopped || this.shouldStop) {
|
|
273
|
+
return { success: false, error: "Stopped by user" };
|
|
274
|
+
}
|
|
258
275
|
if (turnSummary.assertionStatus === "passed") {
|
|
259
276
|
this.stats.assertionsPassed++;
|
|
260
277
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { appendCustomSections, buildBaseSystemPrompt, isBrowserTarget } from "./base.js";
|
|
2
|
-
import { buildAppContextSection } from "./execution.js";
|
|
3
|
-
export function buildDesignModePrompt(deviceInfo, customInstructions = {}, appContextText = "") {
|
|
2
|
+
import { buildAppContextSection, buildSecretKeysContextSection } from "./execution.js";
|
|
3
|
+
export function buildDesignModePrompt(deviceInfo, customInstructions = {}, appContextText = "", options = {}) {
|
|
4
4
|
const designCustomText = typeof customInstructions.designModeInstructions === "string" ? customInstructions.designModeInstructions.trim() : "";
|
|
5
5
|
const basePrompt = buildBaseSystemPrompt(deviceInfo, customInstructions);
|
|
6
6
|
const appContextSection = buildAppContextSection(appContextText);
|
|
7
|
+
const secretKeysContextSection = buildSecretKeysContextSection(options.availableSecretKeys);
|
|
7
8
|
const browserTarget = isBrowserTarget(deviceInfo);
|
|
8
9
|
const prompt = `${basePrompt}
|
|
9
10
|
|
|
@@ -93,7 +94,7 @@ WRONG Example (DON'T DO THIS):
|
|
|
93
94
|
3. etc...
|
|
94
95
|
\`\`\`
|
|
95
96
|
|
|
96
|
-
Remember: You are autonomous. Explore confidently. Generate simple, executable test scripts.${appContextSection ? `\n\n${appContextSection}` : ""}
|
|
97
|
+
Remember: You are autonomous. Explore confidently. Generate simple, executable test scripts.${appContextSection ? `\n\n${appContextSection}` : ""}${secretKeysContextSection ? `\n\n${secretKeysContextSection}` : ""}
|
|
97
98
|
`;
|
|
98
99
|
return appendCustomSections(prompt, [
|
|
99
100
|
{ title: "Base Prompt Instructions", text: customInstructions.basePromptInstructions },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_AIM_ASSIST_MODE } from "../core/aim-assist.js";
|
|
2
|
+
import { normalizeSecretKeys } from "../core/secrets.js";
|
|
2
3
|
import { appendCustomSections, buildBaseSystemPrompt, isBrowserTarget } from "./base.js";
|
|
3
4
|
export function buildAppContextSection(text) {
|
|
4
5
|
const trimmed = typeof text === "string" ? text.trim() : "";
|
|
@@ -22,12 +23,25 @@ They do not replace or override the test instructions.
|
|
|
22
23
|
|
|
23
24
|
${trimmed}`;
|
|
24
25
|
}
|
|
26
|
+
export function buildSecretKeysContextSection(keys = []) {
|
|
27
|
+
const normalized = normalizeSecretKeys(keys);
|
|
28
|
+
if (normalized.length === 0) {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
return `SECRET KEYS FOR THIS RUN:
|
|
32
|
+
The following secret keys are available. You can see only the keys, not the values.
|
|
33
|
+
Use the \`type_sensitive_text\` tool when you need to enter passwords, OTPs, tokens, API keys, or other hidden credentials.
|
|
34
|
+
Never ask the user to reveal these values and never type placeholder text when a matching secret key is available.
|
|
35
|
+
|
|
36
|
+
${normalized.map((key) => `- ${key}`).join("\n")}`;
|
|
37
|
+
}
|
|
25
38
|
export function buildExecutionModePrompt(deviceInfo, customInstructions = {}, appContextText = "", options = {}) {
|
|
26
39
|
const executionCustomText = typeof customInstructions.executionModeInstructions === "string" ? customInstructions.executionModeInstructions.trim() : "";
|
|
27
40
|
const aimAssistMode = typeof options.aimAssistMode === "string" ? options.aimAssistMode : DEFAULT_AIM_ASSIST_MODE;
|
|
28
41
|
const basePrompt = buildBaseSystemPrompt(deviceInfo, { aimAssistMode });
|
|
29
42
|
const appContextSection = buildAppContextSection(appContextText);
|
|
30
43
|
const testDataContextSection = buildTestDataContextSection(options.testDataContext);
|
|
44
|
+
const secretKeysContextSection = buildSecretKeysContextSection(options.availableSecretKeys);
|
|
31
45
|
const browserTarget = isBrowserTarget(deviceInfo);
|
|
32
46
|
const prompt = `${basePrompt}
|
|
33
47
|
|
|
@@ -64,7 +78,7 @@ Your process:
|
|
|
64
78
|
7. Stop immediately - no commentary, no questions
|
|
65
79
|
|
|
66
80
|
Each instruction is independent. Do not reference previous instructions or ask about next steps.
|
|
67
|
-
${appContextSection ? `\n\n${appContextSection}` : ""}${testDataContextSection ? `\n\n${testDataContextSection}` : ""}`;
|
|
81
|
+
${appContextSection ? `\n\n${appContextSection}` : ""}${testDataContextSection ? `\n\n${testDataContextSection}` : ""}${secretKeysContextSection ? `\n\n${secretKeysContextSection}` : ""}`;
|
|
68
82
|
return appendCustomSections(prompt, [
|
|
69
83
|
{ title: "Base Prompt Instructions", text: customInstructions.basePromptInstructions },
|
|
70
84
|
{ title: "Execution Mode Instructions", text: executionCustomText }
|