@reconcrap/boss-recommend-mcp 1.3.37 → 1.3.39
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 +6 -2
- package/package.json +1 -1
- package/skills/boss-chat/SKILL.md +8 -0
- package/skills/boss-recommend-pipeline/SKILL.md +2 -0
- package/src/boss-chat.js +32 -27
- package/src/cli.js +8 -1
- package/src/index.js +28 -0
- package/src/pipeline.js +7 -0
- package/src/test-boss-chat.js +191 -75
- package/src/test-pipeline.js +57 -0
- package/vendor/boss-chat-cli/src/app.js +21 -16
- package/vendor/boss-chat-cli/src/cli.js +68 -50
- package/vendor/boss-chat-cli/src/services/profile-store.js +26 -21
- package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +208 -63
- package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +129 -0
package/README.md
CHANGED
|
@@ -188,7 +188,7 @@ node src/cli.js launch-chrome --port 9222
|
|
|
188
188
|
node src/cli.js run --instruction-file request.txt --confirmation-file confirmation.json --overrides-file overrides.json
|
|
189
189
|
node src/cli.js run --instruction-file request.txt --confirmation-file confirmation.json --overrides-file overrides.json --follow-up-file follow-up.json
|
|
190
190
|
node src/cli.js chat health-check
|
|
191
|
-
node src/cli.js chat run --job "算法工程师" --start-from unread --criteria "有 AI Agent 经验" --targetCount 20
|
|
191
|
+
node src/cli.js chat run --job "算法工程师" --start-from unread --criteria "有 AI Agent 经验" --targetCount 20 --greeting-text "您好,方便发下简历吗?"
|
|
192
192
|
```
|
|
193
193
|
|
|
194
194
|
## Recommend + Chat Follow-up
|
|
@@ -201,6 +201,7 @@ node src/cli.js chat run --job "算法工程师" --start-from unread --criteria
|
|
|
201
201
|
"chat": {
|
|
202
202
|
"criteria": "候选人需要继续在聊天页过滤有 AI Agent 经验的人选",
|
|
203
203
|
"start_from": "unread",
|
|
204
|
+
"greeting_text": "您好,方便发下简历吗?",
|
|
204
205
|
"target_count": 20,
|
|
205
206
|
"profile": "default",
|
|
206
207
|
"dry_run": false,
|
|
@@ -215,9 +216,11 @@ node src/cli.js chat run --job "算法工程师" --start-from unread --criteria
|
|
|
215
216
|
说明:
|
|
216
217
|
|
|
217
218
|
- `criteria` / `start_from` / `target_count` 为必填
|
|
219
|
+
- `greeting_text` 可选(兼容 `greetingText`)
|
|
218
220
|
- `profile` 可选,默认 `default`
|
|
219
221
|
- `job` 与 `port` 继承 recommend run 已选岗位和调试端口
|
|
220
222
|
- `baseUrl` / `apiKey` / `model` 不再单独传入,固定复用 recommend 的 `screening-config.json`
|
|
223
|
+
- `greeting_text` 默认优先级:本次显式值 > profile 历史值 > 内置默认招呼语(`Hi同学,能麻烦发下简历吗?`)
|
|
221
224
|
- 若缺少 `follow_up.chat` 必填项,pipeline 会返回 `NEED_INPUT`
|
|
222
225
|
- recommend 成功后,父 run 继续存活并进入 `chat_followup`;chat 结束后父 run 才会进入最终终态
|
|
223
226
|
- `boss-chat` 子任务状态统一写入 `~/.boss-recommend-mcp/boss-chat`(或 `BOSS_CHAT_HOME` 指定目录),不再依赖工作区 `cwd`
|
|
@@ -229,7 +232,7 @@ node src/cli.js chat run --job "算法工程师" --start-from unread --criteria
|
|
|
229
232
|
- CLI:
|
|
230
233
|
- `boss-recommend-mcp chat health-check`
|
|
231
234
|
- `boss-recommend-mcp chat prepare-run`
|
|
232
|
-
- `boss-recommend-mcp chat run --job "算法工程师" --start-from unread --targetCount 20 --criteria "有 AI Agent 经验"`(后台启动,不自动轮询)
|
|
235
|
+
- `boss-recommend-mcp chat run --job "算法工程师" --start-from unread --targetCount 20 --criteria "有 AI Agent 经验" [--greeting-text "您好,方便发下简历吗?"]`(后台启动,不自动轮询)
|
|
233
236
|
- `boss-recommend-mcp chat start-run|get-run|pause-run|resume-run|cancel-run`
|
|
234
237
|
- MCP:
|
|
235
238
|
- `boss_chat_health_check`
|
|
@@ -247,6 +250,7 @@ chat-only 交互建议:
|
|
|
247
250
|
|
|
248
251
|
- 先调用一次 `prepare_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
|
|
249
252
|
- 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count`、`criteria` 后调用 `start_boss_chat_run` 启动任务。
|
|
253
|
+
- `greeting_text` 可选;未传时会自动沿用 profile 上次输入,若无历史值则使用默认招呼语(`Hi同学,能麻烦发下简历吗?`)。
|
|
250
254
|
- `target_count` 支持正整数、`all`、`-1`;若用户给出 `全部候选人` / `所有候选人`,会自动按不限(扫到底)处理。
|
|
251
255
|
|
|
252
256
|
Trae-CN / 长对话防循环建议:
|
package/package.json
CHANGED
|
@@ -31,12 +31,19 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
|
|
|
31
31
|
可选:
|
|
32
32
|
|
|
33
33
|
- `profile`(默认 `default`)
|
|
34
|
+
- `greeting_text`(兼容 `greetingText`,可选自定义首条打招呼消息)
|
|
34
35
|
- `port`
|
|
35
36
|
- `dry_run`
|
|
36
37
|
- `no_state`
|
|
37
38
|
- `safe_pacing`
|
|
38
39
|
- `batch_rest_enabled`
|
|
39
40
|
|
|
41
|
+
`greeting_text` 默认规则:
|
|
42
|
+
|
|
43
|
+
- 本次显式传入 `greeting_text`:使用本次值
|
|
44
|
+
- 本次未传,但当前 `profile` 有历史输入:使用历史值
|
|
45
|
+
- 两者都没有:使用内置默认招呼语(`Hi同学,能麻烦发下简历吗?`)
|
|
46
|
+
|
|
40
47
|
`target_count` 填写规则(关键):
|
|
41
48
|
|
|
42
49
|
- 正整数:如 `20`
|
|
@@ -55,6 +62,7 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
|
|
|
55
62
|
- 当用户说“全部候选人/所有候选人”时,必须按“扫到底(unlimited)”处理,不要再追问正整数。
|
|
56
63
|
- 参数名必须写 `target_count`(不要写“目标数量”等中文键名)。
|
|
57
64
|
- 当用户选择“扫到底/全部候选人/所有候选人”时,调用参数优先写:`"target_count": "all"`;`-1` 只作为兼容输入和内部 CLI 表示。
|
|
65
|
+
- `greeting_text` 是可选项,不能因为缺少它阻塞启动或追加必填追问。
|
|
58
66
|
- 若工具或提问选项里出现“扫到底(必须传 `target_count=\"all\"`)”之类字样,下一次工具调用时必须直接照抄这个字面量,不要只保留“扫到底”语义。
|
|
59
67
|
- 禁止 agent 自行补全 `job/start_from/criteria` 并直接执行,必须由用户明确给出或确认。
|
|
60
68
|
- chat-only 启动流程必须先进入聊天页并拉取岗位列表,再让用户从列表中选择 `job`。
|
|
@@ -72,8 +72,10 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
72
72
|
- `start_from`: `unread|all`
|
|
73
73
|
- `target_count`
|
|
74
74
|
- `follow_up.chat` 可选:
|
|
75
|
+
- `greeting_text`(兼容 `greetingText`,自定义首条打招呼消息)
|
|
75
76
|
- `profile`(默认 `default`)
|
|
76
77
|
- `dry_run/no_state/safe_pacing/batch_rest_enabled`
|
|
78
|
+
- `greeting_text` 未传时,boss-chat 会自动按默认优先级回退:本次显式值 > profile 历史值 > 内置默认招呼语
|
|
77
79
|
- `job` / `port` 继承 recommend run 上下文;不要单独向用户再要一份
|
|
78
80
|
- LLM 配置固定复用 recommend 的 `screening-config.json`;不要单独向用户再要 `baseUrl/apiKey/model`
|
|
79
81
|
- 缺少 `follow_up.chat` 必填项时,按 `pending_questions` 补缺口;不要额外发起一轮独立的 chat 确认流程
|
package/src/boss-chat.js
CHANGED
|
@@ -573,20 +573,22 @@ function resolveBossChatScreenConfig(workspaceRoot) {
|
|
|
573
573
|
};
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
-
function normalizeBossChatStartInput(input = {}) {
|
|
577
|
-
const profile = normalizeText(input.profile) || "default";
|
|
578
|
-
const job = normalizeText(input.job);
|
|
579
|
-
const startFromRaw = normalizeText(input.startFrom || input.start_from).toLowerCase();
|
|
580
|
-
const startFrom = startFromRaw === "all" ? "all" : startFromRaw === "unread" ? "unread" : "";
|
|
581
|
-
const criteria = normalizeText(input.criteria);
|
|
582
|
-
const
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
576
|
+
function normalizeBossChatStartInput(input = {}) {
|
|
577
|
+
const profile = normalizeText(input.profile) || "default";
|
|
578
|
+
const job = normalizeText(input.job);
|
|
579
|
+
const startFromRaw = normalizeText(input.startFrom || input.start_from).toLowerCase();
|
|
580
|
+
const startFrom = startFromRaw === "all" ? "all" : startFromRaw === "unread" ? "unread" : "";
|
|
581
|
+
const criteria = normalizeText(input.criteria);
|
|
582
|
+
const greetingText = normalizeText(input.greeting_text || input.greetingText || input.greeting);
|
|
583
|
+
const parsedTarget = normalizeTargetCountInput(getBossChatTargetCountValue(input));
|
|
584
|
+
const port = parsePositiveInteger(input.port);
|
|
585
|
+
return {
|
|
586
|
+
profile,
|
|
587
|
+
job,
|
|
588
|
+
startFrom,
|
|
589
|
+
criteria,
|
|
590
|
+
greetingText,
|
|
591
|
+
targetCount: parsedTarget.targetCount,
|
|
590
592
|
targetCountArg: parsedTarget.cliArg,
|
|
591
593
|
targetCountProvided: parsedTarget.provided,
|
|
592
594
|
targetCountPublicValue: parsedTarget.publicValue,
|
|
@@ -645,10 +647,11 @@ function buildNextCallExample(input = {}, missingFields = []) {
|
|
|
645
647
|
const sample = {};
|
|
646
648
|
if (normalized.job) sample.job = normalized.job;
|
|
647
649
|
if (normalized.startFrom) sample.start_from = normalized.startFrom;
|
|
648
|
-
if (normalized.criteria) sample.criteria = normalized.criteria;
|
|
649
|
-
if (normalized.
|
|
650
|
-
|
|
651
|
-
|
|
650
|
+
if (normalized.criteria) sample.criteria = normalized.criteria;
|
|
651
|
+
if (normalized.greetingText) sample.greeting_text = normalized.greetingText;
|
|
652
|
+
if (normalized.targetCountProvided) {
|
|
653
|
+
sample.target_count = normalized.targetCountPublicValue || (normalized.targetCountArg === "-1" ? "all" : normalized.targetCount);
|
|
654
|
+
} else if (missingFields.includes("target_count")) {
|
|
652
655
|
sample.target_count = "all";
|
|
653
656
|
}
|
|
654
657
|
return Object.keys(sample).length > 0 ? sample : null;
|
|
@@ -678,9 +681,10 @@ function buildBossChatCliArgs(command, input, resolvedConfig, runtimeLayout = nu
|
|
|
678
681
|
const normalized = normalizeBossChatStartInput(input);
|
|
679
682
|
args.push("--profile", normalized.profile);
|
|
680
683
|
if (normalized.job) args.push("--job", normalized.job);
|
|
681
|
-
if (normalized.startFrom) args.push("--start-from", normalized.startFrom);
|
|
682
|
-
if (normalized.criteria) args.push("--criteria", normalized.criteria);
|
|
683
|
-
if (normalized.
|
|
684
|
+
if (normalized.startFrom) args.push("--start-from", normalized.startFrom);
|
|
685
|
+
if (normalized.criteria) args.push("--criteria", normalized.criteria);
|
|
686
|
+
if (normalized.greetingText) args.push("--greeting", normalized.greetingText);
|
|
687
|
+
if (normalized.targetCountArg) args.push("--targetCount", normalized.targetCountArg);
|
|
684
688
|
args.push("--port", String(normalized.port || resolvedConfig.debugPort || 9222));
|
|
685
689
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
686
690
|
args.push("--apikey", resolvedConfig.apiKey);
|
|
@@ -702,12 +706,13 @@ function buildBossChatCliArgs(command, input, resolvedConfig, runtimeLayout = nu
|
|
|
702
706
|
args.push("--profile", normalized.profile);
|
|
703
707
|
if (normalized.dryRun) args.push("--dry-run");
|
|
704
708
|
if (normalized.noState) args.push("--no-state");
|
|
705
|
-
args.push("--job", normalized.job);
|
|
706
|
-
args.push("--start-from", normalized.startFrom);
|
|
707
|
-
args.push("--criteria", normalized.criteria);
|
|
708
|
-
if (normalized.
|
|
709
|
-
|
|
710
|
-
|
|
709
|
+
args.push("--job", normalized.job);
|
|
710
|
+
args.push("--start-from", normalized.startFrom);
|
|
711
|
+
args.push("--criteria", normalized.criteria);
|
|
712
|
+
if (normalized.greetingText) args.push("--greeting", normalized.greetingText);
|
|
713
|
+
if (normalized.targetCountArg) {
|
|
714
|
+
args.push("--targetCount", normalized.targetCountArg);
|
|
715
|
+
}
|
|
711
716
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
712
717
|
args.push("--apikey", resolvedConfig.apiKey);
|
|
713
718
|
args.push("--model", resolvedConfig.model);
|
package/src/cli.js
CHANGED
|
@@ -1377,7 +1377,7 @@ function printHelp() {
|
|
|
1377
1377
|
console.log("");
|
|
1378
1378
|
console.log("Run command:");
|
|
1379
1379
|
console.log(" boss-recommend-mcp run --instruction \"推荐页上筛选211男生,近14天没有,有大模型平台经验\" [--confirmation-json '{...}'] [--overrides-json '{...}'] [--follow-up-json '{...}']");
|
|
1380
|
-
console.log(" boss-recommend-mcp chat run --job \"算法工程师\" --start-from unread --criteria \"有 AI Agent 经验\" --targetCount 20 # 后台启动,不自动轮询");
|
|
1380
|
+
console.log(" boss-recommend-mcp chat run --job \"算法工程师\" --start-from unread --criteria \"有 AI Agent 经验\" --targetCount 20 [--greeting-text \"你好,方便发下简历吗?\"] # 后台启动,不自动轮询");
|
|
1381
1381
|
console.log(" boss-recommend-mcp config set --base-url <url> --api-key <key> --model <model> [--thinking-level off|low|medium|high|current] [--openai-organization <id>] [--openai-project <id>]");
|
|
1382
1382
|
console.log(" boss-recommend-mcp install --agent trae-cn");
|
|
1383
1383
|
console.log(" boss-recommend-mcp doctor --agent trae-cn --page-scope featured");
|
|
@@ -1481,11 +1481,18 @@ async function runPipelineOnce(options) {
|
|
|
1481
1481
|
}
|
|
1482
1482
|
|
|
1483
1483
|
function buildBossChatCliInput(options = {}) {
|
|
1484
|
+
const greetingTextRaw =
|
|
1485
|
+
options["greeting-text"]
|
|
1486
|
+
?? options.greeting_text
|
|
1487
|
+
?? options.greetingText
|
|
1488
|
+
?? options.greeting;
|
|
1489
|
+
const greetingText = typeof greetingTextRaw === "string" ? greetingTextRaw.trim() : undefined;
|
|
1484
1490
|
return {
|
|
1485
1491
|
profile: typeof options.profile === "string" ? options.profile.trim() : undefined,
|
|
1486
1492
|
job: typeof options.job === "string" ? options.job.trim() : undefined,
|
|
1487
1493
|
start_from: String(options["start-from"] || options.start_from || "").trim().toLowerCase() || undefined,
|
|
1488
1494
|
criteria: typeof options.criteria === "string" ? options.criteria.trim() : undefined,
|
|
1495
|
+
greeting_text: greetingText || undefined,
|
|
1489
1496
|
target_count: parseBossChatTargetCountOption(options.targetCount || options["target-count"] || options.target_count),
|
|
1490
1497
|
port: parsePositivePort(options.port),
|
|
1491
1498
|
dry_run: options["dry-run"] === true || options.dryRun === true,
|
package/src/index.js
CHANGED
|
@@ -437,6 +437,14 @@ function createRunInputSchema() {
|
|
|
437
437
|
type: "string",
|
|
438
438
|
enum: ["unread", "all"]
|
|
439
439
|
},
|
|
440
|
+
greeting_text: {
|
|
441
|
+
type: "string",
|
|
442
|
+
description: "可选,首条打招呼消息;未传时按 profile 历史值/默认值自动回退"
|
|
443
|
+
},
|
|
444
|
+
greetingText: {
|
|
445
|
+
type: "string",
|
|
446
|
+
description: "兼容字段;优先使用 greeting_text。可选首条打招呼消息"
|
|
447
|
+
},
|
|
440
448
|
target_count: createTargetCountInputSchema("boss-chat follow-up 本次处理人数上限;支持正整数、all 或 -1(扫到底)"),
|
|
441
449
|
dry_run: { type: "boolean" },
|
|
442
450
|
no_state: { type: "boolean" },
|
|
@@ -475,6 +483,14 @@ function createBossChatStartInputSchema({ requireFullInput = false } = {}) {
|
|
|
475
483
|
type: "string",
|
|
476
484
|
description: "boss-chat 的筛选 criteria"
|
|
477
485
|
},
|
|
486
|
+
greeting_text: {
|
|
487
|
+
type: "string",
|
|
488
|
+
description: "可选,首条打招呼消息;未传时按 profile 历史值/默认值自动回退"
|
|
489
|
+
},
|
|
490
|
+
greetingText: {
|
|
491
|
+
type: "string",
|
|
492
|
+
description: "兼容字段;优先使用 greeting_text。可选首条打招呼消息"
|
|
493
|
+
},
|
|
478
494
|
target_count: createTargetCountInputSchema("本次处理人数上限;支持正整数、all 或 -1(扫到底),也兼容 { value: \"all\" } 等包装对象"),
|
|
479
495
|
targetCount: createTargetCountInputSchema("兼容字段;优先使用 target_count。本次处理人数上限,支持正整数、all 或 -1(扫到底)"),
|
|
480
496
|
port: {
|
|
@@ -773,6 +789,18 @@ function validateBossChatStartArgs(args) {
|
|
|
773
789
|
return "criteria must be a non-empty string when provided";
|
|
774
790
|
}
|
|
775
791
|
}
|
|
792
|
+
if (
|
|
793
|
+
Object.prototype.hasOwnProperty.call(args, "greeting_text")
|
|
794
|
+
&& typeof args.greeting_text !== "string"
|
|
795
|
+
) {
|
|
796
|
+
return "greeting_text must be a string when provided";
|
|
797
|
+
}
|
|
798
|
+
if (
|
|
799
|
+
Object.prototype.hasOwnProperty.call(args, "greetingText")
|
|
800
|
+
&& typeof args.greetingText !== "string"
|
|
801
|
+
) {
|
|
802
|
+
return "greetingText must be a string when provided";
|
|
803
|
+
}
|
|
776
804
|
if (
|
|
777
805
|
Object.prototype.hasOwnProperty.call(args, "target_count")
|
|
778
806
|
|| Object.prototype.hasOwnProperty.call(args, "targetCount")
|
package/src/pipeline.js
CHANGED
|
@@ -476,6 +476,7 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
476
476
|
const explicitCriteria = normalizeText(raw.criteria);
|
|
477
477
|
const explicitStartFromRaw = normalizeText(raw.start_from).toLowerCase();
|
|
478
478
|
const explicitStartFrom = explicitStartFromRaw === "all" ? "all" : explicitStartFromRaw === "unread" ? "unread" : "";
|
|
479
|
+
const explicitGreetingText = normalizeText(raw.greeting_text || raw.greetingText);
|
|
479
480
|
const explicitTarget = normalizeFollowUpTargetCountInput(raw.target_count);
|
|
480
481
|
const explicitTargetCount = explicitTarget.launchValue;
|
|
481
482
|
|
|
@@ -493,6 +494,7 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
493
494
|
profile,
|
|
494
495
|
criteria: criteria || null,
|
|
495
496
|
start_from: startFrom || null,
|
|
497
|
+
greeting_text: explicitGreetingText || null,
|
|
496
498
|
target_count: targetCountSummaryValue,
|
|
497
499
|
dry_run: raw.dry_run === true,
|
|
498
500
|
no_state: raw.no_state === true,
|
|
@@ -572,6 +574,7 @@ function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
|
572
574
|
profile,
|
|
573
575
|
criteria: criteria || null,
|
|
574
576
|
start_from: startFrom || null,
|
|
577
|
+
greeting_text: explicitGreetingText || undefined,
|
|
575
578
|
target_count: targetCount,
|
|
576
579
|
dry_run: raw.dry_run === true,
|
|
577
580
|
no_state: raw.no_state === true,
|
|
@@ -619,6 +622,7 @@ function buildResolvedFollowUpChatInput(followUpChat, { selectedJob, debugPort }
|
|
|
619
622
|
job: selectedJob?.title || selectedJob?.label || selectedJob?.value || null,
|
|
620
623
|
start_from: followUpChat?.input?.start_from || null,
|
|
621
624
|
criteria: followUpChat?.input?.criteria || null,
|
|
625
|
+
greeting_text: followUpChat?.input?.greeting_text || null,
|
|
622
626
|
target_count: followUpChat?.input?.target_count || null,
|
|
623
627
|
port: Number.isFinite(debugPort) ? debugPort : null,
|
|
624
628
|
dry_run: followUpChat?.input?.dry_run === true,
|
|
@@ -639,6 +643,7 @@ function buildBossChatFollowUpStatus({ payload, runId, fallbackInput = null, sta
|
|
|
639
643
|
job: normalizeText(fallbackInput?.job) || null,
|
|
640
644
|
start_from: normalizeText(fallbackInput?.start_from) || null,
|
|
641
645
|
criteria: normalizeText(fallbackInput?.criteria) || null,
|
|
646
|
+
greeting_text: normalizeText(fallbackInput?.greeting_text) || null,
|
|
642
647
|
target_count: normalizePipelineTargetCountValue(fallbackInput?.target_count),
|
|
643
648
|
port: parsePositiveIntegerValue(fallbackInput?.port),
|
|
644
649
|
progress: {
|
|
@@ -995,6 +1000,7 @@ async function runBossChatFollowUpPhase({
|
|
|
995
1000
|
profile: resolvedChatInput.profile,
|
|
996
1001
|
job: resolvedChatInput.job,
|
|
997
1002
|
start_from: resolvedChatInput.start_from,
|
|
1003
|
+
greeting_text: resolvedChatInput.greeting_text,
|
|
998
1004
|
target_count: resolvedChatInput.target_count
|
|
999
1005
|
});
|
|
1000
1006
|
|
|
@@ -1006,6 +1012,7 @@ async function runBossChatFollowUpPhase({
|
|
|
1006
1012
|
job: resolvedChatInput.job,
|
|
1007
1013
|
start_from: resolvedChatInput.start_from,
|
|
1008
1014
|
criteria: resolvedChatInput.criteria,
|
|
1015
|
+
greeting_text: resolvedChatInput.greeting_text,
|
|
1009
1016
|
target_count: resolvedChatInput.target_count,
|
|
1010
1017
|
port: resolvedChatInput.port,
|
|
1011
1018
|
dry_run: resolvedChatInput.dry_run,
|
package/src/test-boss-chat.js
CHANGED
|
@@ -287,16 +287,17 @@ async function testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli() {
|
|
|
287
287
|
assert.equal(stateAfterPrepare.last_prepare_args["llm-timeout-ms"], "65000");
|
|
288
288
|
assert.equal(stateAfterPrepare.last_prepare_args["llm-max-retries"], "4");
|
|
289
289
|
|
|
290
|
-
const started = await startBossChatRun({
|
|
291
|
-
workspaceRoot,
|
|
292
|
-
input: {
|
|
293
|
-
profile: "default",
|
|
294
|
-
job: "算法工程师",
|
|
295
|
-
start_from: "unread",
|
|
296
|
-
criteria: "有 AI Agent 经验",
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
290
|
+
const started = await startBossChatRun({
|
|
291
|
+
workspaceRoot,
|
|
292
|
+
input: {
|
|
293
|
+
profile: "default",
|
|
294
|
+
job: "算法工程师",
|
|
295
|
+
start_from: "unread",
|
|
296
|
+
criteria: "有 AI Agent 经验",
|
|
297
|
+
greeting_text: "你好,方便发下简历吗?",
|
|
298
|
+
target_count: 2
|
|
299
|
+
}
|
|
300
|
+
});
|
|
300
301
|
assert.equal(started.status, "ACCEPTED");
|
|
301
302
|
assert.equal(Boolean(started.run_id), true);
|
|
302
303
|
|
|
@@ -304,9 +305,10 @@ async function testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli() {
|
|
|
304
305
|
assert.equal(stateAfterStart.last_start_args.profile, "default");
|
|
305
306
|
assert.equal(stateAfterStart.last_start_args["data-dir"], getTestChatDataDir(workspaceRoot));
|
|
306
307
|
assert.equal(stateAfterStart.last_start_args.job, "算法工程师");
|
|
307
|
-
assert.equal(stateAfterStart.last_start_args["start-from"], "unread");
|
|
308
|
-
assert.equal(stateAfterStart.last_start_args.criteria, "有 AI Agent 经验");
|
|
309
|
-
assert.equal(stateAfterStart.last_start_args.
|
|
308
|
+
assert.equal(stateAfterStart.last_start_args["start-from"], "unread");
|
|
309
|
+
assert.equal(stateAfterStart.last_start_args.criteria, "有 AI Agent 经验");
|
|
310
|
+
assert.equal(stateAfterStart.last_start_args.greeting, "你好,方便发下简历吗?");
|
|
311
|
+
assert.equal(stateAfterStart.last_start_args.targetCount, "2");
|
|
310
312
|
assert.equal(stateAfterStart.last_start_args.baseurl, "https://api.example.com/v1");
|
|
311
313
|
assert.equal(stateAfterStart.last_start_args.apikey, "sk-test-key");
|
|
312
314
|
assert.equal(stateAfterStart.last_start_args.model, "gpt-4.1-mini");
|
|
@@ -908,15 +910,20 @@ async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
|
908
910
|
method: "tools/list",
|
|
909
911
|
params: {}
|
|
910
912
|
}, workspaceRoot);
|
|
911
|
-
const tools = toolsResponse.result.tools;
|
|
912
|
-
const
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
assert.
|
|
916
|
-
assert.
|
|
917
|
-
assert.equal(startToolSchema.
|
|
918
|
-
assert.equal(startToolSchema.properties.
|
|
919
|
-
assert.equal(startToolSchema.
|
|
913
|
+
const tools = toolsResponse.result.tools;
|
|
914
|
+
const runToolSchema = tools.find((item) => item.name === "start_recommend_pipeline_run").inputSchema;
|
|
915
|
+
const prepareToolSchema = tools.find((item) => item.name === TOOL_BOSS_CHAT_PREPARE_RUN).inputSchema;
|
|
916
|
+
const startToolSchema = tools.find((item) => item.name === TOOL_BOSS_CHAT_START_RUN).inputSchema;
|
|
917
|
+
assert.equal(prepareToolSchema.required, undefined);
|
|
918
|
+
assert.deepEqual(startToolSchema.required, ["job", "start_from", "criteria"]);
|
|
919
|
+
assert.equal(typeof startToolSchema.properties.greeting_text, "object");
|
|
920
|
+
assert.equal(typeof startToolSchema.properties.greetingText, "object");
|
|
921
|
+
assert.equal(startToolSchema.anyOf.some((item) => item.required?.includes("target_count")), true);
|
|
922
|
+
assert.equal(startToolSchema.anyOf.some((item) => item.required?.includes("targetCount")), true);
|
|
923
|
+
assert.equal(startToolSchema.properties.target_count.examples.includes("all"), true);
|
|
924
|
+
assert.equal(startToolSchema.examples.some((item) => item.target_count === "all"), true);
|
|
925
|
+
assert.equal(typeof runToolSchema.properties.follow_up.properties.chat.properties.greeting_text, "object");
|
|
926
|
+
assert.equal(typeof runToolSchema.properties.follow_up.properties.chat.properties.greetingText, "object");
|
|
920
927
|
|
|
921
928
|
const prepared = await callTool(workspaceRoot, TOOL_BOSS_CHAT_PREPARE_RUN, {}, 101);
|
|
922
929
|
assert.equal(prepared.status, "NEED_INPUT");
|
|
@@ -960,13 +967,20 @@ async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
|
960
967
|
assert.equal(invalidTargetOnly.next_call_example.target_count, "all");
|
|
961
968
|
assert.equal(invalidTargetOnly.recommended_argument_patch.target_count, "all");
|
|
962
969
|
|
|
963
|
-
const invalidStartResponse = await handleRequest(
|
|
964
|
-
makeToolCall(11, TOOL_BOSS_CHAT_START_RUN, {
|
|
965
|
-
start_from: "invalid-value"
|
|
970
|
+
const invalidStartResponse = await handleRequest(
|
|
971
|
+
makeToolCall(11, TOOL_BOSS_CHAT_START_RUN, {
|
|
972
|
+
start_from: "invalid-value"
|
|
966
973
|
}),
|
|
967
974
|
workspaceRoot
|
|
968
|
-
);
|
|
969
|
-
assert.equal(invalidStartResponse.error.code, -32602);
|
|
975
|
+
);
|
|
976
|
+
assert.equal(invalidStartResponse.error.code, -32602);
|
|
977
|
+
const invalidGreetingResponse = await handleRequest(
|
|
978
|
+
makeToolCall(1112, TOOL_BOSS_CHAT_START_RUN, {
|
|
979
|
+
greeting_text: 123
|
|
980
|
+
}),
|
|
981
|
+
workspaceRoot
|
|
982
|
+
);
|
|
983
|
+
assert.equal(invalidGreetingResponse.error.code, -32602);
|
|
970
984
|
|
|
971
985
|
const invalidGetResponse = await handleRequest(
|
|
972
986
|
makeToolCall(12, TOOL_BOSS_CHAT_GET_RUN, {}),
|
|
@@ -977,13 +991,15 @@ async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
|
977
991
|
const health = await callTool(workspaceRoot, TOOL_BOSS_CHAT_HEALTH_CHECK, {}, 13);
|
|
978
992
|
assert.equal(health.status, "OK");
|
|
979
993
|
|
|
980
|
-
const started = await callTool(workspaceRoot, TOOL_BOSS_CHAT_START_RUN, {
|
|
981
|
-
job: "算法工程师",
|
|
982
|
-
start_from: "unread",
|
|
983
|
-
criteria: "有 AI Agent 经验",
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
994
|
+
const started = await callTool(workspaceRoot, TOOL_BOSS_CHAT_START_RUN, {
|
|
995
|
+
job: "算法工程师",
|
|
996
|
+
start_from: "unread",
|
|
997
|
+
criteria: "有 AI Agent 经验",
|
|
998
|
+
greeting_text: "您好,方便投递一份简历吗?",
|
|
999
|
+
target_count: 2
|
|
1000
|
+
}, 14);
|
|
1001
|
+
assert.equal(started.status, "ACCEPTED");
|
|
1002
|
+
assert.equal(readStubState(workspaceRoot).last_start_args.greeting, "您好,方便投递一份简历吗?");
|
|
987
1003
|
|
|
988
1004
|
const startedAll = await callTool(workspaceRoot, TOOL_BOSS_CHAT_START_RUN, {
|
|
989
1005
|
job: "算法工程师",
|
|
@@ -1033,15 +1049,17 @@ async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
|
1033
1049
|
async function testBossChatCliShouldSupportRunAndFollowUpParsing() {
|
|
1034
1050
|
const followUpJson = cliTestables.getRunFollowUp({
|
|
1035
1051
|
"follow-up-json": JSON.stringify({
|
|
1036
|
-
chat: {
|
|
1037
|
-
criteria: "有 AI Agent 经验",
|
|
1038
|
-
start_from: "unread",
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
assert.equal(followUpJson.chat.
|
|
1052
|
+
chat: {
|
|
1053
|
+
criteria: "有 AI Agent 经验",
|
|
1054
|
+
start_from: "unread",
|
|
1055
|
+
greeting_text: "您好,方便发下简历吗?",
|
|
1056
|
+
target_count: 2
|
|
1057
|
+
}
|
|
1058
|
+
})
|
|
1059
|
+
});
|
|
1060
|
+
assert.equal(followUpJson.chat.criteria, "有 AI Agent 经验");
|
|
1061
|
+
assert.equal(followUpJson.chat.greeting_text, "您好,方便发下简历吗?");
|
|
1062
|
+
assert.equal(followUpJson.chat.target_count, 2);
|
|
1045
1063
|
|
|
1046
1064
|
const tempFile = path.join(os.tmpdir(), `boss-recommend-follow-up-${Date.now()}.json`);
|
|
1047
1065
|
fs.writeFileSync(tempFile, JSON.stringify({
|
|
@@ -1074,18 +1092,20 @@ async function testBossChatCliShouldSupportRunAndFollowUpParsing() {
|
|
|
1074
1092
|
const logs = await captureConsoleLogs(async () => {
|
|
1075
1093
|
await cliTestables.runBossChatCliCommand("run", {
|
|
1076
1094
|
"workspace-root": workspaceRoot,
|
|
1077
|
-
job: "算法工程师",
|
|
1078
|
-
"start-from": "unread",
|
|
1079
|
-
criteria: "有 AI Agent 经验",
|
|
1080
|
-
|
|
1081
|
-
|
|
1095
|
+
job: "算法工程师",
|
|
1096
|
+
"start-from": "unread",
|
|
1097
|
+
criteria: "有 AI Agent 经验",
|
|
1098
|
+
"greeting-text": "您好,方便发下简历吗?",
|
|
1099
|
+
targetCount: "2"
|
|
1100
|
+
});
|
|
1082
1101
|
});
|
|
1083
1102
|
assert.equal(logs.length > 0, true);
|
|
1084
|
-
const payload = JSON.parse(logs[0]);
|
|
1085
|
-
assert.equal(payload.status, "ACCEPTED");
|
|
1086
|
-
assert.equal(typeof payload.run_id, "string");
|
|
1087
|
-
const state = readStubState(workspaceRoot);
|
|
1088
|
-
assert.equal(state.
|
|
1103
|
+
const payload = JSON.parse(logs[0]);
|
|
1104
|
+
assert.equal(payload.status, "ACCEPTED");
|
|
1105
|
+
assert.equal(typeof payload.run_id, "string");
|
|
1106
|
+
const state = readStubState(workspaceRoot);
|
|
1107
|
+
assert.equal(state.last_start_args.greeting, "您好,方便发下简历吗?");
|
|
1108
|
+
assert.equal(state.get_calls[payload.run_id] || 0, 0);
|
|
1089
1109
|
|
|
1090
1110
|
await captureConsoleLogs(async () => {
|
|
1091
1111
|
await cliTestables.runBossChatCliCommand("run", {
|
|
@@ -1179,14 +1199,106 @@ async function testVendorBossChatCliShouldRetryJobListDuringPromptRunProfile() {
|
|
|
1179
1199
|
overrides: {
|
|
1180
1200
|
jobSelection: "AI应用开发工程师(2026) _ 杭州",
|
|
1181
1201
|
startFrom: "unread",
|
|
1182
|
-
screeningCriteria: "小样本联通性验证",
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
assert.equal(profile.
|
|
1188
|
-
assert.equal(profile.
|
|
1189
|
-
|
|
1202
|
+
screeningCriteria: "小样本联通性验证",
|
|
1203
|
+
greetingText: "你好,方便发下简历吗?",
|
|
1204
|
+
targetCount: 1,
|
|
1205
|
+
},
|
|
1206
|
+
});
|
|
1207
|
+
assert.equal(profile.jobSelection.label, "AI应用开发工程师(2026) _ 杭州");
|
|
1208
|
+
assert.equal(profile.startFrom, "unread");
|
|
1209
|
+
assert.equal(profile.greetingText, "你好,方便发下简历吗?");
|
|
1210
|
+
assert.equal(profile.targetCount, 1);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
async function testVendorBossChatCliShouldUseGreetingFallbacksInPromptRunProfile() {
|
|
1214
|
+
const page = {
|
|
1215
|
+
async ensureOnChatPage() {
|
|
1216
|
+
return {
|
|
1217
|
+
href: "https://www.zhipin.com/web/chat/index",
|
|
1218
|
+
hasListContainer: true,
|
|
1219
|
+
listItemCount: 8,
|
|
1220
|
+
};
|
|
1221
|
+
},
|
|
1222
|
+
async listJobs() {
|
|
1223
|
+
return [
|
|
1224
|
+
{
|
|
1225
|
+
value: "job-1",
|
|
1226
|
+
label: "算法工程师 _ 杭州",
|
|
1227
|
+
active: true,
|
|
1228
|
+
},
|
|
1229
|
+
];
|
|
1230
|
+
},
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
const fromLastProfile = await vendorCliTestables.promptRunProfile({
|
|
1234
|
+
page,
|
|
1235
|
+
persistentProfile: {
|
|
1236
|
+
greetingText: "上次招呼语",
|
|
1237
|
+
llm: {
|
|
1238
|
+
baseUrl: "https://api.example.com/v1",
|
|
1239
|
+
apiKey: "sk-test-key",
|
|
1240
|
+
model: "gpt-4.1-mini",
|
|
1241
|
+
},
|
|
1242
|
+
chrome: {
|
|
1243
|
+
port: 9222,
|
|
1244
|
+
},
|
|
1245
|
+
runtime: {},
|
|
1246
|
+
},
|
|
1247
|
+
overrides: {
|
|
1248
|
+
jobSelection: "算法工程师 _ 杭州",
|
|
1249
|
+
startFrom: "unread",
|
|
1250
|
+
screeningCriteria: "有 AI Agent 经验",
|
|
1251
|
+
targetCount: 1,
|
|
1252
|
+
},
|
|
1253
|
+
});
|
|
1254
|
+
assert.equal(fromLastProfile.greetingText, "上次招呼语");
|
|
1255
|
+
|
|
1256
|
+
const fromExplicitOverride = await vendorCliTestables.promptRunProfile({
|
|
1257
|
+
page,
|
|
1258
|
+
persistentProfile: {
|
|
1259
|
+
greetingText: "上次招呼语",
|
|
1260
|
+
llm: {
|
|
1261
|
+
baseUrl: "https://api.example.com/v1",
|
|
1262
|
+
apiKey: "sk-test-key",
|
|
1263
|
+
model: "gpt-4.1-mini",
|
|
1264
|
+
},
|
|
1265
|
+
chrome: {
|
|
1266
|
+
port: 9222,
|
|
1267
|
+
},
|
|
1268
|
+
runtime: {},
|
|
1269
|
+
},
|
|
1270
|
+
overrides: {
|
|
1271
|
+
jobSelection: "算法工程师 _ 杭州",
|
|
1272
|
+
startFrom: "unread",
|
|
1273
|
+
screeningCriteria: "有 AI Agent 经验",
|
|
1274
|
+
greetingText: "本次新招呼语",
|
|
1275
|
+
targetCount: 1,
|
|
1276
|
+
},
|
|
1277
|
+
});
|
|
1278
|
+
assert.equal(fromExplicitOverride.greetingText, "本次新招呼语");
|
|
1279
|
+
|
|
1280
|
+
const fromBuiltInDefault = await vendorCliTestables.promptRunProfile({
|
|
1281
|
+
page,
|
|
1282
|
+
persistentProfile: {
|
|
1283
|
+
llm: {
|
|
1284
|
+
baseUrl: "https://api.example.com/v1",
|
|
1285
|
+
apiKey: "sk-test-key",
|
|
1286
|
+
model: "gpt-4.1-mini",
|
|
1287
|
+
},
|
|
1288
|
+
chrome: {
|
|
1289
|
+
port: 9222,
|
|
1290
|
+
},
|
|
1291
|
+
runtime: {},
|
|
1292
|
+
},
|
|
1293
|
+
overrides: {
|
|
1294
|
+
jobSelection: "算法工程师 _ 杭州",
|
|
1295
|
+
startFrom: "unread",
|
|
1296
|
+
screeningCriteria: "有 AI Agent 经验",
|
|
1297
|
+
targetCount: 1,
|
|
1298
|
+
},
|
|
1299
|
+
});
|
|
1300
|
+
assert.equal(fromBuiltInDefault.greetingText, "Hi同学,能麻烦发下简历吗?");
|
|
1301
|
+
}
|
|
1190
1302
|
|
|
1191
1303
|
function testCliShouldPinInstalledPackageVersionInGeneratedMcpConfig() {
|
|
1192
1304
|
const installedSpecifier = cliTestables.getDefaultMcpPackageSpecifier({
|
|
@@ -1213,18 +1325,21 @@ function testCliShouldPinInstalledPackageVersionInGeneratedMcpConfig() {
|
|
|
1213
1325
|
assert.equal(launchConfig.args[0], "-y");
|
|
1214
1326
|
}
|
|
1215
1327
|
|
|
1216
|
-
function testVendorBossChatCliShouldParseSharedLlmTransportArgs() {
|
|
1217
|
-
const parsed = vendorCliTestables.parseArgs([
|
|
1218
|
-
"start-run",
|
|
1219
|
-
"--llm-timeout-ms",
|
|
1220
|
-
"70000",
|
|
1221
|
-
"--llm-max-retries",
|
|
1222
|
-
"5",
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
assert.equal(parsed.
|
|
1227
|
-
|
|
1328
|
+
function testVendorBossChatCliShouldParseSharedLlmTransportArgs() {
|
|
1329
|
+
const parsed = vendorCliTestables.parseArgs([
|
|
1330
|
+
"start-run",
|
|
1331
|
+
"--llm-timeout-ms",
|
|
1332
|
+
"70000",
|
|
1333
|
+
"--llm-max-retries",
|
|
1334
|
+
"5",
|
|
1335
|
+
"--greeting",
|
|
1336
|
+
"您好,方便发下简历吗?",
|
|
1337
|
+
]);
|
|
1338
|
+
assert.equal(parsed.command, "start-run");
|
|
1339
|
+
assert.equal(parsed.overrides.llm.timeoutMs, 70000);
|
|
1340
|
+
assert.equal(parsed.overrides.llm.maxRetries, 5);
|
|
1341
|
+
assert.equal(parsed.overrides.greetingText, "您好,方便发下简历吗?");
|
|
1342
|
+
}
|
|
1228
1343
|
|
|
1229
1344
|
function testBossChatLlmParserShouldAcceptMinimalDecisionJson() {
|
|
1230
1345
|
const parsed = parseLlmJson(
|
|
@@ -3049,8 +3164,9 @@ async function main() {
|
|
|
3049
3164
|
testVendorBossChatCliShouldResolveExplicitDataDir();
|
|
3050
3165
|
testVendorBossChatCliShouldUseRecommendHomeForDefaultDataDir();
|
|
3051
3166
|
await testVendorBossChatCliShouldWaitForHydratedChatShell();
|
|
3052
|
-
await testVendorBossChatCliShouldRetryJobListDuringPromptRunProfile();
|
|
3053
|
-
|
|
3167
|
+
await testVendorBossChatCliShouldRetryJobListDuringPromptRunProfile();
|
|
3168
|
+
await testVendorBossChatCliShouldUseGreetingFallbacksInPromptRunProfile();
|
|
3169
|
+
testCliShouldPinInstalledPackageVersionInGeneratedMcpConfig();
|
|
3054
3170
|
testVendorBossChatCliShouldParseSharedLlmTransportArgs();
|
|
3055
3171
|
testBossChatLlmParserShouldAcceptMinimalDecisionJson();
|
|
3056
3172
|
testBossChatLlmParserShouldAcceptPlainPassFailText();
|