@loadmill/droid-cua 2.10.1-rc.9 → 2.12.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.
@@ -40,7 +40,9 @@ CRITICAL RULES:
40
40
  Your process:
41
41
  1. Read the instruction
42
42
  2. Choose the right tool when the instruction is really a higher-level capability:
43
- - Use \`assert\` for assertions and verification intent, including legacy \`assert:\` lines and natural language like verify, validate, ensure, or confirm
43
+ - Use \`assert\` for assertions and verification intent, including legacy \`assert:\` lines and natural language like verify, validate, ensure, or confirm. When calling \`assert\`, include a \`context\` value with the prior details needed to interpret the condition. If no prior context is needed, say that explicitly.
44
+ - Before calling \`assert\`, make sure the current screen contains the evidence needed to validate the condition. If needed, perform only minimal preparation first, such as taking a screenshot, waiting briefly, closing the keyboard, scrolling within the relevant view, or opening the relevant menu.
45
+ - Once you call \`assert\`, validation is read-only. The assertion tool will not click, type, wait, scroll, request screenshots, or change the device state.
44
46
  - Use \`navigate\` for direct browser navigation instead of trying to manipulate the address bar
45
47
  - Use \`loadmill\` only when the instruction explicitly references the word "loadmill"
46
48
  3. Execute the required actions
@@ -88,24 +90,39 @@ ${transitionLine}${transcriptSection}
88
90
 
89
91
  [IMPORTANT: Continue the current test from this new page. Use the prior context above, but rely on the latest screenshot for the current UI.]`;
90
92
  }
