halo-agent 2.0.0 → 2.0.1

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 (2) hide show
  1. package/orchestrator.js +47 -25
  2. package/package.json +1 -1
package/orchestrator.js CHANGED
@@ -369,15 +369,46 @@ async function runJob(queueItem, chromeConn, config, reportStatus) {
369
369
  });
370
370
 
371
371
  if (visionResult.submitted) {
372
- // Vision already submitted we're done
372
+ // Vision THINKS it submitted, but we shouldn't trust that without
373
+ // verifying — vision can confuse "review page rendered" with
374
+ // "application accepted." Route through the same verify-submit
375
+ // gate everything else uses. Worst case → REVIEWING, user clicks
376
+ // Submit. Better than a false-positive DONE.
373
377
  const confirmShot = await page.screenshot({ type: 'jpeg', quality: 70 }).catch(() => null);
374
378
  const confirmKey = confirmShot ? await uploadScreenshot(config, confirmShot, `confirm_${queueId}.jpg`) : null;
375
- await reportStatus('DONE', {
376
- confirmation_screenshot_r2_key: confirmKey || null,
379
+ const verdictUrl = page.url();
380
+ let vVerdict = { submitted: null, error_message: null, source: 'unavailable' };
381
+ try {
382
+ const vRes = await fetch(`${config.apiUrl}/agent/verify-submit`, {
383
+ method: 'POST',
384
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.token}` },
385
+ body: JSON.stringify({ queue_id: queueId, page_url: verdictUrl }),
386
+ });
387
+ if (vRes.ok) vVerdict = await vRes.json();
388
+ } catch {}
389
+ if (vVerdict.submitted === true) {
390
+ await reportStatus('DONE', { confirmation_screenshot_r2_key: confirmKey || null, fields_filled: cumulativeFilled });
391
+ await clearCheckpoint(config, queueId);
392
+ console.log(`[orchestrator] Done via vision (verified): ${queueItem.company} - ${queueItem.title}`);
393
+ return;
394
+ }
395
+ if (vVerdict.submitted === false) {
396
+ await reportStatus('NEEDS_ATTENTION', {
397
+ review_screenshot_r2_key: confirmKey || null,
398
+ needs_attention_reason: `Vision submitted but ATS rejected: ${vVerdict.error_message || 'unknown'}`,
399
+ intervention_type: 'submit_failed',
400
+ step: 'VERIFY',
401
+ step_detail: (vVerdict.error_message || '').slice(0, 200),
402
+ fields_filled: cumulativeFilled,
403
+ });
404
+ throw new Error(`Vision-submit failed verification: ${vVerdict.error_message || 'unknown'}`);
405
+ }
406
+ await reportStatus('REVIEWING', {
407
+ review_screenshot_r2_key: confirmKey || null,
408
+ step: 'REVIEWING',
409
+ step_detail: 'Vision attempted submit — verifier unavailable, please eyeball',
377
410
  fields_filled: cumulativeFilled,
378
411
  });
379
- await clearCheckpoint(config, queueId);
380
- console.log(`[orchestrator] Done via vision: ${queueItem.company} - ${queueItem.title}`);
381
412
  return;
382
413
  }
383
414
 
@@ -474,31 +505,22 @@ async function runJob(queueItem, chromeConn, config, reportStatus) {
474
505
  }
475
506
 
476
507
  if (verdict.submitted === null) {
477
- // Auto-submit mode means "don't ask me, just submit." If we can't
478
- // verify but the user opted into hands-off, trust the click and
479
- // mark DONE (the screenshot is the receipt; user can audit later).
480
- // Without this, autoSubmit was silently being ignored every time
481
- // Firecrawl was slow/down — exactly the case user hit.
482
- const autoSubmit = config.autoSubmit || aep.agent_config?.auto_submit;
483
- if (autoSubmit) {
484
- console.log(`[orchestrator] Could not verify (source: ${verdict.source}) auto-submit ON, trusting click.`);
485
- await reportStatus('DONE', {
486
- confirmation_screenshot_r2_key: confirmKey || null,
487
- fields_filled: cumulativeFilled,
488
- });
489
- await clearCheckpoint(config, queueId);
490
- console.log(`[orchestrator] Done (auto-submit, unverified): ${queueItem.company} - ${queueItem.title}`);
491
- return;
492
- }
493
- console.warn(`[orchestrator] Could not verify submission (source: ${verdict.source}). Sending to REVIEWING for your eyeball.`);
508
+ // EARLIER VERSION: when auto-submit was ON, we trusted the click and
509
+ // marked DONE. That was wrong it produced false-positive submissions
510
+ // (applied=true in DB, no actual application sent). Auto-submit means
511
+ // "don't make me click Submit on the dashboard" — it does NOT mean
512
+ // "lie about delivery."
513
+ //
514
+ // Honest behavior: unverified == REVIEWING regardless of auto-submit.
515
+ // The screenshot is right there in the dashboard, one click confirms.
516
+ // Better to over-ask than to ghost-apply.
517
+ console.warn(`[orchestrator] Could not verify submission (source: ${verdict.source}). REVIEWING — please eyeball the screenshot + click Submit.`);
494
518
  await reportStatus('REVIEWING', {
495
519
  review_screenshot_r2_key: confirmKey || null,
496
520
  step: 'REVIEWING',
497
- step_detail: 'Could not auto-verify — please confirm the submit',
521
+ step_detail: `Submit clicked at ${verdictUrl.slice(0, 100)} verifier unavailable, please confirm`,
498
522
  fields_filled: cumulativeFilled,
499
523
  });
500
- // Stop here; user clicks Submit on dashboard → /apply-queue/submit/:id
501
- // will flip to DONE. Don't return — let the function return naturally.
502
524
  return;
503
525
  }
504
526
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halo-agent",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "HALO local apply agent — auto-fills job applications using your real Chrome session",
5
5
  "main": "index.js",
6
6
  "bin": {