Skip to main content
Looping tools allow you to repeat the same sequence of actions multiple times over a collection of items or a fixed count. The system learns the pattern once during the first iteration, then deterministically replays it for all remaining iterations.

Overview

Instead of manually repeating actions in a focused_action step, use start_loop and end_loop_iteration to automate repetitive tasks:
  • Learn once, replay many: The agent maps out one iteration, then the system replays it automatically
  • Access loop items: Use the special {{loop_item}} runtime variable to reference the current item in a focused_action step
  • Structured results: Get detailed summaries of all iterations including successes and failures
  • Cache friendly: Loops work seamlessly with trajectories for lightning-fast execution
  • Error handling: Loops exit early if errors occur, returning partial results

Actions

start_loop

Begin a loop iteration over an array or integer count. Parameters:
  • text: JSON array like ["item1", "item2", "item3"] or integer like "5" for counting loops
Usage:
Use start_loop with text='["Alice", "Bob", "Carol"]'
What Happens:
  1. System creates a loop context with all items
  2. Sets current iteration to 0 (first item)
  3. Agent completes ONE full iteration manually
  4. Agent calls end_loop_iteration when done
  5. System automatically replays remaining iterations
The {{loop_item}} Variable: Access the current loop item using the special {{loop_item}} runtime variable:
  • Simple items: Type {{loop_item}} → types “Alice” during iteration 0
  • Nested objects: {{loop_item.name}} → accesses name field
  • Array elements: {{loop_item[0]}} → accesses first array element
  • Deep nesting: {{loop_item.user.emails[0]}} → accesses nested data

end_loop_iteration

Mark the current iteration as complete and trigger replay of remaining iterations. Parameters:
  • text: Summary of this iteration (can use runtime variables for dynamic info)
Usage:
Use end_loop_iteration with text="Processed user {{loop_item}}: status={{status}}"
What Happens:
  1. System evaluates runtime variables in the summary text
  2. Adds iteration result to the results array
  3. If iteration 0: Captures trajectory steps and replays iterations 1 to N-1
  4. If recovery iteration: Replays from current + 1 to N-1
  5. Returns JSON summary of all iterations when complete
Summary Format:
{
  "total_iterations": 3,
  "completed_iterations": 3,
  "successful": 3,
  "failed": 0,
  "iteration_results": [
    {"iteration": 0, "status": "success", "summary": "Processed user Alice: status=complete"},
    {"iteration": 1, "status": "success", "summary": "Processed user Bob: status=complete"},
    {"iteration": 2, "status": "success", "summary": "Processed user Carol: status=complete"}
  ],
  "message": "Loop completed successfully: 3 successful, 0 failed"
}

Loop Types

Array Loop

Iterate over a collection of items. Example - Loop over names:
1. Use start_loop with text='["Alice", "Bob", "Carol"]'
2. Click on user {{loop_item}} in the list
3. Extract their email and save as {{user_email}}
4. Use end_loop_iteration with text="Processed {{loop_item}}: {{user_email}}"
Result: System executes the pattern for Alice, then automatically replays for Bob and Carol.

Integer Loop

Repeat actions a fixed number of times. The {{loop_item}} variable will be 0, 1, 2, etc. Example - Download 5 reports:
1. Navigate to reports page
2. Use start_loop with text="5"
3. Click on report number {{loop_item}}
4. Download the report
5. Use end_loop_iteration with text="Downloaded report {{loop_item}}"
Result: Executes for items 0, 1, 2, 3, 4.

Variable-Based Loop

Loop over data collected during the workflow using runtime or input variables. Example - Loop over runtime variable:
1. Use focused_action to extract all order IDs from the screen. Save as {{order_ids}} as a JSON array
2. Use start_loop with text="{{order_ids}}"
3. Navigate to order {{loop_item}}
4. Extract order details
5. Use end_loop_iteration with text="Processed order {{loop_item}}"
Example - Loop over input variable:
Input variables: {patient_ids: ["P001", "P002", "P003"]}

1. Use start_loop with text="{patient_ids}"
2. Search for patient {{loop_item}}
3. Extract medical records
4. Use end_loop_iteration with text="Extracted data for {{loop_item}}"

Passing Complex JSON Arrays via SDK

When creating a run programmatically, you can pass arrays of complex objects as input variables. The workflow can then loop over these objects and access nested fields. Workflow Prompt:
1. Use start_loop with text="{patients}"
2. Search for patient {{loop_item.mrn}}
3. Verify name matches {{loop_item.first_name}} {{loop_item.last_name}}
4. Navigate to the {{loop_item.department}} department
5. Extract the latest lab results
6. Use end_loop_iteration with text="Processed {{loop_item.first_name}} {{loop_item.last_name}}"
SDK Code to Create the Run:
  • TypeScript
  • Python
import { createCyberdeskClient } from 'cyberdesk';

const client = createCyberdeskClient('YOUR_API_KEY');

