@reconcrap/boss-recommend-mcp 2.0.43 → 2.0.45

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/README.md CHANGED
@@ -259,7 +259,13 @@ config/screening-config.example.json
259
259
  - `debugPort`:未显式传 `port` 时,recommend / search / chat CDP-only MCP run 和健康检查默认连接这个 Chrome 调试端口。
260
260
  - `outputDir`:recommend / search / chat 完成后的最终 CSV 与 report JSON 会写入这里;run state / checkpoint 仍保留在各自状态目录,方便 pause/resume/cancel。
261
261
  - `llmThinkingLevel`:默认 `low`。可设为 `off/minimal/low/medium/high/auto/current`,用于控制 OpenAI-compatible LLM 的 thinking/reasoning 强度。
262
- - `humanRestEnabled`:默认 `false`。当前 CDP-only recommend / search / chat run 尚未实现随机休息层,因此会读取并保留该字段但不改变节奏;如后续重新加入 human rest,应以此字段为默认值。
262
+ - `humanBehavior`:默认 `{ "enabled": true, "profile": "paced_with_rests" }`。用于 recommend / search / chat 的可靠性实验,支持:
263
+ - `profile: "baseline"`:关闭人类节奏,保持确定性行为。
264
+ - `profile: "paced"`:启用 CDP-only Bezier 鼠标移动、较大按钮的安全 inset 点击点、分块 `Input.insertText`、列表 wheel/settle jitter,以及小的动作前后读秒。
265
+ - `profile: "paced_with_rests"`:在 `paced` 基础上启用候选人短休和批次休息。
266
+ - `humanRestEnabled`:兼容旧配置。设为 `true` 时等价于 `humanBehavior.profile="paced_with_rests"`;设为 `false` 时不会关闭当前默认节奏。如需关闭,请显式设置 `humanBehavior.enabled=false` 或 `humanBehavior.profile="baseline"`。
267
+ - recommend / search / chat 图片简历 fallback 与主列表滚动都会在启用 `listScrollJitter` 时使用 coverage-safe scroll jitter:每次 wheel delta 在安全范围内变化,并保留截图重叠、重复检测、bottom-marker / stop-boundary 逻辑,实际 delta 和 settle 时间会写入 artifact metadata。
268
+ - chat/recommend/search run 也兼容显式参数 `safe_pacing` 与 `batch_rest_enabled`:run 参数优先于配置文件。
263
269
 
264
270
  ## 常用命令
265
271
 
@@ -12,7 +12,17 @@
12
12
  "llmImageDetail": "low",
13
13
  "debugPort": 9222,
14
14
  "outputDir": "",
15
- "humanRestEnabled": false,
15
+ "humanRestEnabled": true,
16
+ "humanBehavior": {
17
+ "enabled": true,
18
+ "profile": "paced_with_rests",
19
+ "clickMovement": true,
20
+ "textEntry": true,
21
+ "listScrollJitter": true,
22
+ "shortRest": true,
23
+ "batchRest": true,
24
+ "actionCooldown": true
25
+ },
16
26
  "openaiOrganization": "optional-org-id",
17
27
  "openaiProject": "optional-project-id"
18
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.43",
3
+ "version": "2.0.45",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -83,7 +83,6 @@
83
83
  "config/screening-config.example.json",
84
84
  "skills",
85
85
  "scripts/postinstall.cjs",
86
- "scripts/live-recommend-recovery-smoke.js",
87
86
  "src/core",
88
87
  "src/domains",
89
88
  "src/chat-mcp.js",
package/src/chat-mcp.js CHANGED
@@ -48,6 +48,7 @@ import {
48
48
  normalizeTargetCountInput,
49
49
  resolveBossConfiguredOutputDir,
50
50
  resolveBossChatRuntimeLayout,
51
+ resolveHumanBehaviorForRun,
51
52
  resolveBossScreeningConfig
52
53
  } from "./chat-runtime-config.js";
53
54
  import { DEFAULT_MAX_IMAGE_PAGES } from "./core/cv-acquisition/index.js";
@@ -1078,6 +1079,7 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "", configRe
1078
1079
  const shouldRequestResume = shouldRequestChatResume(args);
1079
1080
  const useLlm = shouldUseChatLlm(args);
1080
1081
  const resolvedConfig = configResolution || (useLlm ? resolveBossScreeningConfig(workspaceRoot) : { ok: false });
