@minicor/mcp-server 3.1.4 → 3.2.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.
Files changed (55) hide show
  1. package/README.md +251 -61
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/laminar-client.d.ts +5 -0
  6. package/dist/laminar-client.d.ts.map +1 -1
  7. package/dist/laminar-client.js +6 -0
  8. package/dist/laminar-client.js.map +1 -1
  9. package/dist/lib.d.ts.map +1 -1
  10. package/dist/lib.js +2 -0
  11. package/dist/lib.js.map +1 -1
  12. package/dist/prompts/build-rpa.d.ts.map +1 -1
  13. package/dist/prompts/build-rpa.js +86 -272
  14. package/dist/prompts/build-rpa.js.map +1 -1
  15. package/dist/prompts/debug-execution.js +1 -1
  16. package/dist/seed-skills.d.ts +13 -0
  17. package/dist/seed-skills.d.ts.map +1 -0
  18. package/dist/seed-skills.js +82 -0
  19. package/dist/seed-skills.js.map +1 -0
  20. package/dist/skills-service-client.d.ts +54 -0
  21. package/dist/skills-service-client.d.ts.map +1 -0
  22. package/dist/skills-service-client.js +93 -0
  23. package/dist/skills-service-client.js.map +1 -0
  24. package/dist/skills.d.ts +34 -0
  25. package/dist/skills.d.ts.map +1 -0
  26. package/dist/skills.js +152 -0
  27. package/dist/skills.js.map +1 -0
  28. package/dist/sync.d.ts +18 -2
  29. package/dist/sync.d.ts.map +1 -1
  30. package/dist/sync.js +404 -104
  31. package/dist/sync.js.map +1 -1
  32. package/dist/tools/core.d.ts.map +1 -1
  33. package/dist/tools/core.js +12 -0
  34. package/dist/tools/core.js.map +1 -1
  35. package/dist/tools/skills.d.ts +3 -0
  36. package/dist/tools/skills.d.ts.map +1 -0
  37. package/dist/tools/skills.js +429 -0
  38. package/dist/tools/skills.js.map +1 -0
  39. package/dist/tools/sync-tools.d.ts.map +1 -1
  40. package/dist/tools/sync-tools.js +53 -12
  41. package/dist/tools/sync-tools.js.map +1 -1
  42. package/dist/tools/vm.js +2 -2
  43. package/dist/tools/vm.js.map +1 -1
  44. package/dist/tools/workflow-ops.js +1 -1
  45. package/dist/tools/workflow-ops.js.map +1 -1
  46. package/package.json +4 -2
  47. package/skills/general/cdp-browser-automation.md +97 -0
  48. package/skills/general/data-extraction-strategies.md +64 -0
  49. package/skills/general/data-hydration-patterns.md +167 -0
  50. package/skills/general/data-passing-between-steps.md +46 -0
  51. package/skills/general/desktop-uiautomation.md +80 -0
  52. package/skills/general/rpa-testing-workflow.md +145 -0
  53. package/skills/general/session-keepalive.md +101 -0
  54. package/skills/general/smart-launch-patterns.md +213 -0
  55. package/skills/general/state-verification.md +56 -0
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: data-hydration-patterns
3
+ description: Patterns for building standard CRUD data workflows (Get Practitioners, Patients, Appointments, Encounter Notes). API-first vs RPA approaches, data output conventions, pagination, orchestration order, pass-through return steps. Use when building data extraction or synchronization workflows for healthcare/enterprise apps.
4
+ category: general
5
+ tags: [data, hydration, crud, practitioners, patients, appointments, notes, api, scraping]
6
+ priority: 8
7
+ ---
8
+
9
+ # Data Hydration Patterns
10
+
11
+ Most Minicor workspaces follow a standard set of data workflows. This skill documents the patterns across production deployments.
12
+
13
+ ## The Standard Workflow Set
14
+
15
+ Every EHR/EMR integration workspace typically needs:
16
+
17
+ 1. **Get Practitioners** — list of doctors/providers
18
+ 2. **Get Patients** — patient roster (all or by search)
19
+ 3. **Get Patient By ID** — single patient detail
20
+ 4. **Get Appointments** — schedule for a date range
21
+ 5. **Get Encounter Notes** — clinical notes for a patient
22
+ 6. **Create Encounter Note** — write back a note
23
+ 7. **Verify Patient** — confirm a patient exists by name/DOB
24
+
25
+ ## Two Approaches
26
+
27
+ ### API-First (Preferred)
28
+
29
+ If the app has accessible internal APIs (discovered via CDP network interception or documentation):
30
+
31
+ ```python
32
+ import json, urllib.request
33
+
34
+ url = f'{base_url}/api/patients?page={page}&size=100'
35
+ req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
36
+ resp = urllib.request.urlopen(req)
37
+ data = json.loads(resp.read())
38
+ ```
39
+
40
+ **Pros**: Fast, reliable, handles pagination natively, returns structured data.
41
+ **Examples**: HSMC (Insta HMS API), Novelle SQL queries.
42
+
43
+ ### RPA Scraping (Fallback)
44
+
45
+ When no API is available, scrape data from the UI via CDP or uiautomation:
46
+
47
+ ```python
48
+ # CDP: Execute JS to extract table data
49
+ table_data = cdp_eval(ws, """
50
+ (function() {
51
+ var rows = document.querySelectorAll('table tbody tr');
52
+ var data = [];
53
+ rows.forEach(function(row) {
54
+ var cells = row.querySelectorAll('td');
55
+ data.push({
56
+ name: cells[0]?.textContent?.trim(),
57
+ id: cells[1]?.textContent?.trim(),
58
+ dob: cells[2]?.textContent?.trim()
59
+ });
60
+ });
61
+ return JSON.stringify(data);
62
+ })();
63
+ """)
64
+ ```
65
+
66
+ **Pros**: Works when no API exists.
67
+ **Cons**: Fragile, slow, pagination via scrolling.
68
+ **Examples**: Hillside (PracticeFusion CDP scraping).
69
+
70
+ ## Data Output Convention
71
+
72
+ All data workflows should return structured JSON:
73
+
74
+ ```json
75
+ {
76
+ "count": 42,
77
+ "data": [
78
+ {"id": "P001", "name": "John Doe", "dob": "1990-01-15"},
79
+ {"id": "P002", "name": "Jane Smith", "dob": "1985-07-22"}
80
+ ]
81
+ }
82
+ ```
83
+
84
+ For single-record queries (Get Patient By ID):
85
+
86
+ ```json
87
+ {
88
+ "patient_id": "P001",
89
+ "name": "John Doe",
90
+ "dob": "1990-01-15",
91
+ "phone": "555-1234"
92
+ }
93
+ ```
94
+
95
+ ## The Pass-Through Return Step
96
+
97
+ The workflow engine returns whatever the LAST step produces. If your last step is RPA (cleanup/navigation), the data gets lost.
98
+
99
+ **Pattern**: Add a final GENERAL_FUNCTION step that passes through the data:
100
+
101
+ ```javascript
102
+ (data) => {
103
+ return data.step_3?.data || data.step_3?.response;
104
+ }
105
+ ```
106
+
107
+ This ensures the formatted data from step 3 becomes the workflow output.
108
+
109
+ ## Pagination
110
+
111
+ ### API Pagination
112
+
113
+ ```python
114
+ all_records = []
115
+ page = 1
116
+ while True:
117
+ data = fetch_page(page, page_size=100)
118
+ all_records.extend(data['records'])
119
+ if len(data['records']) < 100:
120
+ break
121
+ page += 1
122
+ ```
123
+
124
+ ### CDP Scroll Pagination
125
+
126
+ ```python
127
+ previous_count = 0
128
+ while True:
129
+ # Scroll to bottom
130
+ cdp_eval(ws, 'window.scrollTo(0, document.body.scrollHeight)')
131
+ time.sleep(2)
132
+
133
+ # Count rows
134
+ count = int(cdp_eval(ws, 'document.querySelectorAll("table tbody tr").length'))
135
+ if count == previous_count:
136
+ break # No new rows loaded
137
+ previous_count = count
138
+ ```
139
+
140
+ ## Hydration Orchestration
141
+
142
+ When running multiple data workflows to populate a cache:
143
+
144
+ 1. **Practitioners first** — no dependencies
145
+ 2. **Appointments** — optionally filtered by practitioner IDs from step 1
146
+ 3. **Patients from appointments** — derived from appointment data (step 2)
147
+ 4. **Encounter notes** — for patients from step 3
148
+
149
+ This is the pattern used by HSMC (WFs 753-756) — each workflow is standalone but designed to chain.
150
+
151
+ ## Workflow Step Structure
152
+
153
+ A typical data workflow has 3-4 steps:
154
+
155
+ 1. **Navigate/Setup** (RPA) — navigate to the right page or prepare the API call
156
+ 2. **Extract/Scrape** (RPA) — get the raw data via API or scraping
157
+ 3. **Format** (GENERAL_FUNCTION) — transform raw data into the standard JSON format
158
+ 4. **Return** (GENERAL_FUNCTION) — pass-through step that ensures data is the workflow output
159
+
160
+ ## Config Store Properties
161
+
162
+ Standard properties for data workflows:
163
+ - `{{config.username}}` / `{{config.password}}` — credentials
164
+ - `{{config.url}}` or `{{config.base_url}}` — app URL
165
+ - `{{config.channelId}}` — RPA channel for desktop dispatch
166
+ - `{{config.sullyOrganizationId}}` — customer ID for middleware routing
167
+ - `{{config.BASE_API_URL}}` — middleware/API base URL
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: data-passing-between-steps
3
+ description: How to pass data between workflow steps in Minicor RPA workflows. JS-layer interpolation from data.input and data.step_N, config variables via {{config.xxx}}, common pitfalls with Python template vars. Use when building multi-step workflows that need to share data.
4
+ category: general
5
+ tags: [data-passing, workflow, steps, interpolation, config]
6
+ priority: 9
7
+ ---
8
+
9
+ # Data Passing Between Steps
10
+
11
+ Workflow input is available as `data.input` in the **JS wrapper**, NOT in Python directly. Dynamic values must be interpolated in the JS layer, then embedded into the Python template literal.
12
+
13
+ ## CORRECT — JS-Layer Interpolation
14
+
15
+ ```javascript
16
+ (data) => {
17
+ const mrn = data?.input?.mrn || "DEFAULT";
18
+ const pythonScript = `
19
+ mrn = "${mrn}"
20
+ # ... rest of Python script uses mrn variable
21
+ `;
22
+ return { "lam.httpRequest": { body: { script: pythonScript } } };
23
+ }
24
+ ```
25
+
26
+ ## WRONG — Python Template Vars
27
+
28
+ ```python
29
+ mrn = "{{input.mrn}}" # WRONG — this is not a config variable
30
+ mrn = data.input.mrn # WRONG — data object doesn't exist in Python
31
+ ```
32
+
33
+ ## Config Variables
34
+
35
+ `{{config.username}}` and `{{config.password}}` ARE resolved by the workflow engine and work in both JS and Python layers. Use these for credentials and environment URLs via config stores. **NEVER** hardcode credentials.
36
+
37
+ ## Previous Step Outputs
38
+
39
+ - `data.step_N.response` — for HTTP/RPA steps
40
+ - `data.step_N.data` — for GENERAL_FUNCTION steps
41
+
42
+ ## Important Notes
43
+
44
+ - `create_rpa_flow` generates the JS wrapper automatically, but if the script needs dynamic input from `data.input` or `data.step_N`, you must construct the JS wrapper yourself or update the flow after creation to add JS-layer const declarations
45
+ - The **last step** is the workflow output. If you need structured data returned, the last step must output it. Don't put meaningful data in step N and cleanup in step N+1. Combine cleanup + data return in the last step, or add a GENERAL_FUNCTION step after RPA that formats the final result.
46
+
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: desktop-uiautomation
3
+ description: Desktop RPA patterns using uiautomation, pywinauto, and pyautogui. Framework selection by app type, element-based selectors over coordinates, wait-for-element patterns, retry strategies, popup dismissal. Use when building desktop automations or Windows app RPA.
4
+ category: general
5
+ tags: [desktop, uiautomation, pywinauto, pyautogui, windows, jab]
6
+ priority: 10
7
+ ---
8
+
9
+ # Desktop UI Automation
10
+
11
+ ## Framework Selection
12
+
13
+
14
+ | App Type | Framework |
15
+ | ---------------------------- | --------------------------------------- |
16
+ | .NET / WPF / WinForms | `uiautomation` (default) or `pywinauto` |
17
+ | Java / Swing | `jab` (Java Access Bridge) |
18
+ | Electron / web-based desktop | `pywinauto` or `pyautogui` |
19
+ | Legacy Win32 | `uiautomation` |
20
+ | Unknown | Start with `uiautomation` |
21
+
22
+
23
+ Desktop scripts use `/execute` and run **one at a time** per VM (the physical screen is shared).
24
+
25
+ ## Wait for Elements (Never `time.sleep` for UI Waits)
26
+
27
+ ```python
28
+ for _ in range(20):
29
+ el = auto.WindowControl(Name="Patient Search")
30
+ if el.Exists(0.5): break
31
+ time.sleep(0.5)
32
+ else:
33
+ raise TimeoutError("Patient Search not found after 10s")
34
+ ```
35
+
36
+ ## Click by Element Selector (Not Coordinates)
37
+
38
+ ```python
39
+ # YES — element-based, resilient
40
+ auto.ButtonControl(Name="Find").Click()
41
+
42
+ # NO — coordinate-based, fragile
43
+ # pyautogui.click(517, 432)
44
+ ```
45
+
46
+ ## Retry Flaky Actions
47
+
48
+ ```python
49
+ for attempt in range(3):
50
+ try:
51
+ auto.ButtonControl(Name="Find").Click()
52
+ time.sleep(1)
53
+ if auto.ListControl(Name="Results").Exists(1): break
54
+ except Exception:
55
+ if attempt == 2: raise
56
+ time.sleep(2)
57
+ ```
58
+
59
+ ## Dismiss Popups (Check Before Major Actions)
60
+
61
+ ```python
62
+ popup = auto.WindowControl(searchDepth=1, RegexName=".*Error.*|.*Warning.*")
63
+ if popup.Exists(0.5):
64
+ close_btn = popup.ButtonControl(Name="Close")
65
+ ok_btn = popup.ButtonControl(Name="OK")
66
+ if close_btn.Exists(0.5):
67
+ close_btn.Click()
68
+ elif ok_btn.Exists(0.5):
69
+ ok_btn.Click()
70
+ ```
71
+
72
+ ## Output (Always Structured JSON)
73
+
74
+ ```python
75
+ print(json.dumps({"status": "success", "patient": name, "data": results}))
76
+ ```
77
+
78
+ ## Mixed Browser + Desktop
79
+
80
+ Some browser automations need the physical screen (OS alerts, file upload dialogs, native file picker, CAPTCHA). These **cannot use `/execute/browser`** — use `/execute` instead and treat as desktop automation.
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: rpa-testing-workflow
3
+ description: Mandatory testing workflow for RPA steps. Prescriptive tool call sequences for per-step testing through the Minicor executor, polling execution status, taking screenshots during runs, and end-to-end validation. Use EVERY TIME you build or modify RPA workflows.
4
+ category: general
5
+ tags: [testing, workflow, execution, validation, mandatory]
6
+ priority: 100
7
+ ---
8
+
9
+ # RPA Testing Workflow
10
+
11
+ This skill is MANDATORY. Every RPA step must be tested through the Minicor executor, not just `vm_execute_script`. Follow these exact sequences.
12
+
13
+ ## Why `vm_execute_script` Is Not Enough
14
+
15
+ `vm_execute_script` runs raw Python directly on the VM. It does NOT go through the Minicor workflow engine. This means:
16
+
17
+ - `{{config.username}}`, `{{config.password}}`, and all `{{config.*}}` variables are NOT resolved — they appear as literal text
18
+ - `data.input` interpolation does NOT work — the JS wrapper that injects input values is not executed
19
+ - `data.step_N.response` references to previous step outputs do NOT work
20
+ - The `lam.httpRequest` / `lam.rpa` dispatch wrapper is NOT executed
21
+ - Recording, execution tracking, and monitoring are NOT triggered
22
+
23
+ A script that passes via `vm_execute_script` can and WILL fail when run through `execute_workflow`. You MUST test through the real executor.
24
+
25
+ ## Per-Step Testing (After Each `create_rpa_flow`)
26
+
27
+ After saving a step with `create_rpa_flow`, IMMEDIATELY test it through Minicor:
28
+
29
+ ### Step 1: Run the step in isolation
30
+
31
+ ```
32
+ execute_workflow_async(
33
+ workflowId: <workflow_id>,
34
+ input: { ... real test data ... },
35
+ configurationId: <config_store_id>,
36
+ start_from_step: <this_step_order>,
37
+ end_at_step: <this_step_order>
38
+ )
39
+ ```
40
+
41
+ This runs ONLY this step through the real Minicor executor with config variable resolution and the JS wrapper.
42
+
43
+ ### Step 2: Poll execution status
44
+
45
+ ```
46
+ Loop every 5-10 seconds:
47
+ get_execution_status(workflowId, executionId)
48
+
49
+ While status is "RUNNING" or "PENDING":
50
+ - Take vm_screenshot to watch progress on the VM
51
+ - Continue polling
52
+
53
+ When status is "COMPLETED":
54
+ - Take vm_screenshot to verify final state
55
+ - Call get_full_execution to see step output and any recordings
56
+ - Proceed to next step
57
+
58
+ When status is "FAILED" or "ERROR":
59
+ - Take vm_screenshot to see the error state
60
+ - Call diagnose_execution for failure analysis
61
+ - Call get_full_execution for detailed error output
62
+ - Go to failure recovery (below)
63
+ ```
64
+
65
+ ### Step 3: Verify the result
66
+
67
+ After the step completes successfully:
68
+ - `vm_screenshot` to visually confirm the expected state
69
+ - Check the execution output via `get_full_execution` — verify the step produced the expected data
70
+ - If the step reads data, verify the output JSON is correct
71
+
72
+ ### Step 4: Only then proceed to the next step
73
+
74
+ Do NOT start writing the next step until the current one passes through the executor.
75
+
76
+ ## Failure Recovery
77
+
78
+ When a step fails through the Minicor executor:
79
+
80
+ 1. `diagnose_execution(workflowId, executionId)` — get failure analysis with hints
81
+ 2. `get_full_execution(workflowId, executionId)` — see stdout, stderr, exit code, recording
82
+ 3. `vm_screenshot` — see what state the VM is in
83
+ 4. Identify the cause:
84
+ - **Config variable not resolving?** Check the config store has the right properties
85
+ - **Data passing broken?** Check the JS wrapper interpolation — load `get_skill("data-passing-between-steps")`
86
+ - **Script error?** Fix the Python, but test the fix through the executor, NOT `vm_execute_script`
87
+ 5. `update_flow(flowId, program: <fixed_code>)` — update the step code
88
+ 6. Re-run `execute_workflow_async` with `start_from_step`/`end_at_step` again
89
+ 7. Repeat until the step passes through the executor
90
+
91
+ CRITICAL: Do NOT use `vm_execute_script` to "verify" a fix. The fix must pass through `execute_workflow_async`.
92
+
93
+ ## End-to-End Testing
94
+
95
+ After ALL steps pass individually through the executor:
96
+
97
+ ### Full workflow test
98
+
99
+ ```
100
+ execute_workflow_async(
101
+ workflowId: <workflow_id>,
102
+ input: { ... real production-like data ... },
103
+ configurationId: <config_store_id>
104
+ )
105
+ ```
106
+
107
+ No `start_from_step` / `end_at_step` — run the ENTIRE workflow.
108
+
109
+ ### Monitor the full run
110
+
111
+ ```
112
+ Loop every 5-10 seconds:
113
+ get_execution_status(workflowId, executionId)
114
+ vm_screenshot (every 2-3 polls to watch progress)
115
+
116
+ On completion:
117
+ get_full_execution to verify all steps produced correct output
118
+ vm_screenshot to verify final state
119
+
120
+ On failure:
121
+ diagnose_execution
122
+ Identify which step failed
123
+ Fix with update_flow
124
+ Re-run the FULL workflow (not just the failing step)
125
+ ```
126
+
127
+ ### Completion criteria
128
+
129
+ The workflow is NOT done until:
130
+ - `execute_workflow_async` returns with all steps in "COMPLETED" status
131
+ - The output data is correct (verify via `get_full_execution`)
132
+ - The VM is in the expected final state (verify via `vm_screenshot`)
133
+
134
+ For production workflows, also:
135
+ - Run the workflow a second time to verify idempotency
136
+ - Verify state verification passes (expectedPreState/expectedPostState)
137
+
138
+ ## Common Mistakes to Avoid
139
+
140
+ 1. **Testing with `vm_execute_script` and declaring done** — this is the #1 mistake. Always use `execute_workflow_async`.
141
+ 2. **Not using `configurationId`** — config variables only resolve when a config store is attached to the execution.
142
+ 3. **Fixing a script and testing with `vm_execute_script`** — even fixes must be verified through the executor.
143
+ 4. **Not polling `get_execution_status`** — the async execution runs in the background. You must poll to know when it finishes.
144
+ 5. **Skipping per-step testing** — testing only end-to-end makes it hard to isolate failures. Test each step individually first.
145
+ 6. **Not taking screenshots during execution** — screenshots while the workflow runs show you what's actually happening on the VM.
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: session-keepalive
3
+ description: Patterns for keeping RPA sessions alive using scheduled agents, health checks, and auto-recovery. Detecting session timeouts, re-authentication sequences, monitoring agent setup. Use when hardening production workflows that depend on persistent app sessions.
4
+ category: general
5
+ tags: [session, keepalive, monitoring, scheduled, recovery, production]
6
+ priority: 8
7
+ ---
8
+
9
+ # Session Keepalive
10
+
11
+ Production RPA workflows depend on an active, authenticated session. Sessions time out. This skill covers how to detect and recover from session loss automatically.
12
+
13
+ ## The Problem
14
+
15
+ - Web apps log you out after inactivity (15-60 minutes typically)
16
+ - Desktop apps lock or disconnect after RDP session timeout
17
+ - Citrix sessions disconnect after idle periods
18
+ - Data workflows fail silently when the session is gone
19
+
20
+ ## Solution: Scheduled Health Check Agent
21
+
22
+ ### Step 1: Build a Smart Launch workflow
23
+
24
+ Load `get_skill("smart-launch-patterns")` and build a Smart Launch workflow first. This is the recovery mechanism.
25
+
26
+ ### Step 2: Create a scheduled monitoring agent
27
+
28
+ ```
29
+ create_agent(
30
+ name: "session-keepalive",
31
+ task: "Run the Smart Launch workflow to verify the session is active. If it fails, take a screenshot, diagnose, and report.",
32
+ mode: "scheduled",
33
+ cronSchedule: "*/5 * * * *",
34
+ workspaceId: <id>,
35
+ vmUrl: "<tunnel_url>",
36
+ watchWorkflowId: <smart_launch_workflow_id>
37
+ )
38
+ ```
39
+
40
+ ### Step 3: Add failure monitoring
41
+
42
+ ```
43
+ create_agent(
44
+ name: "session-monitor",
45
+ task: "When the Smart Launch fails, take a screenshot, check what went wrong, re-run Smart Launch, and notify Slack.",
46
+ mode: "monitor",
47
+ watchWorkflowId: <smart_launch_workflow_id>,
48
+ recoveryTask: "Take a screenshot. If login page is showing, the session expired — re-run Smart Launch. If the app crashed, restart it. Report results to Slack.",
49
+ slackWebhookUrl: "<webhook>",
50
+ workspaceId: <id>,
51
+ vmUrl: "<tunnel_url>"
52
+ )
53
+ ```
54
+
55
+ ## Detecting Session Loss
56
+
57
+ ### Browser (CDP)
58
+
59
+ ```python
60
+ # Check if still on the app page vs redirected to login
61
+ current_url = cdp_eval(ws, 'window.location.href')
62
+ if '/login' in current_url or '/lock' in current_url:
63
+ print("Session expired — need to re-authenticate")
64
+ ```
65
+
66
+ ### Desktop (uiautomation)
67
+
68
+ ```python
69
+ # Check if the expected signed-in element still exists
70
+ window = auto.WindowControl(searchDepth=1, Name='MyApp')
71
+ if not window.Exists(3):
72
+ print("App window gone — need to relaunch")
73
+ else:
74
+ signed_in_indicator = window.HyperlinkControl(Name='Dashboard')
75
+ if not signed_in_indicator.Exists(3):
76
+ print("Signed out — need to re-authenticate")
77
+ ```
78
+
79
+ ## Recovery Sequence
80
+
81
+ 1. Take a screenshot to document the failure state
82
+ 2. Run the Smart Launch workflow
83
+ 3. Verify the session is restored (check for the signed-in indicator)
84
+ 4. If still failing, try a full restart (close app, relaunch, login)
85
+ 5. Report results via Slack webhook or issue update
86
+
87
+ ## RDP/Citrix Session Management
88
+
89
+ For desktop automations that require an active RDP session:
90
+ - The VM's MDS handles this — it runs as a scheduled task in the interactive session
91
+ - If the RDP session disconnects, MDS continues running but UI automation may fail
92
+ - The health check detects this (window controls become unresponsive)
93
+ - Recovery: The VM service can trigger a reconnect via `start_mds_on_vm`
94
+
95
+ ## Production Checklist
96
+
97
+ - [ ] Smart Launch workflow built and tested
98
+ - [ ] Scheduled keepalive agent created (5-minute interval)
99
+ - [ ] Monitor agent watching Smart Launch for failures
100
+ - [ ] Slack notifications configured for failure alerts
101
+ - [ ] Recovery task handles all known failure modes (timeout, crash, disconnect)