// Define your complex array of objects
const patients = [
  { mrn: 'MRN-001', first_name: 'Alice', last_name: 'Smith', department: 'Cardiology' },
  { mrn: 'MRN-002', first_name: 'Bob', last_name: 'Johnson', department: 'Neurology' },
  { mrn: 'MRN-003', first_name: 'Carol', last_name: 'Williams', department: 'Oncology' }
];

// Create the run with the array as an input variable
const { data: run } = await client.runs.create({
  workflow_id: 'your-workflow-id',
  machine_id: 'your-machine-id',
  input_values: {
    patients: patients  // Pass the array directly - SDK handles JSON serialization
  }
});

console.log('Run created:', run.id);
Accessing Nested Fields: Inside your loop, use dot notation to access object properties:
  • {{loop_item.mrn}}"MRN-001"
  • {{loop_item.first_name}}"Alice"
  • {{loop_item.department}}"Cardiology"
For deeply nested data, chain the accessors: {{loop_item.address.city}} or {{loop_item.contacts[0].email}}

Advanced Patterns

Nested Data Access

When looping over objects or arrays, access nested fields: Example:
// Input: [{"name": "Alice", "dept": "Sales"}, {"name": "Bob", "dept": "Engineering"}]

1. Use start_loop with text="{employees}"
2. Search for employee {{loop_item.name}}
3. Navigate to {{loop_item.dept}} department
4. Use end_loop_iteration with text="Processed {{loop_item.name}} from {{loop_item.dept}}"

Combining With Async Extraction

Extract data asynchronously while looping for maximum efficiency: Example:
1. Use start_loop with text='["Form1", "Form2", "Form3"]'
2. Navigate to {{loop_item}}
3. Take screenshot with extract_prompt="Extract all form data as JSON" and process_async="run"
4. Use end_loop_iteration with text="Queued extraction for {{loop_item}}"
5. All extractions complete at end of run before generating final output

Error Handling in Loops

Loops exit early if errors occur, returning partial results: Example:
1. Use start_loop with text="{document_names}"
2. Open document {{loop_item}}
3. If document fails to open, use declare_task_failed with message "Cannot open {{loop_item}}"
4. Process the document
5. Use end_loop_iteration with text="Completed {{loop_item}}"
If iteration 2 fails: Loop returns summary with iterations 0-1 successful, iteration 2 failed, and early_exit: true.

Cache Detection & Recovery

Loops integrate seamlessly with Cyberdesk’s trajectory caching system:

Cached Trajectory With Loop

When a trajectory containing a loop is replayed:
  1. start_loop executes from cache → creates loop context
  2. Iteration 0 steps execute from cache (click, type, focused_action, etc.)
  3. end_loop_iteration executes from cache → triggers automatic replay of iterations 1-N
  4. All iterations complete without invoking the AI
Result: Entire loop (all iterations) executes in seconds via cache!

Cache Miss Recovery

If cache detection fails during loop replay (e.g., UI changed):
  1. System detects cache miss at a specific step in iteration 2
  2. Recovery agent is invoked with loop context:
    • Current iteration number
    • Current loop item
    • Instructions to call end_loop_iteration after recovery
  3. Agent completes recovery and calls end_loop_iteration
  4. System automatically resumes replay for remaining iterations (3 to N-1)
Result: Graceful recovery with minimal AI invocation.

Important Limitations

Limited Cache Detection Inside Loops: Currently, cache detection (trajectory matching) is only performed at start_loop. Steps inside a loop iteration do not benefit from cache detection—they are always executed fresh during the first iteration, then deterministically replayed for subsequent iterations.If you need cache detection on individual steps, we recommend:
  1. Don’t use the loop feature for that workflow
  2. Create a separate Cyberdesk workflow just for the loop iteration logic
  3. Handle looping in your application code, creating a new Cyberdesk run for each iteration
This way, each run benefits from full trajectory caching and cache detection.We are actively working on seamless cache detection during Cyberdesk loops. Stay tuned for updates!
Nested Loops Not Supported: You cannot call start_loop while already in a loop. Complete the current loop first before starting a new one.If you attempt nested loops, the system will return an error: "Cannot start_loop while already in a loop. Call end_loop_iteration first."
Unclosed Loop Reminder: If you haven’t called end_loop_iteration after 20 steps, the system will append a reminder to your tool results. This prevents accidentally leaving loops open.

Complete Examples

Example 1: Discover and Process Patients (Runtime Variable Loop)