1082
+ const humanBehavior = resolveHumanBehaviorForRun(args, resolvedConfig?.config || {});
1081
1083
  return {
1082
1084
  client: session.client,
1083
1085
  targetUrl: CHAT_TARGET_URL,
@@ -1124,6 +1126,8 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "", configRe
1124
1126
  listSettleMs: parsePositiveInteger(args.list_settle_ms, slowLive ? 1800 : 1200),
1125
1127
  listFallbackPoint: null,
1126
1128
  imageOutputDir: resolveBossConfiguredOutputDir("", getChatRunsDir()),
1129
+ humanRestEnabled: humanBehavior.restEnabled,
1130
+ humanBehavior,
1127
1131
  name: "mcp-boss-chat-run"
1128
1132
  };
1129
1133
  }
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
+ import { normalizeHumanBehaviorOptions } from "./core/browser/index.js";
5
6
 
6
7
  const BOSS_CHAT_RUNTIME_SUBDIR = "boss-chat";
7
8
  const TARGET_COUNT_WRAPPER_KEYS = ["target_count", "targetCount", "value", "count", "limit"];
@@ -238,6 +239,84 @@ function parseConfigBoolean(raw, fallback = false) {
238
239
  return fallback;
239
240
  }
240
241
 
242
+ function readFirstOwn(source, keys = []) {
243
+ if (!source || typeof source !== "object") return undefined;
244
+ for (const key of keys) {
245
+ if (Object.prototype.hasOwnProperty.call(source, key)) return source[key];
246
+ }
247
+ return undefined;
248
+ }
249
+
250
+ function parseOptionalConfigBoolean(raw) {
251
+ if (raw === undefined || raw === null || raw === "") return null;
252
+ return parseConfigBoolean(raw, null);
253
+ }
254
+
255
+ function applyHumanBehaviorProfileDefaults(target, profileRaw) {
256
+ const defaults = normalizeHumanBehaviorOptions(String(profileRaw || ""));
257
+ Object.assign(target, {
258
+ enabled: defaults.enabled,
259
+ profile: defaults.profile,
260
+ clickMovement: defaults.clickMovement,
261
+ textEntry: defaults.textEntry,
262
+ listScrollJitter: defaults.listScrollJitter,
263
+ shortRest: defaults.shortRest,
264
+ batchRest: defaults.batchRest,
265
+ actionCooldown: defaults.actionCooldown
266
+ });
267
+ }
268
+
269
+ export function resolveHumanBehaviorForRun(args = {}, config = {}) {
270
+ const base = normalizeHumanBehaviorOptions(config.humanBehavior || config.human_behavior || null, {
271
+ legacyEnabled: config.humanRestEnabled === true
272
+ });
273
+ const override = {};
274
+ const rawBehavior = readFirstOwn(args, ["human_behavior", "humanBehavior"]);
275
+ if (typeof rawBehavior === "boolean") {
276
+ override.enabled = rawBehavior;
277
+ } else if (typeof rawBehavior === "string") {
278
+ applyHumanBehaviorProfileDefaults(override, rawBehavior);
279
+ } else if (rawBehavior && typeof rawBehavior === "object" && !Array.isArray(rawBehavior)) {
280
+ const rawProfile = readFirstOwn(rawBehavior, ["profile", "mode", "behaviorProfile", "behavior_profile"]);
281
+ if (rawProfile !== undefined) applyHumanBehaviorProfileDefaults(override, rawProfile);
282
+ Object.assign(override, rawBehavior);
283
+ }
284
+
285
+ const profile = readFirstOwn(args, ["human_behavior_profile", "humanBehaviorProfile"]);
286
+ if (profile !== undefined) applyHumanBehaviorProfileDefaults(override, profile);
287
+ const enabled = parseOptionalConfigBoolean(readFirstOwn(args, [
288
+ "human_behavior_enabled",
289
+ "humanBehaviorEnabled"
290
+ ]));
291
+ if (enabled !== null) {
292
+ override.enabled = enabled;
293
+ if (enabled === true && !override.profile) applyHumanBehaviorProfileDefaults(override, "paced");
294
+ }
295
+
296
+ const safePacing = parseOptionalConfigBoolean(readFirstOwn(args, ["safe_pacing", "safePacing"]));
297
+ if (safePacing === true) {
298
+ applyHumanBehaviorProfileDefaults(override, "paced");
299
+ } else if (safePacing === false) {
300
+ override.enabled = false;
301
+ }
302
+
303
+ const batchRest = parseOptionalConfigBoolean(readFirstOwn(args, [
304
+ "batch_rest_enabled",
305
+ "batchRestEnabled",
306
+ "batch_rest"
307
+ ]));
308
+ if (batchRest === true) {
309
+ applyHumanBehaviorProfileDefaults(override, "paced_with_rests");
310
+ } else if (batchRest === false) {
311
+ override.batchRest = false;
312
+ }
313
+
314
+ return normalizeHumanBehaviorOptions({
315
+ ...base,
316
+ ...override
317
+ });
318
+ }
319
+
241
320
  function normalizeLlmThinkingLevel(raw, fallback = "low") {
242
321
  const normalized = normalizeText(raw).toLowerCase();
243
322
  return LLM_THINKING_LEVELS.has(normalized) ? normalized : fallback;
@@ -455,6 +534,10 @@ export function resolveBossScreeningConfig(workspaceRoot) {
455
534
  || parsed.greeting_text
456
535
  || parsed.greeting
457
536
  );
537
+ const humanRestEnabled = parseConfigBoolean(parsed.humanRestEnabled, false);
538
+ const humanBehavior = normalizeHumanBehaviorOptions(parsed.humanBehavior || parsed.human_behavior || null, {
539
+ legacyEnabled: humanRestEnabled
540
+ });
458
541
  return {
459
542
  ok: true,
460
543
  config: {
@@ -469,7 +552,8 @@ export function resolveBossScreeningConfig(workspaceRoot) {
469
552
  greetingText,
470
553
  debugPort: parsePositiveInteger(parsed.debugPort, 9222),
471
554
  outputDir: resolveConfigPathValue(parsed.outputDir, configDir),
472
- humanRestEnabled: parseConfigBoolean(parsed.humanRestEnabled, false)
555
+ humanRestEnabled,
556
+ humanBehavior
473
557
  },
474
558
  config_path: configPath,
475
559
  config_dir: configDir,
package/src/cli.js CHANGED
@@ -63,7 +63,11 @@ const installConfigDefaults = Object.freeze({
63
63
  llmMaxRetries: 3,
64
64
  llmImageLimit: 8,
65
65
  llmImageDetail: "low",
66
- humanRestEnabled: false
66
+ humanRestEnabled: true,
67
+ humanBehavior: {
68
+ enabled: true,
69
+ profile: "paced_with_rests"
70
+ }
67
71
  });
68
72
  const bossChatRuntimeChildDirs = ["logs", "runs", "profiles", "reports", "artifacts", "state"];
69
73
  const bossChatCliUnsupportedStartCode = "CHAT_CLI_ASYNC_UNSUPPORTED_CDP_ONLY";
@@ -2687,6 +2691,10 @@ async function runPipelineOnce(options = {}) {
2687
2691
  "action_timeout_ms",
2688
2692
  "action_interval_ms",
2689
2693
  "action_after_click_delay_ms",
2694
+ "human_behavior_enabled",
2695
+ "human_behavior_profile",
2696
+ "safe_pacing",
2697
+ "batch_rest_enabled",
2690
2698
  "llm_timeout_ms",
2691
2699
  "llm_image_limit",
2692
2700
  "llm_image_detail"
@@ -2761,6 +2769,10 @@ function buildBossChatCliInput(options = {}) {
2761
2769
  max_candidates: parseNonNegativeInteger(options["max-candidates"] ?? options.max_candidates),
2762
2770
  dry_run: options["dry-run"] === true || options.dryRun === true,
2763
2771
  no_state: options["no-state"] === true || options.noState === true,
2772
+ human_behavior_enabled: parseBooleanOption(options["human-behavior-enabled"] ?? options.human_behavior_enabled),
2773
+ human_behavior_profile: typeof (options["human-behavior-profile"] ?? options.human_behavior_profile) === "string"
2774
+ ? (options["human-behavior-profile"] ?? options.human_behavior_profile).trim()
2775
+ : undefined,
2764
2776
  safe_pacing: parseBooleanOption(options["safe-pacing"] ?? options.safe_pacing),
2765
2777
  batch_rest_enabled: parseBooleanOption(options["batch-rest"] ?? options.batch_rest_enabled)
2766
2778
  };