swipium 1.1.0 → 1.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 (75) hide show
  1. package/README.md +15 -9
  2. package/THREAT_MODEL.md +59 -0
  3. package/dist/appMap/diff.js +60 -0
  4. package/dist/appMap/diff.js.map +1 -0
  5. package/dist/automation/capabilities.js +167 -0
  6. package/dist/automation/capabilities.js.map +1 -0
  7. package/dist/automation/gestures.js +94 -0
  8. package/dist/automation/gestures.js.map +1 -0
  9. package/dist/automation/maestroIr.js +282 -0
  10. package/dist/automation/maestroIr.js.map +1 -0
  11. package/dist/automation/plan.js +268 -0
  12. package/dist/automation/plan.js.map +1 -0
  13. package/dist/automation/report.js +83 -0
  14. package/dist/automation/report.js.map +1 -0
  15. package/dist/automation/selectors.js +201 -0
  16. package/dist/automation/selectors.js.map +1 -0
  17. package/dist/automation/types.js +13 -0
  18. package/dist/automation/types.js.map +1 -0
  19. package/dist/automation/waits.js +91 -0
  20. package/dist/automation/waits.js.map +1 -0
  21. package/dist/automation/webview.js +81 -0
  22. package/dist/automation/webview.js.map +1 -0
  23. package/dist/flows/repair.js +363 -0
  24. package/dist/flows/repair.js.map +1 -0
  25. package/dist/interop/maestro.js +151 -0
  26. package/dist/interop/maestro.js.map +1 -0
  27. package/dist/mobileAudit/checks.js +251 -0
  28. package/dist/mobileAudit/checks.js.map +1 -0
  29. package/dist/mobileAudit/evidence.js +49 -0
  30. package/dist/mobileAudit/evidence.js.map +1 -0
  31. package/dist/mobileAudit/profiles.js +73 -0
  32. package/dist/mobileAudit/profiles.js.map +1 -0
  33. package/dist/mobileAudit/results.js +41 -0
  34. package/dist/mobileAudit/results.js.map +1 -0
  35. package/dist/mobileAudit/runner.js +229 -0
  36. package/dist/mobileAudit/runner.js.map +1 -0
  37. package/dist/oracle/locator.js +177 -0
  38. package/dist/oracle/locator.js.map +1 -0
  39. package/dist/server.js +20 -0
  40. package/dist/server.js.map +1 -1
  41. package/dist/testSuite/exporter.js +70 -0
  42. package/dist/testSuite/exporter.js.map +1 -0
  43. package/dist/testSuite/lint.js +53 -0
  44. package/dist/testSuite/lint.js.map +1 -0
  45. package/dist/tools/capabilities.js +46 -4
  46. package/dist/tools/capabilities.js.map +1 -1
  47. package/dist/tools/flow.js +67 -1
  48. package/dist/tools/flow.js.map +1 -1
  49. package/dist/tools/flowRepair.js +66 -0
  50. package/dist/tools/flowRepair.js.map +1 -0
  51. package/dist/tools/idling.js +104 -0
  52. package/dist/tools/idling.js.map +1 -0
  53. package/dist/tools/inputCapabilities.js +30 -0
  54. package/dist/tools/inputCapabilities.js.map +1 -0
  55. package/dist/tools/issues.js +218 -0
  56. package/dist/tools/issues.js.map +1 -0
  57. package/dist/tools/jobs.js +12 -1
  58. package/dist/tools/jobs.js.map +1 -1
  59. package/dist/tools/locator.js +57 -0
  60. package/dist/tools/locator.js.map +1 -0
  61. package/dist/tools/maestro.js +78 -0
  62. package/dist/tools/maestro.js.map +1 -0
  63. package/dist/tools/mobileAudit.js +115 -0
  64. package/dist/tools/mobileAudit.js.map +1 -0
  65. package/dist/tools/suite.js +49 -1
  66. package/dist/tools/suite.js.map +1 -1
  67. package/dist/tools/testSuite.js +233 -0
  68. package/dist/tools/testSuite.js.map +1 -0
  69. package/dist/tools/wait.js +58 -0
  70. package/dist/tools/wait.js.map +1 -0
  71. package/dist/version.js +31 -2
  72. package/dist/version.js.map +1 -1
  73. package/docs/mcp-server.md +1 -1
  74. package/docs/tools.md +46 -1
  75. package/package.json +3 -2
