@reconcrap/boss-recommend-mcp 1.3.36 → 1.3.37
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 +1 -1
- package/src/pipeline.js +132 -14
- package/src/test-pipeline.js +66 -4
package/package.json
CHANGED
package/src/pipeline.js
CHANGED
|
@@ -30,6 +30,15 @@ const SEARCH_NO_IFRAME_RETRY_DELAY_MS = 1200;
|
|
|
30
30
|
const MAX_SEARCH_FILTER_AUTO_RETRY_ATTEMPTS = 2;
|
|
31
31
|
const SEARCH_FILTER_AUTO_RETRY_DELAY_MS = 1200;
|
|
32
32
|
const BOSS_CHAT_FOLLOW_UP_POLL_MS = 1500;
|
|
33
|
+
const FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN = "passed_count";
|
|
34
|
+
const FOLLOW_UP_TARGET_COUNT_PASSED_LABEL = "通过筛选数";
|
|
35
|
+
const FOLLOW_UP_TARGET_COUNT_PASSED_ALIASES = new Set([
|
|
36
|
+
FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN,
|
|
37
|
+
"passed",
|
|
38
|
+
"通过筛选数",
|
|
39
|
+
"筛选通过数",
|
|
40
|
+
"通过数"
|
|
41
|
+
]);
|
|
33
42
|
const SEARCH_FILTER_RETRY_TOKENS = [
|
|
34
43
|
"FILTER_CONFIRM_FAILED",
|
|
35
44
|
"FILTER_DOM_CLASS_VERIFY_FAILED",
|
|
@@ -72,6 +81,75 @@ function normalizePipelineTargetCountValue(value) {
|
|
|
72
81
|
return normalizeTargetCountInput(value).publicValue;
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
function isFollowUpPassedTargetCountToken(value) {
|
|
85
|
+
const normalized = normalizeText(value).toLowerCase().replace(/\s+/g, "");
|
|
86
|
+
if (!normalized) return false;
|
|
87
|
+
return FOLLOW_UP_TARGET_COUNT_PASSED_ALIASES.has(normalized);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function normalizeFollowUpTargetCountInput(value) {
|
|
91
|
+
const normalized = normalizeTargetCountInput(value);
|
|
92
|
+
if (normalized.provided) {
|
|
93
|
+
return {
|
|
94
|
+
...normalized,
|
|
95
|
+
launchValue: normalized.publicValue,
|
|
96
|
+
passedCountMode: false
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (isFollowUpPassedTargetCountToken(value)) {
|
|
100
|
+
return {
|
|
101
|
+
provided: true,
|
|
102
|
+
targetCount: null,
|
|
103
|
+
cliArg: null,
|
|
104
|
+
publicValue: FOLLOW_UP_TARGET_COUNT_PASSED_LABEL,
|
|
105
|
+
rawValue: value,
|
|
106
|
+
parseError: null,
|
|
107
|
+
launchValue: FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN,
|
|
108
|
+
passedCountMode: true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
...normalized,
|
|
113
|
+
launchValue: null,
|
|
114
|
+
passedCountMode: false
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resolveFollowUpChatTargetCountForLaunch(targetCount, recommendSummary = null) {
|
|
119
|
+
if (isFollowUpPassedTargetCountToken(targetCount)) {
|
|
120
|
+
const passedCount = parsePositiveIntegerValue(recommendSummary?.passed_count);
|
|
121
|
+
if (passedCount) {
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
target_count: passedCount,
|
|
125
|
+
resolved_from: FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN,
|
|
126
|
+
passed_count: recommendSummary?.passed_count ?? null
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
code: "FOLLOW_UP_TARGET_COUNT_PASSED_UNAVAILABLE",
|
|
132
|
+
message: "boss-chat follow-up 选择了“通过筛选数”,但本次通过人数为空或 0。请改为正整数,或填写 all(扫到底)。",
|
|
133
|
+
passed_count: recommendSummary?.passed_count ?? null
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const normalized = normalizeTargetCountInput(targetCount);
|
|
137
|
+
if (normalized.provided) {
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
target_count: normalized.publicValue,
|
|
141
|
+
resolved_from: "explicit",
|
|
142
|
+
passed_count: recommendSummary?.passed_count ?? null
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
code: "FOLLOW_UP_TARGET_COUNT_INVALID",
|
|
148
|
+
message: normalized.parseError || "boss-chat follow-up target_count 无效。",
|
|
149
|
+
passed_count: recommendSummary?.passed_count ?? null
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
75
153
|
function sleep(ms) {
|
|
76
154
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
77
155
|
}
|
|
@@ -394,13 +472,12 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
394
472
|
const defaultCriteria = normalizeText(defaults?.criteria || "");
|
|
395
473
|
const defaultStartFromRaw = normalizeText(defaults?.start_from || "").toLowerCase();
|
|
396
474
|
const defaultStartFrom = defaultStartFromRaw === "all" ? "all" : "unread";
|
|
397
|
-
const defaultTargetCount = normalizePipelineTargetCountValue(defaults?.target_count);
|
|
398
475
|
|
|
399
476
|
const explicitCriteria = normalizeText(raw.criteria);
|
|
400
477
|
const explicitStartFromRaw = normalizeText(raw.start_from).toLowerCase();
|
|
401
478
|
const explicitStartFrom = explicitStartFromRaw === "all" ? "all" : explicitStartFromRaw === "unread" ? "unread" : "";
|
|
402
|
-
const explicitTarget =
|
|
403
|
-
const explicitTargetCount = explicitTarget.
|
|
479
|
+
const explicitTarget = normalizeFollowUpTargetCountInput(raw.target_count);
|
|
480
|
+
const explicitTargetCount = explicitTarget.launchValue;
|
|
404
481
|
|
|
405
482
|
const hasExplicitCriteria = Boolean(explicitCriteria);
|
|
406
483
|
const hasExplicitStartFrom = Boolean(explicitStartFrom);
|
|
@@ -408,14 +485,15 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
408
485
|
|
|
409
486
|
const criteria = explicitCriteria || defaultCriteria;
|
|
410
487
|
const startFrom = explicitStartFrom || defaultStartFrom;
|
|
411
|
-
const targetCount = explicitTargetCount ||
|
|
488
|
+
const targetCount = explicitTargetCount || FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN;
|
|
489
|
+
const targetCountSummaryValue = explicitTarget.publicValue || FOLLOW_UP_TARGET_COUNT_PASSED_LABEL;
|
|
412
490
|
|
|
413
491
|
const profile = normalizeText(raw.profile) || "default";
|
|
414
492
|
const summary = {
|
|
415
493
|
profile,
|
|
416
494
|
criteria: criteria || null,
|
|
417
495
|
start_from: startFrom || null,
|
|
418
|
-
target_count:
|
|
496
|
+
target_count: targetCountSummaryValue,
|
|
419
497
|
dry_run: raw.dry_run === true,
|
|
420
498
|
no_state: raw.no_state === true,
|
|
421
499
|
safe_pacing: typeof raw.safe_pacing === "boolean" ? raw.safe_pacing : null,
|
|
@@ -446,21 +524,39 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
446
524
|
});
|
|
447
525
|
}
|
|
448
526
|
if (!hasExplicitTargetCount) {
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
chat: {
|
|
454
|
-
target_count: "all"
|
|
455
|
-
}
|
|
527
|
+
const recommendedTargetCountPatch = {
|
|
528
|
+
follow_up: {
|
|
529
|
+
chat: {
|
|
530
|
+
target_count: FOLLOW_UP_TARGET_COUNT_PASSED_LABEL
|
|
456
531
|
}
|
|
457
532
|
}
|
|
533
|
+
};
|
|
534
|
+
const targetCountHints = buildTargetCountCompatibilityHints({
|
|
535
|
+
argumentName: "follow_up.chat.target_count",
|
|
536
|
+
recommendedArgumentPatch: recommendedTargetCountPatch
|
|
458
537
|
});
|
|
459
538
|
missing_fields.push("follow_up.chat.target_count");
|
|
460
539
|
pending_questions.push({
|
|
461
540
|
...targetCountHints,
|
|
541
|
+
answer_format: `follow_up.chat.target_count = "${FOLLOW_UP_TARGET_COUNT_PASSED_LABEL}" | 正整数 | "all"`,
|
|
542
|
+
recommended_value: FOLLOW_UP_TARGET_COUNT_PASSED_LABEL,
|
|
543
|
+
recommended_argument_patch: recommendedTargetCountPatch,
|
|
544
|
+
canonical_passed_count_value: FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN,
|
|
545
|
+
accepted_examples: [
|
|
546
|
+
FOLLOW_UP_TARGET_COUNT_PASSED_LABEL,
|
|
547
|
+
...targetCountHints.accepted_examples
|
|
548
|
+
],
|
|
549
|
+
options: [
|
|
550
|
+
{
|
|
551
|
+
label: `通过筛选数(推荐)`,
|
|
552
|
+
value: FOLLOW_UP_TARGET_COUNT_PASSED_LABEL,
|
|
553
|
+
canonical_value: FOLLOW_UP_TARGET_COUNT_PASSED_TOKEN,
|
|
554
|
+
argument_patch: recommendedTargetCountPatch
|
|
555
|
+
},
|
|
556
|
+
...(Array.isArray(targetCountHints.options) ? targetCountHints.options : [])
|
|
557
|
+
],
|
|
462
558
|
field: "follow_up.chat.target_count",
|
|
463
|
-
question: "请填写 boss-chat follow-up
|
|
559
|
+
question: "请填写 boss-chat follow-up 目标人数。默认建议填写“通过筛选数”;若要扫到底,请在 follow_up.chat.target_count 里字面填写 \"all\"。",
|
|
464
560
|
value: summary.target_count,
|
|
465
561
|
...(explicitTarget.rawValue !== undefined ? { received_target_count: explicitTarget.rawValue } : {}),
|
|
466
562
|
...(explicitTarget.parseError ? { target_count_parse_error: explicitTarget.parseError } : {})
|
|
@@ -857,10 +953,32 @@ async function runBossChatFollowUpPhase({
|
|
|
857
953
|
cancelChatRun
|
|
858
954
|
}) {
|
|
859
955
|
const recommendSummary = recommendResult?.result || null;
|
|
860
|
-
const
|
|
956
|
+
const requestedChatInput = buildResolvedFollowUpChatInput(followUpChat, {
|
|
861
957
|
selectedJob,
|
|
862
958
|
debugPort
|
|
863
959
|
});
|
|
960
|
+
const targetCountResolution = resolveFollowUpChatTargetCountForLaunch(
|
|
961
|
+
requestedChatInput.target_count,
|
|
962
|
+
recommendSummary
|
|
963
|
+
);
|
|
964
|
+
if (!targetCountResolution.ok) {
|
|
965
|
+
return buildFollowUpFailedResponse(
|
|
966
|
+
targetCountResolution.code || "BOSS_CHAT_FOLLOW_UP_TARGET_COUNT_INVALID",
|
|
967
|
+
targetCountResolution.message || "boss-chat follow-up target_count 无法解析。",
|
|
968
|
+
recommendResult,
|
|
969
|
+
{
|
|
970
|
+
enabled: true,
|
|
971
|
+
input: requestedChatInput,
|
|
972
|
+
target_count_resolution: targetCountResolution
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
const resolvedChatInput = {
|
|
977
|
+
...requestedChatInput,
|
|
978
|
+
target_count: targetCountResolution.target_count,
|
|
979
|
+
target_count_source: targetCountResolution.resolved_from,
|
|
980
|
+
target_count_requested: requestedChatInput.target_count
|
|
981
|
+
};
|
|
864
982
|
let chatRunId = normalizeText(resume?.chat_run_id || "");
|
|
865
983
|
const resumeFromChatPhase = resume?.resume === true && normalizeText(resume?.follow_up_phase) === "chat_followup";
|
|
866
984
|
let pauseRequested = false;
|
package/src/test-pipeline.js
CHANGED
|
@@ -2183,8 +2183,9 @@ async function testFollowUpChatMissingFieldsShouldExposeRecommendDefaults() {
|
|
|
2183
2183
|
const targetCountQuestion = result.pending_questions.find((item) => item.field === "follow_up.chat.target_count");
|
|
2184
2184
|
assert.equal(criteriaQuestion?.value, "默认沿用 recommend 的筛选条件");
|
|
2185
2185
|
assert.equal(startFromQuestion?.value, "unread");
|
|
2186
|
-
assert.equal(targetCountQuestion?.value,
|
|
2187
|
-
assert.equal(targetCountQuestion?.recommended_argument_patch?.follow_up?.chat?.target_count, "
|
|
2186
|
+
assert.equal(targetCountQuestion?.value, "通过筛选数");
|
|
2187
|
+
assert.equal(targetCountQuestion?.recommended_argument_patch?.follow_up?.chat?.target_count, "通过筛选数");
|
|
2188
|
+
assert.equal(targetCountQuestion?.options?.some((item) => item.label.includes("通过筛选数(推荐)")), true);
|
|
2188
2189
|
assert.equal(targetCountQuestion?.options?.some((item) => item.label.includes('follow_up.chat.target_count="all"')), true);
|
|
2189
2190
|
}
|
|
2190
2191
|
|
|
@@ -2225,7 +2226,9 @@ async function testFollowUpChatMissingTargetCountShouldNeedInput() {
|
|
|
2225
2226
|
assert.equal(result.missing_fields.includes("follow_up.chat.target_count"), true);
|
|
2226
2227
|
const targetQuestion = result.pending_questions.find((item) => item.field === "follow_up.chat.target_count");
|
|
2227
2228
|
assert.equal(Boolean(targetQuestion), true);
|
|
2228
|
-
assert.equal(targetQuestion.recommended_argument_patch?.follow_up?.chat?.target_count, "
|
|
2229
|
+
assert.equal(targetQuestion.recommended_argument_patch?.follow_up?.chat?.target_count, "通过筛选数");
|
|
2230
|
+
assert.equal(targetQuestion.options?.some((item) => item.value === "通过筛选数"), true);
|
|
2231
|
+
assert.equal(targetQuestion.options?.some((item) => item.value === "all"), true);
|
|
2229
2232
|
}
|
|
2230
2233
|
|
|
2231
2234
|
async function testFollowUpChatInvalidTargetCountShouldNeedInputWithDiagnostics() {
|
|
@@ -2248,7 +2251,8 @@ async function testFollowUpChatInvalidTargetCountShouldNeedInputWithDiagnostics(
|
|
|
2248
2251
|
assert.equal(targetQuestion?.received_target_count, "not a target");
|
|
2249
2252
|
assert.equal(Boolean(targetQuestion?.target_count_parse_error), true);
|
|
2250
2253
|
assert.equal(targetQuestion?.accepted_examples.includes("all"), true);
|
|
2251
|
-
assert.equal(targetQuestion?.
|
|
2254
|
+
assert.equal(targetQuestion?.accepted_examples.includes("通过筛选数"), true);
|
|
2255
|
+
assert.equal(targetQuestion?.recommended_argument_patch?.follow_up?.chat?.target_count, "通过筛选数");
|
|
2252
2256
|
}
|
|
2253
2257
|
|
|
2254
2258
|
async function testFollowUpChatAllTargetCountShouldLaunchUnlimited() {
|
|
@@ -2317,6 +2321,63 @@ async function testFollowUpChatAllTargetCountShouldLaunchUnlimited() {
|
|
|
2317
2321
|
assert.equal(result.follow_up?.chat?.target_count, "all");
|
|
2318
2322
|
}
|
|
2319
2323
|
|
|
2324
|
+
async function testFollowUpChatPassedTargetCountShouldLaunchWithPassedCount() {
|
|
2325
|
+
let capturedChatInput = null;
|
|
2326
|
+
const result = await runRecommendPipeline(
|
|
2327
|
+
{
|
|
2328
|
+
workspaceRoot: process.cwd(),
|
|
2329
|
+
instruction: "test",
|
|
2330
|
+
confirmation: createJobConfirmedConfirmation(),
|
|
2331
|
+
overrides: {},
|
|
2332
|
+
followUp: createFollowUpChat({ target_count: "通过筛选数" })
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
parseRecommendInstruction: () => createParsed(),
|
|
2336
|
+
runPipelinePreflight: () => ({ ok: true, checks: [], debug_port: 9555 }),
|
|
2337
|
+
ensureBossRecommendPageReady: async () => ({ ok: true, state: "RECOMMEND_READY", page_state: {} }),
|
|
2338
|
+
listRecommendJobs: async () => createJobListResult(),
|
|
2339
|
+
runRecommendSearchCli: async () => ({
|
|
2340
|
+
ok: true,
|
|
2341
|
+
summary: {
|
|
2342
|
+
candidate_count: 6,
|
|
2343
|
+
applied_filters: {},
|
|
2344
|
+
page_state: {}
|
|
2345
|
+
}
|
|
2346
|
+
}),
|
|
2347
|
+
runRecommendScreenCli: async () => ({
|
|
2348
|
+
ok: true,
|
|
2349
|
+
summary: {
|
|
2350
|
+
processed_count: 6,
|
|
2351
|
+
passed_count: 2,
|
|
2352
|
+
skipped_count: 0
|
|
2353
|
+
}
|
|
2354
|
+
}),
|
|
2355
|
+
startBossChatRun: async ({ input }) => {
|
|
2356
|
+
capturedChatInput = input;
|
|
2357
|
+
return {
|
|
2358
|
+
status: "ACCEPTED",
|
|
2359
|
+
run_id: "chat-run-pass-count",
|
|
2360
|
+
message: "chat started"
|
|
2361
|
+
};
|
|
2362
|
+
},
|
|
2363
|
+
getBossChatRun: async () => ({
|
|
2364
|
+
status: "COMPLETED",
|
|
2365
|
+
run: {
|
|
2366
|
+
runId: "chat-run-pass-count",
|
|
2367
|
+
state: "completed",
|
|
2368
|
+
lastMessage: "chat completed",
|
|
2369
|
+
progress: { processed: 2, matched: 2 }
|
|
2370
|
+
}
|
|
2371
|
+
})
|
|
2372
|
+
}
|
|
2373
|
+
);
|
|
2374
|
+
|
|
2375
|
+
assert.equal(result.status, "COMPLETED");
|
|
2376
|
+
assert.equal(capturedChatInput?.target_count, 2);
|
|
2377
|
+
assert.equal(result.follow_up?.chat?.input?.target_count, 2);
|
|
2378
|
+
assert.equal(result.follow_up?.chat?.input?.target_count_requested, "passed_count");
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2320
2381
|
async function testFinalReviewShouldIncludeFollowUpChatSummary() {
|
|
2321
2382
|
const result = await runRecommendPipeline(
|
|
2322
2383
|
{
|
|
@@ -2576,6 +2637,7 @@ async function main() {
|
|
|
2576
2637
|
await testFinalReviewShouldIncludeFollowUpChatSummary();
|
|
2577
2638
|
await testCompletedPipelineShouldRunChatFollowUp();
|
|
2578
2639
|
await testFollowUpChatAllTargetCountShouldLaunchUnlimited();
|
|
2640
|
+
await testFollowUpChatPassedTargetCountShouldLaunchWithPassedCount();
|
|
2579
2641
|
await testCompletedPipelineShouldFailWhenChatLaunchFails();
|
|
2580
2642
|
await testCompletedPipelineShouldFailWhenChatRunFails();
|
|
2581
2643
|
console.log("pipeline tests passed");
|