halo-agent 2.0.5 → 2.0.6
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/detectFormErrors.js +30 -7
- package/package.json +1 -1
- package/smartFill.js +15 -0
package/detectFormErrors.js
CHANGED
|
@@ -143,14 +143,37 @@ async function detectFormErrors(page) {
|
|
|
143
143
|
if (type === 'file') return !el.files || el.files.length === 0;
|
|
144
144
|
// contenteditable: empty when innerText is whitespace.
|
|
145
145
|
if (el.isContentEditable) return !(el.innerText || '').trim();
|
|
146
|
-
// Combobox
|
|
147
|
-
//
|
|
148
|
-
|
|
146
|
+
// Combobox / custom-select: React-Select (which Greenhouse uses for
|
|
147
|
+
// ALL its dropdowns) renders the selected value as a
|
|
148
|
+
// .select__single-value or .select__multi-value chip INSIDE the
|
|
149
|
+
// control wrapper, NOT as the input's .value. We must look there or
|
|
150
|
+
// we falsely flag every filled dropdown as empty (the bug that made
|
|
151
|
+
// the retry re-pick Gender + Ethnicity).
|
|
152
|
+
if (el.getAttribute('role') === 'combobox' || tag === 'select') {
|
|
153
|
+
// Walk up to the React-Select control wrapper (a few class variants).
|
|
154
|
+
const wrap = el.closest(
|
|
155
|
+
'[class*="select__control"], [class*="select-control"], ' +
|
|
156
|
+
'[class*="select__value-container"], [class*="select"], ' +
|
|
157
|
+
'[class*="combobox"], [class*="Select"]'
|
|
158
|
+
) || el.parentElement;
|
|
159
|
+
if (wrap) {
|
|
160
|
+
// Any rendered value chip → NOT empty.
|
|
161
|
+
const valueEl = wrap.querySelector(
|
|
162
|
+
'[class*="single-value"], [class*="multi-value"], ' +
|
|
163
|
+
'[class*="multiValue"], [class*="singleValue"], ' +
|
|
164
|
+
'[class*="chip"], [class*="tag"], [class*="__value"]'
|
|
165
|
+
);
|
|
166
|
+
if (valueEl && (valueEl.innerText || valueEl.textContent || '').trim()) return false;
|
|
167
|
+
// Also: the control's own text, minus the placeholder. If the
|
|
168
|
+
// control shows "Male" it's filled; if it shows "Select..." it's empty.
|
|
169
|
+
const wrapText = (wrap.innerText || '').trim();
|
|
170
|
+
const placeholderEl = wrap.querySelector('[class*="placeholder"]');
|
|
171
|
+
const placeholderText = placeholderEl ? (placeholderEl.innerText || '').trim() : '';
|
|
172
|
+
if (wrapText && wrapText !== placeholderText && !/^select\b|^choose\b|^\.\.\./i.test(wrapText)) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
149
176
|
const v = (el.value || el.innerText || '').trim();
|
|
150
|
-
// Empty AND no chip/pill rendered as a sibling means truly empty.
|
|
151
|
-
const wrap = el.closest('[class*="select"], [class*="combobox"]');
|
|
152
|
-
const chip = wrap?.querySelector('[class*="chip"], [class*="multi-value"], [class*="tag"]');
|
|
153
|
-
if (chip && (chip.innerText || '').trim()) return false;
|
|
154
177
|
return !v;
|
|
155
178
|
}
|
|
156
179
|
// Plain inputs / textareas
|
package/package.json
CHANGED
package/smartFill.js
CHANGED
|
@@ -362,6 +362,21 @@ async function openAndPickOption(page, triggerLocator, value, llmCtx) {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
if (candidates.length === 0) {
|
|
365
|
+
// DIAGNOSTIC: dump the field's actual DOM so we can see what widget
|
|
366
|
+
// this really is (intl-tel? react-select? custom?). One-line summary
|
|
367
|
+
// of the element + its 2 nearest ancestors' classes + any sibling
|
|
368
|
+
// input/button. This is how we stop guessing selectors.
|
|
369
|
+
const diag = await triggerLocator.evaluate((el) => {
|
|
370
|
+
const summ = (n) => n ? `<${n.tagName.toLowerCase()} class="${(n.className || '').toString().slice(0, 120)}" role="${n.getAttribute?.('role') || ''}" aria-haspopup="${n.getAttribute?.('aria-haspopup') || ''}">` : 'null';
|
|
371
|
+
const p1 = el.parentElement;
|
|
372
|
+
const p2 = p1?.parentElement;
|
|
373
|
+
const siblingInputs = Array.from((p2 || p1 || el).querySelectorAll('input, button, [role="combobox"], [role="button"]'))
|
|
374
|
+
.slice(0, 5)
|
|
375
|
+
.map((s) => `${s.tagName.toLowerCase()}.${(s.className || '').toString().split(' ')[0]}`)
|
|
376
|
+
.join(', ');
|
|
377
|
+
return `self=${summ(el)} | p1=${summ(p1)} | p2=${summ(p2)} | nearby=[${siblingInputs}]`;
|
|
378
|
+
}).catch(() => 'diag failed');
|
|
379
|
+
console.warn(`[smartFill] DROPDOWN-DIAG: ${diag}`);
|
|
365
380
|
await page.keyboard.press('Escape').catch(() => {});
|
|
366
381
|
return { ok: false, reason: 'dropdown opened but no visible options found' };
|
|
367
382
|
}
|