@reconcrap/boss-recommend-mcp 2.0.19 → 2.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.19",
3
+ "version": "2.0.20",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -150,6 +150,14 @@ export const CHAT_RESUME_MODAL_SELECTORS = Object.freeze([
150
150
  ".resume-recommend"
151
151
  ]);
152
152
 
153
+ export const CHAT_RESUME_FAST_MODAL_SELECTORS = Object.freeze([
154
+ ".boss-popup__wrapper.new-chat-resume-dialog-main-ui",
155
+ ".new-chat-resume-dialog-main-ui",
156
+ ".resume-common-dialog.search-resume",
157
+ ".resume-recommend",
158
+ 'iframe[src*="/web/frame/c-resume/"]'
159
+ ]);
160
+
153
161
  export const CHAT_RESUME_CONTENT_SELECTORS = Object.freeze([
154
162
  ".resume-center-side .resume-detail-wrap",
155
163
  ".resume-detail-wrap",
@@ -171,14 +179,18 @@ export const CHAT_RESUME_IFRAME_SELECTORS = Object.freeze([
171
179
  export const CHAT_RESUME_CLOSE_SELECTORS = Object.freeze([
172
180
  ".boss-popup__close",
173
181
  ".boss-dialog__close",
182
+ ".new-chat-resume-dialog-main-ui .boss-popup__close",
183
+ ".new-chat-resume-dialog-main-ui .icon-close",
184
+ ".new-chat-resume-dialog-main-ui [class*=\"close\"]",
185
+ ".boss-popup__wrapper [class*=\"close\"]",
186
+ ".boss-dialog [class*=\"close\"]",
174
187
  ".popup-close",
175
188
  ".modal-close",
176
189
  ".dialog-close",
177
190
  ".close-btn",
178
191
  ".icon-close",
179
192
  '[aria-label*="关闭"]',
180
- '[title*="关闭"]',
181
- '[class*="close"]'
193
+ '[title*="关闭"]'
182
194
  ]);
183
195
 
184
196
  export const CHAT_PROFILE_NETWORK_PATTERNS = Object.freeze([
@@ -28,6 +28,7 @@ import {
28
28
  CHAT_PROFILE_NETWORK_PATTERNS,
29
29
  CHAT_RESUME_CLOSE_SELECTORS,
30
30
  CHAT_RESUME_CONTENT_SELECTORS,
31
+ CHAT_RESUME_FAST_MODAL_SELECTORS,
31
32
  CHAT_RESUME_IFRAME_SELECTORS,
32
33
  CHAT_RESUME_MODAL_SELECTORS,
33
34
  CHAT_SEND_BUTTON_SELECTORS
@@ -573,6 +574,43 @@ export async function waitForChatResumeModal(client, {
573
574
  return lastState;
574
575
  }
575
576
 
577
+ export async function quickChatResumeModalOpenProbe(client, {
578
+ selectors = CHAT_RESUME_FAST_MODAL_SELECTORS
579
+ } = {}) {
580
+ const rootState = await getChatRoots(client);
581
+ for (const root of rootState.roots) {
582
+ if (!root?.nodeId) continue;
583
+ for (const selector of selectors) {
584
+ let nodeIds = [];
585
+ try {
586
+ nodeIds = await querySelectorAll(client, root.nodeId, selector);
587
+ } catch {
588
+ nodeIds = [];
589
+ }
590
+ for (const nodeId of nodeIds.slice(0, 4)) {
591
+ try {
592
+ const box = await getNodeBox(client, nodeId);
593
+ if (box?.rect?.width > 8 && box?.rect?.height > 8) {
594
+ return {
595
+ open: true,
596
+ root: root.name,
597
+ selector,
598
+ node_id: nodeId,
599
+ rect: box.rect,
600
+ center: box.center
601
+ };
602
+ }
603
+ } catch {
604
+ // Hidden or stale modal probes are ignored.
605
+ }
606
+ }
607
+ }
608
+ }
609
+ return {
610
+ open: false
611
+ };
612
+ }
613
+
576
614
  export async function readChatResumeHtml(client, resumeState) {
577
615
  let popupHTML = "";
578
616
  let contentHTML = "";
@@ -52,6 +52,7 @@ import {
52
52
  extractChatProfileCandidate,
53
53
  isUnsafeChatOnlineResumeLinkError,
54
54
  openChatOnlineResume,
55
+ quickChatResumeModalOpenProbe,
55
56
  readChatConversationReadyState,
56
57
  requestChatResumeForPassedCandidate,
57
58
  selectChatMessageFilter,
@@ -158,6 +159,36 @@ export function chatDetailSkipReasonFromReadyState(state = {}) {
158
159
  return "";
159
160
  }
160
161
 
162
+ export function makeChatResumeModalOpenBeforeCandidateClickError(closeResult = null) {
163
+ const error = new Error("CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK");
164
+ error.code = "CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK";
165
+ error.close_result = closeResult || null;
166
+ return error;
167
+ }
168
+
169
+ export async function ensureNoOpenChatResumeModalBeforeCandidateClick(client, {
170
+ closeAttempts = 3
171
+ } = {}) {
172
+ const probe = await quickChatResumeModalOpenProbe(client);
173
+ if (!probe.open) {
174
+ return {
175
+ closed: true,
176
+ already_closed: true,
177
+ probe
178
+ };
179
+ }
180
+ const closeResult = await closeChatResumeModal(client, { attemptsLimit: closeAttempts });
181
+ if (closeResult?.closed) {
182
+ return {
183
+ closed: true,
184
+ already_closed: false,
185
+ probe,
186
+ close_result: closeResult
187
+ };
188
+ }
189
+ throw makeChatResumeModalOpenBeforeCandidateClickError(closeResult);
190
+ }
191
+
161
192
  function llmToScreening(llmResult, candidate) {
162
193
  return {
163
194
  status: llmResult?.passed ? "pass" : "fail",
@@ -458,6 +489,7 @@ async function selectFreshChatCandidate(client, {
458
489
  } = {}) {
459
490
  let lastError = null;
460
491
  for (let attempt = 0; attempt < 3; attempt += 1) {
492
+ const modalGuard = await ensureNoOpenChatResumeModalBeforeCandidateClick(client);
461
493
  const rootState = await getChatRoots(client);
462
494
  const freshNodeId = await resolveFreshChatCardNodeId(client, {
463
495
  fallbackNodeId: cardNodeId,
@@ -479,6 +511,7 @@ async function selectFreshChatCandidate(client, {
479
511
  ready,
480
512
  card_node_id: freshNodeId,
481
513
  refreshed_node: freshNodeId !== cardNodeId,
514
+ modal_guard: modalGuard,
482
515
  attempt: attempt + 1
483
516
  };
484
517
  } catch (error) {
@@ -1344,6 +1377,9 @@ export async function runChatWorkflow({
1344
1377
  if (closeResume) {
1345
1378
  detailStep = "close_resume_modal";
1346
1379
  closeResult = await measureTiming(timings, "close_detail_ms", () => closeChatResumeModal(client));
1380
+ if (!closeResult?.closed) {
1381
+ throw makeChatResumeModalOpenBeforeCandidateClickError(closeResult);
1382
+ }
1347
1383
  }
1348
1384
  detailResult.close_result = closeResult;
1349
1385
  detailResult.image_evidence = imageEvidence;