@reconcrap/boss-recommend-mcp 2.1.20 → 2.1.21

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.1.20",
3
+ "version": "2.1.21",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -462,6 +462,9 @@ export function createHumanRestController({
462
462
  shortRestProbability = 0.08,
463
463
  shortRestMinMs = 3000,
464
464
  shortRestMaxMs = 7000,
465
+ perCandidateRestEnabled = false,
466
+ perCandidateRestMinMs = 0,
467
+ perCandidateRestMaxMs = 0,
465
468
  batchThresholdBase = 25,
466
469
  batchThresholdJitter = 8,
467
470
  batchRestMinMs = 15000,
@@ -470,7 +473,10 @@ export function createHumanRestController({
470
473
  const nextRandom = normalizeRandom(random);
471
474
  const readNow = typeof nowFn === "function" ? nowFn : Date.now;
472
475
  const normalizedRestLevel = normalizeHumanRestLevel(restLevel);
473
- const budgetProfile = (shortRestEnabled !== false || batchRestEnabled !== false)
476
+ const perCandidateMinMs = Math.max(0, Number(perCandidateRestMinMs) || 0);
477
+ const perCandidateMaxMs = Math.max(perCandidateMinMs, Number(perCandidateRestMaxMs) || perCandidateMinMs);
478
+ const perCandidateEnabled = enabled === true && perCandidateRestEnabled === true && perCandidateMaxMs > 0;
479
+ const budgetProfile = !perCandidateEnabled && (shortRestEnabled !== false || batchRestEnabled !== false)
474
480
  ? HUMAN_REST_LEVEL_PROFILES[normalizedRestLevel] || null
475
481
  : null;
476
482
  const nextBudgetRestInterval = () => budgetProfile
@@ -479,6 +485,9 @@ export function createHumanRestController({
479
485
  const state = {
480
486
  enabled: enabled === true,
481
487
  rest_level: normalizedRestLevel,
488
+ per_candidate_rest_enabled: perCandidateEnabled,
489
+ per_candidate_rest_min_ms: perCandidateMinMs,
490
+ per_candidate_rest_max_ms: perCandidateMaxMs,
482
491
  short_rest_enabled: enabled === true && shortRestEnabled !== false,
483
492
  batch_rest_enabled: enabled === true && batchRestEnabled !== false,
484
493
  rest_counter: 0,
@@ -569,6 +578,40 @@ export function createHumanRestController({
569
578
  }
570
579
  const sleeper = typeof sleepFn === "function" ? sleepFn : sleep;
571
580
  updateActiveElapsed();
581
+ if (state.per_candidate_rest_enabled) {
582
+ state.rest_counter += 1;
583
+ state.processed_count += 1;
584
+ state.candidates_since_last_rest += 1;
585
+ const pauseMs = Math.round(randomBetween(
586
+ nextRandom,
587
+ state.per_candidate_rest_min_ms,
588
+ state.per_candidate_rest_max_ms
589
+ ));
590
+ await sleeper(pauseMs);
591
+ state.rest_count += 1;
592
+ state.total_rest_ms += pauseMs;
593
+ state.last_active_at_ms = Number(readNow()) || state.last_active_at_ms;
594
+ const event = {
595
+ kind: "per_candidate_rest",
596
+ rest_level: normalizedRestLevel,
597
+ pause_ms: pauseMs,
598
+ processed_since_last_rest: state.candidates_since_last_rest
599
+ };
600
+ state.candidates_since_last_rest = 0;
601
+ return {
602
+ enabled: true,
603
+ rested: true,
604
+ pause_ms: pauseMs,
605
+ rest_level: normalizedRestLevel,
606
+ rest_counter: state.rest_counter,
607
+ rest_threshold: state.rest_threshold,
608
+ processed_count: state.processed_count,
609
+ active_elapsed_ms: state.active_elapsed_ms,
610
+ rest_count: state.rest_count,
611
+ total_rest_ms: state.total_rest_ms,
612
+ events: [event]
613
+ };
614
+ }
572
615
  if (budgetProfile) {
573
616
  const budgetEvent = await takeBudgetBreakIfNeeded(sleeper);
574
617
  const pauseMs = budgetEvent?.pause_ms || 0;
@@ -88,11 +88,13 @@ import {
88
88
  makeForbiddenChatResumeNavigationError,
89
89
  recoverChatShell
90
90
  } from "./page-guard.js";
91
- import { getChatRoots } from "./roots.js";
92
-
93
- const DETAIL_SOURCES = new Set(["cascade", "network", "dom", "image"]);
94
-
95
- function normalizeDetailSource(value) {
91
+ import { getChatRoots } from "./roots.js";
92
+
93
+ const DETAIL_SOURCES = new Set(["cascade", "network", "dom", "image"]);
94
+ const CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS = 5000;
95
+ const CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS = 8000;
96
+
97
+ function normalizeDetailSource(value) {
96
98
  const normalized = String(value || "").trim().toLowerCase();
97
99
  return DETAIL_SOURCES.has(normalized) ? normalized : "cascade";
98
100
  }
@@ -939,13 +941,18 @@ export async function runChatWorkflow({
939
941
  humanBehavior = null
940
942
  } = {}, runControl) {
941
943
  if (!client) throw new Error("runChatWorkflow requires a guarded CDP client");
942
- const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
943
- legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
944
- });
945
- const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
946
- configureHumanInteraction(client, {
947
- enabled: effectiveHumanBehavior.enabled,
948
- clickMovementEnabled: effectiveHumanBehavior.clickMovement,
944
+ const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
945
+ legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
946
+ });
947
+ const normalizedDetailSource = normalizeDetailSource(detailSource);
948
+ const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
949
+ const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
950
+ const useLlmScreening = normalizedScreeningMode === "llm" && !collectCvOnly;
951
+ const collectCvPerCandidateRestEnabled = collectCvOnly && effectiveHumanBehavior.enabled;
952
+ const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled || collectCvPerCandidateRestEnabled;
953
+ configureHumanInteraction(client, {
954
+ enabled: effectiveHumanBehavior.enabled,
955
+ clickMovementEnabled: effectiveHumanBehavior.clickMovement,
949
956
  textEntryEnabled: effectiveHumanBehavior.textEntry,
950
957
  safeClickPointEnabled: effectiveHumanBehavior.clickMovement,
951
958
  actionCooldownEnabled: effectiveHumanBehavior.actionCooldown
@@ -954,13 +961,12 @@ export async function runChatWorkflow({
954
961
  enabled: effectiveHumanRestEnabled,
955
962
  shortRestEnabled: effectiveHumanBehavior.shortRest,
956
963
  batchRestEnabled: effectiveHumanBehavior.batchRest,
957
- restLevel: effectiveHumanBehavior.restLevel
964
+ restLevel: effectiveHumanBehavior.restLevel,
965
+ perCandidateRestEnabled: collectCvPerCandidateRestEnabled,
966
+ perCandidateRestMinMs: CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS,
967
+ perCandidateRestMaxMs: CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS
958
968
  });
959
- const normalizedDetailSource = normalizeDetailSource(detailSource);
960
- const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
961
- const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
962
- const useLlmScreening = normalizedScreeningMode === "llm" && !collectCvOnly;
963
- const processedLimit = Math.max(1, Number(maxCandidates) || 1);
969
+ const processedLimit = Math.max(1, Number(maxCandidates) || 1);
964
970
  const passTarget = Number.isFinite(Number(targetPassCount)) && Number(targetPassCount) > 0
965
971
  ? Number(targetPassCount)
966
972
  : null;
@@ -1177,11 +1183,14 @@ export async function runChatWorkflow({
1177
1183
  viewport_checks: viewportGuard.getStats().checks,
1178
1184
  viewport_recoveries: viewportGuard.getStats().recoveries,
1179
1185
  human_behavior_enabled: effectiveHumanBehavior.enabled,
1180
- human_behavior_profile: effectiveHumanBehavior.profile,
1181
- human_rest_level: effectiveHumanBehavior.restLevel,
1182
- human_rest_enabled: effectiveHumanRestEnabled,
1183
- human_rest_count: humanRestController.getState().rest_count,
1184
- human_rest_ms: humanRestController.getState().total_rest_ms,
1186
+ human_behavior_profile: effectiveHumanBehavior.profile,
1187
+ human_rest_level: effectiveHumanBehavior.restLevel,
1188
+ human_rest_enabled: effectiveHumanRestEnabled,
1189
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
1190
+ human_rest_per_candidate_min_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS : null,
1191
+ human_rest_per_candidate_max_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS : null,
1192
+ human_rest_count: humanRestController.getState().rest_count,
1193
+ human_rest_ms: humanRestController.getState().total_rest_ms,
1185
1194
  last_human_event: lastHumanEvent
1186
1195
  });
1187
1196
  runControl.setPhase("chat:done");
@@ -1239,10 +1248,20 @@ export async function runChatWorkflow({
1239
1248
  screening_mode: normalizedScreeningMode,
1240
1249
  unique_seen: compactInfiniteListState(listState).seen_count,
1241
1250
  scroll_count: 0,
1242
- context_recoveries: contextRecoveryAttempts,
1243
- viewport_checks: viewportGuard.getStats().checks,
1244
- viewport_recoveries: viewportGuard.getStats().recoveries
1245
- });
1251
+ context_recoveries: contextRecoveryAttempts,
1252
+ viewport_checks: viewportGuard.getStats().checks,
1253
+ viewport_recoveries: viewportGuard.getStats().recoveries,
1254
+ human_behavior_enabled: effectiveHumanBehavior.enabled,
1255
+ human_behavior_profile: effectiveHumanBehavior.profile,
1256
+ human_rest_level: effectiveHumanBehavior.restLevel,
1257
+ human_rest_enabled: effectiveHumanRestEnabled,
1258
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
1259
+ human_rest_per_candidate_min_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS : null,
1260
+ human_rest_per_candidate_max_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS : null,
1261
+ human_rest_count: humanRestController.getState().rest_count,
1262
+ human_rest_ms: humanRestController.getState().total_rest_ms,
1263
+ last_human_event: lastHumanEvent
1264
+ });
1246
1265
 
1247
1266
  while (
1248
1267
  results.length < processedLimit
@@ -2027,11 +2046,14 @@ export async function runChatWorkflow({
2027
2046
  viewport_checks: viewportGuard.getStats().checks,
2028
2047
  viewport_recoveries: viewportGuard.getStats().recoveries,
2029
2048
  human_behavior_enabled: effectiveHumanBehavior.enabled,
2030
- human_behavior_profile: effectiveHumanBehavior.profile,
2031
- human_rest_level: effectiveHumanBehavior.restLevel,
2032
- human_rest_enabled: effectiveHumanRestEnabled,
2033
- human_rest_count: humanRestController.getState().rest_count,
2034
- human_rest_ms: humanRestController.getState().total_rest_ms,
2049
+ human_behavior_profile: effectiveHumanBehavior.profile,
2050
+ human_rest_level: effectiveHumanBehavior.restLevel,
2051
+ human_rest_enabled: effectiveHumanRestEnabled,
2052
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
2053
+ human_rest_per_candidate_min_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS : null,
2054
+ human_rest_per_candidate_max_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS : null,
2055
+ human_rest_count: humanRestController.getState().rest_count,
2056
+ human_rest_ms: humanRestController.getState().total_rest_ms,
2035
2057
  last_human_event: lastHumanEvent,
2036
2058
  last_candidate_id: screeningCandidate.id || null,
2037
2059
  last_candidate_key: candidateKey,
@@ -2071,10 +2093,11 @@ export async function runChatWorkflow({
2071
2093
  addTiming(compactResult.timings, "human_rest_ms", restElapsed);
2072
2094
  compactResult.timings.total_ms = Date.now() - candidateStarted;
2073
2095
  runControl.updateProgress({
2074
- human_rest_enabled: effectiveHumanRestEnabled,
2075
- human_rest_level: effectiveHumanBehavior.restLevel,
2076
- human_rest_count: humanRestController.getState().rest_count,
2077
- human_rest_ms: humanRestController.getState().total_rest_ms,
2096
+ human_rest_enabled: effectiveHumanRestEnabled,
2097
+ human_rest_level: effectiveHumanBehavior.restLevel,
2098
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
2099
+ human_rest_count: humanRestController.getState().rest_count,
2100
+ human_rest_ms: humanRestController.getState().total_rest_ms,
2078
2101
  human_rest_last: restResult,
2079
2102
  context_recoveries: contextRecoveryAttempts,
2080
2103
  last_human_event: lastHumanEvent
@@ -2175,12 +2198,14 @@ export function createChatRunService({
2175
2198
  if (!client) throw new Error("startChatRun requires a guarded CDP client");
2176
2199
  const normalizedDetailSource = normalizeDetailSource(detailSource);
2177
2200
  const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
2178
- const processedLimit = Math.max(1, Number(maxCandidates) || 1);
2179
- const normalizedDetailLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
2180
- const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
2181
- legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
2182
- });
2183
- const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
2201
+ const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
2202
+ const processedLimit = Math.max(1, Number(maxCandidates) || 1);
2203
+ const normalizedDetailLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
2204
+ const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
2205
+ legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
2206
+ });
2207
+ const collectCvPerCandidateRestEnabled = collectCvOnly && effectiveHumanBehavior.enabled;
2208
+ const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled || collectCvPerCandidateRestEnabled;
2184
2209
  return manager.startRun({
2185
2210
  runId,
2186
2211
  name,
@@ -2206,10 +2231,10 @@ export function createChatRunService({
2206
2231
  llm_configured: Boolean(llmConfig),
2207
2232
  llm_timeout_ms: llmTimeoutMs,
2208
2233
  llm_image_limit: llmImageLimit,
2209
- llm_image_detail: llmImageDetail,
2210
- max_image_pages: maxImagePages,
2211
- image_wheel_delta_y: imageWheelDeltaY,
2212
- list_max_scrolls: listMaxScrolls,
2234
+ llm_image_detail: llmImageDetail,
2235
+ max_image_pages: maxImagePages,
2236
+ image_wheel_delta_y: imageWheelDeltaY,
2237
+ list_max_scrolls: listMaxScrolls,
2213
2238
  list_stable_signature_limit: listStableSignatureLimit,
2214
2239
  list_wheel_delta_y: listWheelDeltaY,
2215
2240
  list_settle_ms: listSettleMs,
@@ -2221,7 +2246,9 @@ export function createChatRunService({
2221
2246
  human_behavior: effectiveHumanBehavior,
2222
2247
  human_rest_level: effectiveHumanBehavior.restLevel,
2223
2248
  human_rest_enabled: effectiveHumanRestEnabled,
2224
- cv_collection_mode: normalizedScreeningMode === "collect_cv"
2249
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
2250
+ human_rest_per_candidate_min_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS : null,
2251
+ human_rest_per_candidate_max_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS : null
2225
2252
  },
2226
2253
  progress: {
2227
2254
  card_count: 0,
@@ -2239,10 +2266,13 @@ export function createChatRunService({
2239
2266
  request_skipped: 0,
2240
2267
  context_recoveries: 0,
2241
2268
  human_behavior_enabled: effectiveHumanBehavior.enabled,
2242
- human_behavior_profile: effectiveHumanBehavior.profile,
2243
- human_rest_level: effectiveHumanBehavior.restLevel,
2244
- human_rest_enabled: effectiveHumanRestEnabled,
2245
- human_rest_count: 0,
2269
+ human_behavior_profile: effectiveHumanBehavior.profile,
2270
+ human_rest_level: effectiveHumanBehavior.restLevel,
2271
+ human_rest_enabled: effectiveHumanRestEnabled,
2272
+ human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
2273
+ human_rest_per_candidate_min_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MIN_MS : null,
2274
+ human_rest_per_candidate_max_ms: collectCvPerCandidateRestEnabled ? CHAT_COLLECT_CV_PER_CANDIDATE_REST_MAX_MS : null,
2275
+ human_rest_count: 0,
2246
2276
  human_rest_ms: 0,
2247
2277
  last_human_event: null
2248
2278
  },