This example demonstrates a common pattern where the loop items are discovered during the run rather than passed as input. The agent extracts a list of patients from the screen and then loops over them. Workflow Prompt:
1. Navigate to patient management system
2. Login with {username} and {$password}
3. Navigate to "Today's Appointments" page
4. Use focused_action with instruction "Extract all patient IDs visible in the appointments table. Return them as a JSON array of objects with fields: id, name, appointment_time. Save the result as {{todays_patients}}"
5. Use start_loop with text="{{todays_patients}}"
6. Click on patient {{loop_item.name}} (ID: {{loop_item.id}})
7. Use focused_action to verify the patient record loaded successfully
8. Navigate to "Demographics" tab
9. Use screenshot with extract_prompt="Extract patient demographics as JSON with fields: name, dob, address, phone" and process_async="batch"
10. Navigate to "Insurance" tab
11. Use copy_to_clipboard with text="insurance_id" to copy the insurance ID
12. Use end_loop_iteration with text="Completed {{loop_item.name}} ({{loop_item.id}}) - Appointment: {{loop_item.appointment_time}}, Insurance: {{insurance_id}}"
13. System will automatically process remaining patients
14. Export final report with all patient data
Input Variables:
{
  "username": "admin"
}
What Happens:
  1. Agent logs in and navigates to the appointments page
  2. focused_action analyzes the screen and extracts patient data, saving it as {{todays_patients}}:
    [
      {"id": "P12345", "name": "Alice Smith", "appointment_time": "9:00 AM"},
      {"id": "P67890", "name": "Bob Johnson", "appointment_time": "10:30 AM"},
      {"id": "P11223", "name": "Carol Williams", "appointment_time": "2:00 PM"}
    ]
    
  3. start_loop begins iterating over this runtime-discovered array
  4. Agent completes iteration 0 (Alice Smith) manually
  5. System automatically replays iterations 1-2 for Bob and Carol
  6. Each iteration accesses nested fields: {{loop_item.id}}, {{loop_item.name}}, {{loop_item.appointment_time}}
Key Pattern: The loop items aren’t known ahead of time—they’re discovered by focused_action during execution and stored as a runtime variable. This is powerful for workflows where you need to “find what’s on the screen, then process each item.”

Example 2: Batch Download Forms

Workflow Prompt:
1. Navigate to forms portal
2. Use start_loop with text="10"
3. Click on form row {{loop_item}}
4. Click "Download PDF" button
5. Wait 2 seconds for download
6. Use end_loop_iteration with text="Downloaded form {{loop_item}}"
7. Use execute_terminal_command to run "Get-ChildItem $env:USERPROFILE\Downloads\*.pdf | Select-Object -First 10 | ConvertTo-Json" to list downloaded files
Execution Flow:
  • Iteration 0: Agent downloads form 0
  • Iterations 1-9: System automatically downloads forms 1-9
  • Terminal command lists all downloaded files
  • Total time: ~10 seconds (cached) vs ~5 minutes (uncached)

Example 3: Data Entry From Spreadsheet

Workflow Prompt:
1. Use execute_terminal_command to run "Import-Csv {csv_file_path} | ConvertTo-Json" to load spreadsheet data
2. Use focused_action to parse the JSON output and save the array as {{records}}
3. Navigate to data entry form
4. Use start_loop with text="{{records}}"
5. Click "New Record" button
6. Type {{loop_item.name}} in the Name field
7. Type {{loop_item.email}} in the Email field
8. Type {{loop_item.phone}} in the Phone field
9. Click "Save" button
10. Use focused_action to verify "Record saved successfully" message appears
11. Use end_loop_iteration with text="Entered record for {{loop_item.name}}"
12. Click "Close" to exit data entry
Result: Efficiently processes entire spreadsheet with validation at each step.

Best Practices

Add focused_action steps within your loop to verify each iteration succeeded:
1. Use start_loop with text="{items}"
2. Process {{loop_item}}
3. Use focused_action to verify processing succeeded
4. Use end_loop_iteration with text="Verified {{loop_item}}"
This ensures errors are caught immediately per iteration.
Make iteration summaries dynamic by using runtime variables:
Use end_loop_iteration with text="Processed {{loop_item}}: found {{count}} results, status={{status}}"
This creates rich per-iteration results you can analyze later.
Keep loop iterations concise (5-15 steps). For complex processing:
1. Use start_loop with text="{orders}"
2. Use focused_action to process order {{loop_item}} completely
3. Use end_loop_iteration with text="Completed {{loop_item}}"
Let focused_action handle complex logic per iteration.
Set flags during iterations for later decision-making:
1. Use start_loop with text="{invoices}"
2. Check invoice {{loop_item}} status
3. If unpaid, save unpaid_count via upsert_runtime_values
4. Use end_loop_iteration with text="Checked {{loop_item}}"
5. After loop: If {{unpaid_count}} > 0, send notification email

Troubleshooting

Cause: An error occurred in one of the iterations.Solution: Check the loop summary’s iteration_results array to see which iteration failed and why. Add focused_action validation steps to catch errors early.
Cause: Runtime variable replacement failed or loop wasn’t started.Solution: Ensure start_loop was called before using {{loop_item}}. Check that the loop items array is valid JSON.
Cause: Attempted to call start_loop while already in a loop.Solution: Complete the current loop with end_loop_iteration before starting a new loop. Nested loops are not supported in the current version.
Cause: You haven’t called end_loop_iteration after 20+ steps.Solution: Call end_loop_iteration when the iteration is complete. The reminder appears every 20 steps to prevent accidental unclosed loops.