@@ -0,0 +1,83 @@
1
+ // Automation Kernel V2 — Workstream 9: Automation Readiness Output. Turns an AutomationPlan into one
2
+ // consistent response shape with natural-language messages a coding agent can repeat verbatim to the
3
+ // user. The canonical phrases below match the plan's required wording for each backend/fallback case.
4
+ import { isVisualStrategy } from './types.js';
5
+ // Canonical phrases (plan Workstream 9 — "Required phrases").
6
+ const PHRASE = {
7
+ iosRawSimulator: 'I can launch and inspect the app in the iOS simulator, but this backend cannot run structured taps. Attach WDA or Appium for selector-based automation.',
8
+ androidDirect: 'I can run this on Android using resource-id/accessibility/text selectors. Unicode text input needs a stronger backend or fixture adjustment.',
9
+ appiumRequired: 'This flow needs an Appium backend because it uses a WebView context or gesture that the current backend cannot perform.',
10
+ visualFallback: 'I can continue with visual/OCR evidence, but that is candidate-level evidence and not a structured locator proof.',
11
+ };
12
+ // Structured interaction/assertion kinds — the ones that produce structured-locator proof. Lifecycle
13
+ // and control steps (openUrl, pressKey, waitForIdle, lifecycle) do NOT make a flow "ready" on their own.
14
+ const STRUCTURED_PROOF_KINDS = new Set([
15
+ 'tap', 'longPress', 'doubleTap', 'inputText', 'clearText', 'scrollUntilVisible',
16
+ 'swipe', 'drag', 'pinch', 'waitForVisible', 'waitForNotVisible', 'assertVisible',
17
+ ]);
18
+ function providesStructuredProof(step) {
19
+ if (step.support !== 'native' && step.support !== 'supported_with_fallback')
20
+ return false;
21
+ if (!STRUCTURED_PROOF_KINDS.has(step.action.kind))
22
+ return false;
23
+ const sel = step.action.selector;
24
+ if (sel && (isVisualStrategy(sel.strategy) || sel.strategy === 'coordinate'))
25
+ return false;
26
+ return true;
27
+ }
28
+ function statusFor(plan) {
29
+ if (!plan.executable)
30
+ return 'blocked';
31
+ const hasStructured = plan.steps.some(providesStructuredProof);
32
+ const hasVisualOnly = plan.steps.some((s) => s.support === 'visual_only');
33
+ if (!hasStructured && hasVisualOnly)
34
+ return 'candidate';
35
+ return 'ready';
36
+ }
37
+ function fallbackUsed(plan) {
38
+ if (plan.backend === 'ios-raw-simulator' && statusFor(plan) !== 'ready')
39
+ return 'ios-raw-simulator';
40
+ if (plan.blockers.some((b) => /appium|webview|context/i.test(`${b.detail} ${b.nextStep}`)))
41
+ return 'appium-required';
42
+ if (plan.steps.some((s) => s.support === 'visual_only'))
43
+ return 'visual-only';
44
+ return 'none';
45
+ }
46
+ function headlineFor(status, backend) {
47
+ const label = status === 'ready' ? 'Ready' : status === 'candidate' ? 'Candidate-only' : 'Blocked';
48
+ return `${label} on ${backend}`;
49
+ }
50
+ /** Build the natural-language agent message, preferring the plan's required canonical phrasing. */
51
+ function agentMessageFor(plan, status) {
52
+ const fallback = fallbackUsed(plan);
53
+ const parts = [plan.agentMessage];
54
+ if (plan.backend === 'ios-raw-simulator' && status !== 'ready')
55
+ parts.push(PHRASE.iosRawSimulator);
56
+ if (plan.backend === 'android-direct')
57
+ parts.push(PHRASE.androidDirect);
58
+ if (fallback === 'appium-required')
59
+ parts.push(PHRASE.appiumRequired);
60
+ if (fallback === 'visual-only' || status === 'candidate')
61
+ parts.push(PHRASE.visualFallback);
62
+ // De-duplicate while preserving order.
63
+ return [...new Set(parts.filter(Boolean))].join(' ');
64
+ }
65
+ export function buildReadiness(plan) {
66
+ const status = statusFor(plan);
67
+ const supportedSteps = plan.steps.filter((s) => s.support !== 'unsupported').length;
68
+ const unsupportedSteps = plan.steps.filter((s) => s.support === 'unsupported').length;
69
+ return {
70
+ status,
71
+ headline: headlineFor(status, plan.backend),
72
+ agentMessage: agentMessageFor(plan, status),
73
+ developerMessage: plan.developerMessage,
74
+ backend: plan.backend,
75
+ supportedSteps,
76
+ unsupportedSteps,
77
+ blockers: plan.blockers,
78
+ warnings: plan.warnings,
79
+ fallbackUsed: fallbackUsed(plan),
80
+ };
81
+ }
82
+ export { PHRASE as READINESS_PHRASES };
83
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/automation/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,201 @@
1
+ // Automation Kernel V2 — Workstream 2: Selector IR V2. One normalized selector model shared by
2
+ // Flow V2, Maestro import/export, the Appium audit, and flow repair. Parses the existing Swipium
3
+ // selector string grammar, normalizes Android resource ids, scores risk/portability, preserves
4
+ // provenance, and refuses unsupported strategies per backend. NEVER generates XPath.
5
+ import { selectorSupported } from './capabilities.js';
6
+ import { isVisualStrategy } from './types.js';
7
+ const PLATFORM_FOR_STRATEGY = {
8
+ resource_id: 'android',
9
+ ios_predicate: 'ios',
10
+ ios_class_chain: 'ios',
11
+ };
12
+ const PORTABLE_STRATEGIES = ['accessibility_id', 'text', 'ocr_text', 'image'];
13
+ /** Normalize the many Android resource-id spellings to a bare id (last `/` segment). */
14
+ export function normalizeResourceId(value) {
15
+ const trimmed = value.trim();
16
+ const slash = trimmed.lastIndexOf('/');
17
+ return slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
18
+ }
19
+ function riskFor(strategy, value, hints) {
20
+ switch (strategy) {
21
+ case 'accessibility_id':
22
+ case 'resource_id':
23
+ return 'low';
24
+ case 'ios_predicate':
25
+ case 'ios_class_chain':
26
+ return 'medium';
27
+ case 'text': {
28
+ // Plain text is CI-brittle (copy/localization). Stable structural hints lower the risk.
29
+ const hasStableHint = !!(hints?.boundsBucket || hints?.screenSignature || hints?.className);
30
+ return hasStableHint ? 'medium' : 'high';
31
+ }
32
+ case 'ocr_text':
33
+ case 'image':
34
+ return 'high';
35
+ case 'coordinate':
36
+ return 'high';
37
+ default:
38
+ return 'high';
39
+ }
40
+ }
41
+ /**
42
+ * Parse one Swipium selector string into Selector IR. Recognizes the existing grammar:
43
+ * `id=foo`, `id/foo`, `com.example:id/foo`, `accessibility id=foo`, `name=foo`,
44
+ * `predicate string=...`, `class chain=...`, and bare text.
45
+ * OCR / image selectors come from dedicated flow steps, not this string grammar — see selectorForVisual().
46
+ */
47
+ export function parseSelector(raw, opts = {}) {
48
+ const source = opts.source ?? 'flow';
49
+ const hints = opts.hints;
50
+ const s = raw.trim();
51
+ const make = (strategy, value) => ({
52
+ strategy,
53
+ value,
54
+ platform: opts.platform ?? PLATFORM_FOR_STRATEGY[strategy] ?? 'cross_platform',
55
+ source,
56
+ hints,
57
+ risk: riskFor(strategy, value, hints),
58
+ portable: PORTABLE_STRATEGIES.includes(strategy),
59
+ });
60
+ let m;
61
+ if ((m = s.match(/^id\s*=\s*(.+)$/i)))
62
+ return make('resource_id', normalizeResourceId(m[1]));
63
+ if ((m = s.match(/^accessibility id\s*=\s*(.+)$/i)))
64
+ return make('accessibility_id', m[1].trim());
65
+ if ((m = s.match(/^name\s*=\s*(.+)$/i)))
66
+ return make('text', m[1].trim());
67
+ if ((m = s.match(/^predicate string\s*=\s*(.+)$/i)))
68
+ return make('ios_predicate', m[1].trim());
69
+ if ((m = s.match(/^class chain\s*=\s*(.+)$/i)))
70
+ return make('ios_class_chain', m[1].trim());
71
+ // `com.example:id/foo` or `id/foo` without an explicit `id=` prefix is still an Android resource id.
72
+ if (/^[\w.]*:id\//.test(s) || /^id\//.test(s))
73
+ return make('resource_id', normalizeResourceId(s));
74
+ return make('text', s);
75
+ }
76
+ /** Build a visual (OCR/image) Selector IR from a dedicated visual flow step. */
77
+ export function selectorForVisual(strategy, value, opts = {}) {
78
+ return {
79
+ strategy,
80
+ value,
81
+ platform: 'cross_platform',
82
+ source: opts.source ?? 'flow',
83
+ hints: opts.hints,
84
+ risk: 'high',
85
+ portable: true,
86
+ };
87
+ }
88
+ /** A coordinate selector (tapAt) — always last-resort, non-portable, high risk. */
89
+ export function selectorForCoordinate(value, source = 'flow') {
90
+ return { strategy: 'coordinate', value, platform: 'cross_platform', source, risk: 'high', portable: false };
91
+ }
92
+ /** Refuse selectors a backend cannot honor (e.g. an iOS predicate on Android DirectDriver). */
93
+ export function checkSelectorBackend(ir, caps) {
94
+ if (selectorSupported(caps, ir.strategy))
95
+ return { supported: true };
96
+ return {
97
+ supported: false,
98
+ code: 'BACKEND_UNSUPPORTED',
99
+ detail: `${ir.strategy} selector is not supported on ${caps.backend}.`,
100
+ nextStep: nextStepForUnsupportedSelector(ir.strategy, caps),
101
+ };
102
+ }
103
+ function nextStepForUnsupportedSelector(strategy, caps) {
104
+ if (strategy === 'ios_predicate' || strategy === 'ios_class_chain') {
105
+ return 'Use an iOS WDA or Appium XCUITest backend, or replace it with an accessibility id.';
106
+ }
107
+ if ((strategy === 'accessibility_id' || strategy === 'resource_id' || strategy === 'text') && !caps.structuredTree) {
108
+ return 'Attach WDA or Appium for structured selectors, or rewrite the step as a visual/OCR check.';
109
+ }
110
+ return `Attach a backend that supports ${strategy}.`;
111
+ }
112
+ /** CI-readiness check: plain text without stable hints is high risk and flagged. */
113
+ export function checkSelectorCiRisk(ir) {
114
+ if (ir.strategy === 'text' && ir.risk === 'high') {
115
+ return {
116
+ risk: 'high',
117
+ code: 'HIGH_RISK_SELECTOR',
118
+ detail: 'Plain-text selector with no stable hints is brittle under copy/localization changes.',
119
+ nextStep: 'Pair it with a stable accessibility id / resource id, or add structural hints (boundsBucket, screenSignature).',
120
+ };
121
+ }
122
+ return { risk: ir.risk };
123
+ }
124
+ /**
125
+ * Resolve a (resource_id | accessibility_id) selector against parsed candidates. Duplicate matches
126
+ * are AMBIGUOUS_SELECTOR unless the IR's hints (boundsBucket / screenSignature) disambiguate to one.
127
+ */
128
+ export function resolveSelector(ir, candidates) {
129
+ const key = (c) => ir.strategy === 'resource_id' ? (c.resourceId ? normalizeResourceId(c.resourceId) : undefined)
130
+ : ir.strategy === 'accessibility_id' ? c.accessibilityId
131
+ : ir.strategy === 'text' ? c.text
132
+ : undefined;
133
+ let matches = candidates.filter((c) => key(c) === ir.value);
134
+ if (matches.length === 0) {
135
+ return { ok: false, code: 'SELECTOR_NOT_FOUND', detail: `No element matches ${ir.strategy}=${ir.value}.`, matches: 0 };
136
+ }
137
+ if (matches.length === 1)
138
+ return { ok: true, match: matches[0] };
139
+ // Try to disambiguate by hints.
140
+ if (ir.hints?.boundsBucket) {
141
+ const narrowed = matches.filter((c) => c.boundsBucket === ir.hints.boundsBucket);
142
+ if (narrowed.length === 1)
143
+ return { ok: true, match: narrowed[0] };
144
+ if (narrowed.length)
145
+ matches = narrowed;
146
+ }
147
+ if (ir.hints?.screenSignature) {
148
+ const narrowed = matches.filter((c) => c.screenSignature === ir.hints.screenSignature);
149
+ if (narrowed.length === 1)
150
+ return { ok: true, match: narrowed[0] };
151
+ }
152
+ return {
153
+ ok: false,
154
+ code: 'AMBIGUOUS_SELECTOR',
155
+ detail: `${matches.length} elements match ${ir.strategy}=${ir.value}; add a disambiguating hint (boundsBucket/screenSignature) or a unique testID.`,
156
+ matches: matches.length,
157
+ };
158
+ }
159
+ /**
160
+ * Export a Selector IR to an Appium native locator strategy. NEVER returns XPath — visual/coordinate
161
+ * strategies have no Appium native locator and return null (the caller must mark them manual_review).
162
+ */
163
+ export function selectorToAppium(ir) {
164
+ switch (ir.strategy) {
165
+ case 'resource_id':
166
+ return { using: 'id', value: ir.value };
167
+ case 'accessibility_id':
168
+ return { using: 'accessibility id', value: ir.value };
169
+ case 'ios_predicate':
170
+ return { using: '-ios predicate string', value: ir.value };
171
+ case 'ios_class_chain':
172
+ return { using: '-ios class chain', value: ir.value };
173
+ case 'text':
174
+ // Prefer a UiAutomator native text matcher over XPath for Android.
175
+ return { using: '-android uiautomator', value: `new UiSelector().text("${ir.value.replace(/"/g, '\\"')}")` };
176
+ default:
177
+ return null;
178
+ }
179
+ }
180
+ /** Convert a Maestro selector block (string | { id|text|... }) to Selector IR without semantic loss. */
181
+ export function selectorFromMaestro(value, source = 'maestro_import') {
182
+ if (typeof value === 'string')
183
+ return parseSelector(value, { source });
184
+ if (!value || typeof value !== 'object')
185
+ return null;
186
+ const v = value;
187
+ if (typeof v.id === 'string')
188
+ return parseSelector(`id=${v.id}`, { source });
189
+ if (typeof v['accessibility id'] === 'string')
190
+ return parseSelector(`accessibility id=${v['accessibility id']}`, { source });
191
+ if (typeof v.label === 'string')
192
+ return parseSelector(`accessibility id=${v.label}`, { source });
193
+ if (typeof v.text === 'string')
194
+ return parseSelector(v.text, { source });
195
+ return null;
196
+ }
197
+ /** True when the strategy is a structured (non-visual) locator. */
198
+ export function isStructuredSelector(ir) {
199
+ return !isVisualStrategy(ir.strategy) && ir.strategy !== 'coordinate';
200
+ }
201
+ //# sourceMappingURL=selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../src/automation/selectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ // Automation Kernel V2 — shared types (Developer 3 lane). A backend-neutral automation model that
2
+ // lets a Flow V2 file be compiled into a deterministic, capability-checked plan *before* a device
3
+ // is touched. Everything here is pure data: no I/O, no driver calls. The runner, the planning
4
+ // tools, Maestro interop, and the Appium audit all share these types so "the same flow means the
5
+ // same thing" regardless of backend.
6
+ //
7
+ // See SWIPIUM-DEVELOPER-3-AUTOMATION-KERNEL-V2-PLAN-2026-05-29.md (Workstreams 1-5, 9).
8
+ /** Visual selector strategies never claim structured-locator proof; they are candidate evidence only. */
9
+ export const VISUAL_STRATEGIES = ['ocr_text', 'image'];
10
+ export function isVisualStrategy(strategy) {
11
+ return VISUAL_STRATEGIES.includes(strategy);
12
+ }
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/automation/types.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,kGAAkG;AAClG,8FAA8F;AAC9F,iGAAiG;AACjG,qCAAqC;AACrC,EAAE;AACF,wFAAwF;AA0CxF,yGAAyG;AACzG,MAAM,CAAC,MAAM,iBAAiB,GAAgC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAEpF,MAAM,UAAU,gBAAgB,CAAC,QAA0B;IACzD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,91 @@
1
+ // Automation Kernel V2 — Workstream 4: Wait and Stability Engine. Explicit wait contracts that
2
+ // replace scattered sleeps. Assertions act as efficient condition waits (Maestro parity); idle waits
3
+ // label their source as app_declared | backend | heuristic (Espresso parity); tap retry is explicit
4
+ // and recorded, never a hidden loop. All executors take an injectable clock so they are deterministic
5
+ // in tests and never block on real wall-clock during unit runs.
6
+ export const realClock = {
7
+ now: () => Date.now(),
8
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
9
+ };
10
+ /**
11
+ * Poll `condition` until it returns true or `timeoutMs` elapses. Exits early the instant the
12
+ * condition holds. Always evaluates the condition at least once.
13
+ */
14
+ export async function waitForCondition(condition, opts) {
15
+ const clock = opts.clock ?? realClock;
16
+ const interval = Math.max(1, opts.intervalMs ?? 250);
17
+ const start = clock.now();
18
+ let polls = 0;
19
+ // First, immediate check.
20
+ polls += 1;
21
+ if (await condition())
22
+ return { ok: true, elapsedMs: clock.now() - start, polls, timedOut: false };
23
+ while (clock.now() - start < opts.timeoutMs) {
24
+ await clock.sleep(interval);
25
+ polls += 1;
26
+ if (await condition())
27
+ return { ok: true, elapsedMs: clock.now() - start, polls, timedOut: false };
28
+ }
29
+ return { ok: false, elapsedMs: clock.now() - start, polls, timedOut: true };
30
+ }
31
+ /** Wait until an element is visible (assertion-as-wait). */
32
+ export async function waitForVisible(isVisible, opts) {
33
+ const r = await waitForCondition(isVisible, opts);
34
+ return { ...r, visible: r.ok };
35
+ }
36
+ /** Wait until an element is NOT visible — e.g. a spinner / modal disappears. */
37
+ export async function waitForNotVisible(isVisible, opts) {
38
+ const r = await waitForCondition(async () => !(await isVisible()), opts);
39
+ return { ...r, visible: !r.ok };
40
+ }
41
+ /**
42
+ * Wait for the UI to settle. The caller declares which idle source is in play:
43
+ * - app_declared: an Espresso/RN idling resource the app exposes (the only true proof)
44
+ * - backend: WDA/XCTest waitForIdle or driver-level idle
45
+ * - heuristic: best-effort settling (screenshot stability etc.) — labeled as NOT app proof
46
+ */
47
+ export async function waitForIdle(isIdle, opts) {
48
+ const r = await waitForCondition(isIdle, opts);
49
+ const appProof = opts.source === 'app_declared' && r.ok;
50
+ const detail = opts.source === 'app_declared'
51
+ ? r.ok ? 'app-declared idling resource reported idle' : 'app-declared idling resource did not settle within timeout'
52
+ : opts.source === 'backend'
53
+ ? r.ok ? 'backend (WDA/XCTest) idle reported' : 'backend idle did not settle within timeout'
54
+ : r.ok ? 'heuristic settling reached (not app-declared proof)' : 'heuristic settling did not stabilize within timeout';
55
+ return { ok: r.ok, source: opts.source, elapsedMs: r.elapsedMs, polls: r.polls, timedOut: r.timedOut, appProof, detail };
56
+ }
57
+ /** waitForAnimationToEnd — on timeout, pass with a warning rather than fail. */
58
+ export async function waitForAnimationToEnd(isAnimating, opts) {
59
+ const r = await waitForCondition(async () => !(await isAnimating()), opts);
60
+ if (r.ok)
61
+ return { ok: true, timedOut: false, elapsedMs: r.elapsedMs };
62
+ return {
63
+ ok: true,
64
+ timedOut: true,
65
+ elapsedMs: r.elapsedMs,
66
+ warning: `animation did not end within ${opts.timeoutMs}ms; continuing (pass-with-warning).`,
67
+ };
68
+ }
69
+ /**
70
+ * Tap with explicit `retryTapIfNoChange` semantics. Taps once; if the UI did not change AND retry is
71
+ * enabled, taps exactly one more time. Every attempt (with before/after signatures) is recorded so
72
+ * the retry is visible in the plan and evidence — never a hidden loop.
73
+ */
74
+ export async function retryTapIfNoChange(doTap, screenSignature, opts) {
75
+ const attempts = [];
76
+ const before1 = await screenSignature();
77
+ await doTap();
78
+ const after1 = await screenSignature();
79
+ const changed1 = before1 !== after1;
80
+ attempts.push({ index: 0, changed: changed1, signatureBefore: before1, signatureAfter: after1 });
81
+ if (changed1 || !opts.enabled) {
82
+ return { ok: true, attempts, retried: false, changed: changed1 };
83
+ }
84
+ const before2 = after1;
85
+ await doTap();
86
+ const after2 = await screenSignature();
87
+ const changed2 = before2 !== after2;
88
+ attempts.push({ index: 1, changed: changed2, signatureBefore: before2, signatureAfter: after2 });
89
+ return { ok: true, attempts, retried: true, changed: changed2 };
90
+ }
91
+ //# sourceMappingURL=waits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waits.js","sourceRoot":"","sources":["../../src/automation/waits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ // Automation Kernel V2 — Workstream 7 (Stage 1): Hybrid / WebView awareness. Pure detection of
2
+ // probable WebView / canvas / map surfaces from UI-tree element classes, plus the diagnostics that
3
+ // tell a developer whether structured automation is possible or whether Appium context support (or a
4
+ // native accessibility id) is required. Stage 1 is detection + read-only Appium context inventory; it
5
+ // does NOT add arbitrary web JS execution.
6
+ // Class/role signals. Matching is case-insensitive substring so framework-prefixed classes still hit.
7
+ const WEBVIEW_SIGNALS = ['webview', 'wkwebview', 'uiwebview', 'org.chromium', 'xwalk', 'xcuielementtypewebview', 'rctwebview'];
8
+ const CANVAS_SIGNALS = ['surfaceview', 'textureview', 'glsurfaceview', 'flutterview', 'unityplayer', 'canvas'];
9
+ const MAP_SIGNALS = ['mapview', 'com.google.android.maps', 'mkmapview', 'gmsmapview'];
10
+ function classify(value) {
11
+ const v = value.toLowerCase();
12
+ if (WEBVIEW_SIGNALS.some((s) => v.includes(s)))
13
+ return 'webview';
14
+ if (MAP_SIGNALS.some((s) => v.includes(s)))
15
+ return 'map';
16
+ if (CANVAS_SIGNALS.some((s) => v.includes(s)))
17
+ return 'canvas';
18
+ return null;
19
+ }
20
+ /** Detect probable hybrid surfaces from a parsed UI tree and emit the right diagnostic. */
21
+ export function detectHybridSurface(elements, opts = {}) {
22
+ const surfaces = [];
23
+ for (const el of elements) {
24
+ for (const value of [el.className, el.role]) {
25
+ if (!value)
26
+ continue;
27
+ const kind = classify(value);
28
+ if (kind && !surfaces.some((s) => s.kind === kind && s.signal === value)) {
29
+ surfaces.push({ kind, signal: value });
30
+ }
31
+ }
32
+ }
33
+ // A high visual-fallback ratio with no structured matches is a soft hybrid/canvas signal.
34
+ const softHybrid = surfaces.length === 0 && (opts.visualFallbackRatio ?? 0) >= 0.5;
35
+ const hybrid = surfaces.length > 0 || softHybrid;
36
+ if (!hybrid) {
37
+ return { hybrid: false, surfaces, diagnostic: 'NATIVE', appiumRequired: false, agentMessage: 'This screen looks like a native UI tree; structured selectors should work.' };
38
+ }
39
+ const hasWebview = surfaces.some((s) => s.kind === 'webview') || softHybrid;
40
+ const nonNativeContexts = (opts.contexts ?? []).filter((c) => !/^native_app$/i.test(c));
41
+ const contextSwitchingPossible = !!opts.caps?.webviewContexts;
42
+ if (hasWebview && nonNativeContexts.length > 0) {
43
+ return {
44
+ hybrid: true,
45
+ surfaces,
46
+ diagnostic: 'WEBVIEW_CONTEXT_AVAILABLE',
47
+ appiumRequired: false,
48
+ agentMessage: `This screen is hybrid (WebView). Appium reports ${nonNativeContexts.length} web context(s) (${nonNativeContexts.join(', ')}); structural web automation is possible by switching context.`,
49
+ };
50
+ }
51
+ if (hasWebview && contextSwitchingPossible) {
52
+ return {
53
+ hybrid: true,
54
+ surfaces,
55
+ diagnostic: 'WEBVIEW_CONTEXT_MISSING',
56
+ appiumRequired: true,
57
+ agentMessage: 'This screen looks like a WebView. The backend can switch contexts, but none are currently reported; attach the WebView context or use a native accessibility id, otherwise only visual candidate evidence is possible.',
58
+ };
59
+ }
60
+ return {
61
+ hybrid: true,
62
+ surfaces,
63
+ diagnostic: 'WEBVIEW_VISUAL_ONLY',
64
+ appiumRequired: true,
65
+ agentMessage: 'This screen looks like a WebView. I need Appium context support or a native accessibility id to automate it structurally; otherwise I can only produce visual candidate evidence.',
66
+ };
67
+ }
68
+ /** Read-only Appium context inventory (Stage 2 deliverable — no arbitrary JS execution). */
69
+ export function summarizeAppiumContexts(contexts, current) {
70
+ const webviewContexts = contexts.filter((c) => !/^native_app$/i.test(c));
71
+ return { current: current ?? null, available: [...contexts], webviewContexts };
72
+ }
73
+ /** An actionable blocker for a structured step that targets a WebView-only surface. */
74
+ export function webviewSelectorBlocker(selector) {
75
+ return {
76
+ code: 'WEBVIEW_VISUAL_ONLY',
77
+ detail: `Selector ${selector.strategy}=${selector.value} targets a WebView surface that the current backend cannot reach structurally.`,
78
+ nextStep: 'This screen looks like a WebView. I need Appium context support or a native accessibility id to automate it structurally; otherwise I can only produce visual candidate evidence.',
79
+ };
80
+ }
81
+ //# sourceMappingURL=webview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webview.js","sourceRoot":"","sources":["../../src/automation/webview.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,2CAA2C;AAsB3C,sGAAsG;AACtG,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,wBAAwB,EAAE,YAAY,CAAC,CAAC;AAC/H,MAAM,cAAc,GAAG,CAAC,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;AAC/G,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,yBAAyB,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAOtF,SAAS,QAAQ,CAAC,KAAa;IAC7B,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjE,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC;AAWD,2FAA2F;AAC3F,MAAM,UAAU,mBAAmB,CAAC,QAAuB,EAAE,OAA4B,EAAE;IACzF,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,EAAE,CAAC;gBACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;IACD,0FAA0F;IAC1F,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC;IACnF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC;IAEjD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,4EAA4E,EAAE,CAAC;IAC9K,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,UAAU,CAAC;IAC5E,MAAM,iBAAiB,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,MAAM,wBAAwB,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;IAE9D,IAAI,UAAU,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,QAAQ;YACR,UAAU,EAAE,2BAA2B;YACvC,cAAc,EAAE,KAAK;YACrB,YAAY,EAAE,mDAAmD,iBAAiB,CAAC,MAAM,oBAAoB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,gEAAgE;SAC1M,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,IAAI,wBAAwB,EAAE,CAAC;QAC3C,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,QAAQ;YACR,UAAU,EAAE,yBAAyB;YACrC,cAAc,EAAE,IAAI;YACpB,YAAY,EAAE,wNAAwN;SACvO,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,QAAQ;QACR,UAAU,EAAE,qBAAqB;QACjC,cAAc,EAAE,IAAI;QACpB,YAAY,EAAE,mLAAmL;KAClM,CAAC;AACJ,CAAC;AAQD,4FAA4F;AAC5F,MAAM,UAAU,uBAAuB,CAAC,QAAkB,EAAE,OAAuB;IACjF,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;AACjF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,sBAAsB,CAAC,QAAoB;IACzD,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,MAAM,EAAE,YAAY,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,KAAK,gFAAgF;QACvI,QAAQ,EAAE,mLAAmL;KAC9L,CAAC;AACJ,CAAC"}