halo-agent 2.4.0 → 2.4.2
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/orchestrator.js +33 -6
- package/package.json +1 -1
package/orchestrator.js
CHANGED
|
@@ -166,22 +166,49 @@ async function runJob(queueItem, chromeConn, config, reportStatus) {
|
|
|
166
166
|
// May return a new page object if Apply opened a new tab.
|
|
167
167
|
page = await clickApplyIfNeeded(page);
|
|
168
168
|
|
|
169
|
-
// STEP 1.5: Find the apply path if the page
|
|
169
|
+
// STEP 1.5: Find the apply path if the page has no actual FORM fields.
|
|
170
170
|
//
|
|
171
171
|
// clickApplyIfNeeded only handles obvious cases (a literal "Apply" button
|
|
172
172
|
// outside the form). It misses:
|
|
173
173
|
// - tabs (Overview / Application on Mercor's Ashby-wrapped careers page)
|
|
174
174
|
// - "Continue to application" CTAs below the fold
|
|
175
175
|
// - non-semantic styled divs that act as the entry point
|
|
176
|
+
// - marketing-shell sites (like Artie's careers page) where scanAccessibility
|
|
177
|
+
// returns 29 nav links + 2 decorative buttons but ZERO form inputs
|
|
178
|
+
//
|
|
179
|
+
// The earlier version checked `earlyScan.length === 0` which let pages
|
|
180
|
+
// like Artie through the AX scanner happily reported "29 fields" but
|
|
181
|
+
// every single one was a Pricing/Blog/Login navlink. Now we count only
|
|
182
|
+
// roles that are actually fillable: textbox, combobox, checkbox, radio,
|
|
183
|
+
// radiogroup, listbox, switch, file. Buttons and links don't count.
|
|
176
184
|
//
|
|
177
185
|
// findApplyPath checks the cache, then probes the AX tree for apply-keyword
|
|
178
|
-
// clickables, then falls back to vision. Bounded to 2 click hops.
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
// clickables, then falls back to vision. Bounded to 2 click hops.
|
|
187
|
+
const FILLABLE_ROLES = new Set([
|
|
188
|
+
'textbox', 'combobox', 'checkbox', 'radio', 'radiogroup',
|
|
189
|
+
'listbox', 'switch', 'spinbutton', 'searchbox', 'slider',
|
|
190
|
+
]);
|
|
191
|
+
function countFormFields(scan) {
|
|
192
|
+
if (!Array.isArray(scan)) return 0;
|
|
193
|
+
let n = 0;
|
|
194
|
+
for (const f of scan) {
|
|
195
|
+
const role = (f && (f.role || f.type)) || '';
|
|
196
|
+
const tag = (f && f.tag) || '';
|
|
197
|
+
const inputType = (f && f.inputType) || '';
|
|
198
|
+
if (FILLABLE_ROLES.has(role)) { n++; continue; }
|
|
199
|
+
if (tag === 'textarea' || tag === 'select') { n++; continue; }
|
|
200
|
+
if (tag === 'input' && inputType && inputType !== 'button' && inputType !== 'submit' && inputType !== 'hidden') { n++; continue; }
|
|
201
|
+
if (inputType === 'file' || role === 'file') { n++; continue; }
|
|
202
|
+
}
|
|
203
|
+
return n;
|
|
204
|
+
}
|
|
205
|
+
|
|
181
206
|
try {
|
|
182
207
|
const earlyScan = await scanAccessibility(page);
|
|
183
|
-
|
|
184
|
-
|
|
208
|
+
const formFieldCount = countFormFields(earlyScan);
|
|
209
|
+
const totalNodes = Array.isArray(earlyScan) ? earlyScan.length : 0;
|
|
210
|
+
if (formFieldCount === 0) {
|
|
211
|
+
console.log(`[orchestrator] No FORM fields detected (${totalNodes} AX nodes, 0 fillable) invoking findApplyPath`);
|
|
185
212
|
const result = await findApplyPath(page, { config, anthropicKey });
|
|
186
213
|
if (result.found) {
|
|
187
214
|
console.log(`[orchestrator] findApplyPath: ${result.method} succeeded (${result.fields_after} fields)`);
|