@reconcrap/boss-recommend-mcp 2.0.6 → 2.0.7
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 +2 -0
- package/package.json +1 -1
- package/src/chat-mcp.js +59 -18
- package/src/core/reporting/legacy-csv.js +2 -2
- package/src/core/screening/index.js +48 -0
- package/src/domains/chat/run-service.js +93 -40
- package/src/domains/recommend/run-service.js +78 -5
- package/src/domains/recruit/run-service.js +87 -12
- package/src/index.js +50 -5
- package/src/recommend-mcp.js +81 -7
- package/src/recruit-mcp.js +103 -8
package/src/recruit-mcp.js
CHANGED
|
@@ -73,6 +73,27 @@ function parseNonNegativeInteger(raw, fallback) {
|
|
|
73
73
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function isDebugTestMode(args = {}) {
|
|
77
|
+
return args.debug_test_mode === true || args.allow_debug_test_mode === true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeScreeningModeArg(args = {}) {
|
|
81
|
+
const raw = normalizeText(args.screening_mode || args.screeningMode || "");
|
|
82
|
+
if (args.use_llm === false) return "deterministic";
|
|
83
|
+
return ["deterministic", "local", "local_scorer"].includes(raw.toLowerCase())
|
|
84
|
+
? "deterministic"
|
|
85
|
+
: "llm";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function collectRecruitDebugTestOptions(args = {}) {
|
|
89
|
+
const reasons = [];
|
|
90
|
+
if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
|
|
91
|
+
if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
|
|
92
|
+
if (args.dry_run_post_action === true) reasons.push("dry_run_post_action");
|
|
93
|
+
if (args.execute_post_action === false) reasons.push("execute_post_action=false");
|
|
94
|
+
return reasons;
|
|
95
|
+
}
|
|
96
|
+
|
|
76
97
|
function methodSummary(methodLog = []) {
|
|
77
98
|
const summary = {};
|
|
78
99
|
for (const entry of methodLog || []) {
|
|
@@ -203,8 +224,15 @@ function ensureRecruitRunArtifacts(snapshot) {
|
|
|
203
224
|
if (meta) meta.checkpointPath = artifacts.checkpoint_path;
|
|
204
225
|
|
|
205
226
|
const summary = snapshot?.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
206
|
-
|
|
207
|
-
|
|
227
|
+
const checkpointResults = Array.isArray(checkpoint.results) ? checkpoint.results : [];
|
|
228
|
+
const artifactSummary = summary || (checkpointResults.length ? {
|
|
229
|
+
domain: "recruit",
|
|
230
|
+
partial: true,
|
|
231
|
+
partial_reason: snapshot?.status || snapshot?.state || "non_terminal",
|
|
232
|
+
results: checkpointResults
|
|
233
|
+
} : null);
|
|
234
|
+
if (artifactSummary) {
|
|
235
|
+
const rows = Array.isArray(artifactSummary.results) ? artifactSummary.results : [];
|
|
208
236
|
writeRecruitLegacyCsvAtomic(artifacts.output_csv, rows, snapshot, meta);
|
|
209
237
|
writeJsonAtomic(artifacts.report_json, {
|
|
210
238
|
run_id: snapshot.runId || snapshot.run_id,
|
|
@@ -213,7 +241,7 @@ function ensureRecruitRunArtifacts(snapshot) {
|
|
|
213
241
|
progress: snapshot.progress || {},
|
|
214
242
|
context: snapshot.context || {},
|
|
215
243
|
checkpoint,
|
|
216
|
-
summary,
|
|
244
|
+
summary: artifactSummary,
|
|
217
245
|
generated_at: new Date().toISOString()
|
|
218
246
|
});
|
|
219
247
|
if (meta) {
|
|
@@ -270,6 +298,12 @@ function buildLegacyRunResult(snapshot) {
|
|
|
270
298
|
const artifacts = ensureRecruitRunArtifacts(snapshot);
|
|
271
299
|
const meta = getRecruitRunMeta(snapshot.runId);
|
|
272
300
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
301
|
+
const checkpoint = snapshot.checkpoint && typeof snapshot.checkpoint === "object" ? snapshot.checkpoint : {};
|
|
302
|
+
const resultRows = Array.isArray(summary?.results)
|
|
303
|
+
? summary.results
|
|
304
|
+
: Array.isArray(checkpoint.results)
|
|
305
|
+
? checkpoint.results
|
|
306
|
+
: [];
|
|
273
307
|
const progress = normalizeLegacyProgress(snapshot.progress, summary);
|
|
274
308
|
const targetCount = Number.isInteger(progress.target_count)
|
|
275
309
|
? progress.target_count
|
|
@@ -302,7 +336,8 @@ function buildLegacyRunResult(snapshot) {
|
|
|
302
336
|
|| null,
|
|
303
337
|
completion_reason: completionReason(snapshot.status),
|
|
304
338
|
target_count_semantics: TARGET_COUNT_SEMANTICS,
|
|
305
|
-
run_id: snapshot.runId
|
|
339
|
+
run_id: snapshot.runId,
|
|
340
|
+
results: resultRows
|
|
306
341
|
};
|
|
307
342
|
}
|
|
308
343
|
|
|
@@ -387,7 +422,34 @@ export function createRecruitPipelineInputSchema() {
|
|
|
387
422
|
detail_limit: {
|
|
388
423
|
type: "integer",
|
|
389
424
|
minimum: 0,
|
|
390
|
-
description: "
|
|
425
|
+
description: "打开详情/CV 的人数上限;默认跟随 max_candidates。detail_limit=0 属于调试路径,需要 debug_test_mode=true"
|
|
426
|
+
},
|
|
427
|
+
debug_test_mode: {
|
|
428
|
+
type: "boolean",
|
|
429
|
+
description: "高级测试开关;默认 false。只有显式为 true 时才允许 deterministic/local scorer、detail_limit=0 等调试路径"
|
|
430
|
+
},
|
|
431
|
+
screening_mode: {
|
|
432
|
+
type: "string",
|
|
433
|
+
enum: ["llm", "deterministic"],
|
|
434
|
+
description: "筛选引擎;默认 llm。deterministic 仅限 debug_test_mode=true"
|
|
435
|
+
},
|
|
436
|
+
use_llm: {
|
|
437
|
+
type: "boolean",
|
|
438
|
+
description: "兼容字段;默认 true。use_llm=false 等同 deterministic,仅限 debug_test_mode=true"
|
|
439
|
+
},
|
|
440
|
+
llm_timeout_ms: {
|
|
441
|
+
type: "integer",
|
|
442
|
+
minimum: 1000,
|
|
443
|
+
description: "可选,单个候选人的 LLM 调用超时"
|
|
444
|
+
},
|
|
445
|
+
llm_image_limit: {
|
|
446
|
+
type: "integer",
|
|
447
|
+
minimum: 1,
|
|
448
|
+
description: "可选,传给 LLM 的图片简历截图页数上限"
|
|
449
|
+
},
|
|
450
|
+
llm_image_detail: {
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "可选,图片输入 detail,默认 high"
|
|
391
453
|
},
|
|
392
454
|
delay_ms: {
|
|
393
455
|
type: "integer",
|
|
@@ -751,22 +813,30 @@ async function connectRecruitChromeSession({
|
|
|
751
813
|
};
|
|
752
814
|
}
|
|
753
815
|
|
|
754
|
-
function getRunOptions(args, parsed, session) {
|
|
816
|
+
function getRunOptions(args, parsed, session, configResolution = null) {
|
|
755
817
|
const slowLive = args.slow_live === true;
|
|
756
818
|
const targetCount = parsePositiveInteger(args.max_candidates, parsed.screenParams.target_count || 10);
|
|
819
|
+
const screeningMode = normalizeScreeningModeArg(args);
|
|
757
820
|
return {
|
|
758
821
|
client: session.client,
|
|
759
822
|
targetUrl: RECRUIT_TARGET_URL,
|
|
760
823
|
criteria: parsed.screenParams.criteria,
|
|
761
824
|
searchParams: parsed.searchParams,
|
|
762
825
|
maxCandidates: targetCount,
|
|
763
|
-
detailLimit: parseNonNegativeInteger(args.detail_limit,
|
|
826
|
+
detailLimit: parseNonNegativeInteger(args.detail_limit, targetCount),
|
|
764
827
|
closeDetail: true,
|
|
765
828
|
delayMs: Math.max(0, parsePositiveInteger(args.delay_ms, 0)),
|
|
766
829
|
cardTimeoutMs: slowLive ? 180000 : 90000,
|
|
767
830
|
resetBeforeSearch: args.reset_search !== false,
|
|
768
831
|
resetTimeoutMs: slowLive ? 300000 : 180000,
|
|
769
832
|
cityOptionTimeoutMs: slowLive ? 60000 : 30000,
|
|
833
|
+
screeningMode,
|
|
834
|
+
llmConfig: screeningMode === "llm" && configResolution?.ok ? {
|
|
835
|
+
...configResolution.config
|
|
836
|
+
} : null,
|
|
837
|
+
llmTimeoutMs: parsePositiveInteger(args.llm_timeout_ms, slowLive ? 180000 : 120000),
|
|
838
|
+
llmImageLimit: parsePositiveInteger(args.llm_image_limit, 8),
|
|
839
|
+
llmImageDetail: normalizeText(args.llm_image_detail) || "high",
|
|
770
840
|
name: "mcp-recruit-pipeline-run"
|
|
771
841
|
};
|
|
772
842
|
}
|
|
@@ -814,6 +884,31 @@ async function startRecruitPipelineRunInternal(args = {}, { workspaceRoot = "" }
|
|
|
814
884
|
const gate = evaluateRecruitPipelineGate(parsed);
|
|
815
885
|
if (gate) return gate;
|
|
816
886
|
const configResolution = resolveBossScreeningConfig(workspaceRoot);
|
|
887
|
+
const screeningMode = normalizeScreeningModeArg(args);
|
|
888
|
+
const debugTestOptions = collectRecruitDebugTestOptions(args);
|
|
889
|
+
if (debugTestOptions.length && !isDebugTestMode(args)) {
|
|
890
|
+
return {
|
|
891
|
+
status: "FAILED",
|
|
892
|
+
error: {
|
|
893
|
+
code: "DEBUG_TEST_MODE_REQUIRED",
|
|
894
|
+
message: `这些参数属于调试/测试路径,正式 live run 不会默认启用:${debugTestOptions.join(", ")}。如确需测试,请显式传 debug_test_mode=true。`,
|
|
895
|
+
retryable: false
|
|
896
|
+
},
|
|
897
|
+
debug_test_options: debugTestOptions
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
if (screeningMode === "llm" && !configResolution.ok) {
|
|
901
|
+
return {
|
|
902
|
+
status: "FAILED",
|
|
903
|
+
error: {
|
|
904
|
+
code: "SCREEN_CONFIG_ERROR",
|
|
905
|
+
message: configResolution.error?.message || "screening-config.json is required for LLM screening.",
|
|
906
|
+
retryable: true
|
|
907
|
+
},
|
|
908
|
+
config_path: configResolution.config_path || null,
|
|
909
|
+
candidate_paths: configResolution.candidate_paths || []
|
|
910
|
+
};
|
|
911
|
+
}
|
|
817
912
|
const host = normalizeText(args.host) || DEFAULT_RECRUIT_HOST;
|
|
818
913
|
const port = parsePositiveInteger(
|
|
819
914
|
args.port,
|
|
@@ -850,7 +945,7 @@ async function startRecruitPipelineRunInternal(args = {}, { workspaceRoot = "" }
|
|
|
850
945
|
|
|
851
946
|
let started;
|
|
852
947
|
try {
|
|
853
|
-
started = recruitRunService.startRecruitRun(getRunOptions(args, parsed, session));
|
|
948
|
+
started = recruitRunService.startRecruitRun(getRunOptions(args, parsed, session, configResolution));
|
|
854
949
|
} catch (error) {
|
|
855
950
|
await session.close?.();
|
|
856
951
|
return {
|