91
- export function buildAssertionSystemPrompt(baseSystemPrompt, assertionPrompt) {
93
+ export function buildAssertionSystemPrompt(baseSystemPrompt, assertionPrompt, { assertionContext = "", transcript = "", } = {}) {
94
+ const normalizedAssertionContext = typeof assertionContext === "string" && assertionContext.trim()
95
+ ? assertionContext.trim()
96
+ : "No additional assertion context was provided.";
97
+ const normalizedTranscript = typeof transcript === "string" && transcript.trim()
98
+ ? transcript.trim()
99
+ : "No prior session transcript was provided.";
92
100
  return `${baseSystemPrompt}
93
101
 
94
102
  ASSERTION MODE:
95
- You are now validating an assertion. The user has provided an assertion statement that you must verify.
103
+ You are validating one assertion as part of an automated test run.
104
+ This is a read-only check.
96
105
 
97
106
  Your task:
98
- 1. Take screenshots and perform LIMITED actions if needed to validate the assertion.
99
- 2. Determine if the assertion is TRUE or FALSE based on the current state.
100
- 3. You MUST respond with a clear verdict in this exact format:
107
+ 1. Use only the provided screenshot, assertion context, app context already included above, and session transcript.
108
+ 2. Do not ask for more screenshots, do not click, do not type, do not wait, and do not propose actions.
109
+ 3. Determine if the assertion is TRUE or FALSE based on the current state and provided context.
110
+ 4. If the evidence is insufficient, treat the assertion as false.
111
+ 5. You MUST respond with a clear verdict in this exact format:
101
112
  - If the assertion is true, include the text: "ASSERTION RESULT: PASS"
102
113
  - If the assertion is false or cannot be confidently validated, include: "ASSERTION RESULT: FAIL"
103
- 4. After the verdict, provide a brief explanation (1-2 sentences) of why it passed or failed.
114
+ 6. After the verdict, provide a brief explanation (1-2 sentences) of why it passed or failed.
104
115
 
105
116
  The assertion to validate is: "${assertionPrompt}"
106
117
 
118
+ ASSERTION CONTEXT:
119
+ ${normalizedAssertionContext}
120
+
121
+ SESSION TRANSCRIPT SO FAR:
122
+ ${normalizedTranscript}
123
+
107
124
  Remember:
108
125
  - If you cannot confidently validate the assertion, treat it as FAIL.
109
126
  - You must include either "ASSERTION RESULT: PASS" or "ASSERTION RESULT: FAIL" in your response.
110
- - Be thorough but efficient. Only take the actions necessary to validate the assertion.`;
127
+ - This is read-only evaluation. Return the verdict and reason only.`;
111
128
  }
@@ -176,6 +176,17 @@ function extractResponseTexts(response) {
176
176
  })
177
177
  .filter(Boolean);
178
178
  }
179
+ function summarizeContextManagement(contextManagement) {
180
+ const entries = Array.isArray(contextManagement) ? contextManagement : [];
181
+ const compaction = entries.find((entry) => entry?.type === "compaction") || null;
182
+ return {
183
+ enabled: Boolean(compaction),
184
+ compactThreshold: typeof compaction?.compact_threshold === "number"
185
+ ? compaction.compact_threshold
186
+ : null,
187
+ entries: entries.length > 0 ? entries : [],
188
+ };
189
+ }
179
190
  export class CuaDebugTracer {
180
191
  constructor(logger) {
181
192
  this.logger = logger;
@@ -221,12 +232,14 @@ export class CuaDebugTracer {
221
232
  const missingCallIds = inputCallIds.filter((callId) => !lastResponseCallIds.includes(callId));
222
233
  const previousResponseIdMatchesLastResponseId = !previousResponseId || !lastResponseMeta?.id ? null : previousResponseId === lastResponseMeta.id;
223
234
  const allInputCallIdsFoundInLastResponse = inputCallIds.length === 0 ? true : missingCallIds.length === 0;
235
+ const contextManagement = summarizeContextManagement(requestParams.context_management);
224
236
  const requestConfig = {
225
237
  model: requestParams.model,
226
238
  tools: requestParams.tools,
227
239
  truncation: requestParams.truncation,
228
240
  reasoning: requestParams.reasoning,
229
- store: requestParams.store
241
+ store: requestParams.store,
242
+ context_management: requestParams.context_management || null
230
243
  };
231
244
  const requestConfigHash = crypto.createHash("sha256").update(JSON.stringify(requestConfig)).digest("hex");
232
245
  emitDesktopDebug("cua.request", scope, ids, {
@@ -249,6 +262,7 @@ export class CuaDebugTracer {
249
262
  acknowledgedSent: acknowledgedSafetyChecksSent,
250
263
  acknowledgedSentCount: acknowledgedSafetyChecksSent.length
251
264
  },
265
+ contextManagement,
252
266
  chain: {
253
267
  lastResponseId: lastResponseMeta?.id ?? null,
254
268
  lastResponseOutputTypes: lastResponseMeta?.outputTypes ?? [],
@@ -269,6 +283,7 @@ export class CuaDebugTracer {
269
283
  lastResponseComputerCallIds: lastResponseCallIds,
270
284
  allInputCallIdsFoundInLastResponse,
271
285
  missingCallIds,
286
+ contextManagement,
272
287
  requestConfigHash
273
288
  });
274
289
  return {
@@ -278,6 +293,7 @@ export class CuaDebugTracer {
278
293
  localRequestId,
279
294
  requestLog,
280
295
  requestConfigHash,
296
+ contextManagement,
281
297
  previousPendingSafetyChecks,
282
298
  acknowledgedSafetyChecksSent,
283
299
  previousResponseIdMatchesLastResponseId,
@@ -322,7 +338,8 @@ export class CuaDebugTracer {
322
338
  output_length: totalItems,
323
339
  output_types: outputTypes,
324
340
  tool_calls: toolCalls.length > 0 ? toolCalls : "none",
325
- pending_safety_checks: safetyChecks.length > 0 ? safetyChecks : "none"
341
+ pending_safety_checks: safetyChecks.length > 0 ? safetyChecks : "none",
342
+ usage: response.usage || null
326
343
  });
327
344
  if (accountedItems < totalItems) {
328
345
  this.logger.debug("UNACCOUNTED OUTPUT ITEMS - Full output array:", response.output);
@@ -334,6 +351,9 @@ export class CuaDebugTracer {
334
351
  outputTypes,
335
352
  output: summarizeResponseOutput(response),
336
353
  texts: extractResponseTexts(response),
354
+ usage: response.usage || null,
355
+ contextManagement: trace.contextManagement,
356
+ requestConfigHash: trace.requestConfigHash,
337
357
  safetyChecks: {
338
358
  pending: safetyChecks,
339
359
  pendingCount: safetyChecks.length
@@ -351,6 +371,7 @@ export class CuaDebugTracer {
351
371
  localRequestId: trace.localRequestId,
352
372
  request: trace.requestLog,
353
373
  requestConfigHash: trace.requestConfigHash,
374
+ contextManagement: trace.contextManagement,
354
375
  chain: {
355
376
  lastResponseId: trace.lastResponseMeta?.id ?? null,
356
377
  lastResponseOutputTypes: trace.lastResponseMeta?.outputTypes ?? [],
@@ -394,6 +415,7 @@ export class CuaDebugTracer {
394
415
  missingCallIds: trace.missingCallIds
395
416
  },
396
417
  requestConfigHash: trace.requestConfigHash,
418
+ contextManagement: trace.contextManagement,
397
419
  details: responseError,
398
420
  safetyChecks: {
399
421
  previousPending: trace.previousPendingSafetyChecks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loadmill/droid-cua",
3
- "version": "2.10.1-rc.9",
3
+ "version": "2.12.0",
4
4
  "description": "AI-powered Android testing agent using OpenAI's computer-use model and ADB",
5
5
  "main": "build/index.js",
6
6
  "type": "module",