@reconcrap/boss-recommend-mcp 1.3.36 → 1.3.38
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 +139 -14
- package/src/test-boss-chat.js +191 -75
- package/src/test-pipeline.js +123 -4
- 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/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();
|
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
|
{
|
|
@@ -2343,6 +2404,62 @@ async function testFinalReviewShouldIncludeFollowUpChatSummary() {
|
|
|
2343
2404
|
assert.equal(finalReview?.value?.follow_up?.chat?.target_count, 5);
|
|
2344
2405
|
}
|
|
2345
2406
|
|
|
2407
|
+
async function testFollowUpChatGreetingTextShouldPassThroughWhenProvided() {
|
|
2408
|
+
let capturedChatInput = null;
|
|
2409
|
+
const result = await runRecommendPipeline(
|
|
2410
|
+
{
|
|
2411
|
+
workspaceRoot: process.cwd(),
|
|
2412
|
+
instruction: "test",
|
|
2413
|
+
confirmation: createJobConfirmedConfirmation(),
|
|
2414
|
+
overrides: {},
|
|
2415
|
+
followUp: createFollowUpChat({ greeting_text: "您好,方便发下简历吗?" })
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
parseRecommendInstruction: () => createParsed(),
|
|
2419
|
+
runPipelinePreflight: () => ({ ok: true, checks: [], debug_port: 9555 }),
|
|
2420
|
+
ensureBossRecommendPageReady: async () => ({ ok: true, state: "RECOMMEND_READY", page_state: {} }),
|
|
2421
|
+
listRecommendJobs: async () => createJobListResult(),
|
|
2422
|
+
runRecommendSearchCli: async () => ({
|
|
2423
|
+
ok: true,
|
|
2424
|
+
summary: {
|
|
2425
|
+
candidate_count: 6,
|
|
2426
|
+
applied_filters: {},
|
|
2427
|
+
page_state: {}
|
|
2428
|
+
}
|
|
2429
|
+
}),
|
|
2430
|
+
runRecommendScreenCli: async () => ({
|
|
2431
|
+
ok: true,
|
|
2432
|
+
summary: {
|
|
2433
|
+
processed_count: 6,
|
|
2434
|
+
passed_count: 2,
|
|
2435
|
+
skipped_count: 0
|
|
2436
|
+
}
|
|
2437
|
+
}),
|
|
2438
|
+
startBossChatRun: async ({ input }) => {
|
|
2439
|
+
capturedChatInput = input;
|
|
2440
|
+
return {
|
|
2441
|
+
status: "ACCEPTED",
|
|
2442
|
+
run_id: "chat-run-greeting",
|
|
2443
|
+
message: "chat started"
|
|
2444
|
+
};
|
|
2445
|
+
},
|
|
2446
|
+
getBossChatRun: async () => ({
|
|
2447
|
+
status: "COMPLETED",
|
|
2448
|
+
run: {
|
|
2449
|
+
runId: "chat-run-greeting",
|
|
2450
|
+
state: "completed",
|
|
2451
|
+
lastMessage: "chat completed",
|
|
2452
|
+
progress: { processed: 2, matched: 2 }
|
|
2453
|
+
}
|
|
2454
|
+
})
|
|
2455
|
+
}
|
|
2456
|
+
);
|
|
2457
|
+
|
|
2458
|
+
assert.equal(result.status, "COMPLETED");
|
|
2459
|
+
assert.equal(capturedChatInput?.greeting_text, "您好,方便发下简历吗?");
|
|
2460
|
+
assert.equal(result.follow_up?.chat?.input?.greeting_text, "您好,方便发下简历吗?");
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2346
2463
|
async function testCompletedPipelineShouldRunChatFollowUp() {
|
|
2347
2464
|
let getChatRunCalls = 0;
|
|
2348
2465
|
const result = await runRecommendPipeline(
|
|
@@ -2574,8 +2691,10 @@ async function main() {
|
|
|
2574
2691
|
await testFollowUpChatMissingTargetCountShouldNeedInput();
|
|
2575
2692
|
await testFollowUpChatInvalidTargetCountShouldNeedInputWithDiagnostics();
|
|
2576
2693
|
await testFinalReviewShouldIncludeFollowUpChatSummary();
|
|
2694
|
+
await testFollowUpChatGreetingTextShouldPassThroughWhenProvided();
|
|
2577
2695
|
await testCompletedPipelineShouldRunChatFollowUp();
|
|
2578
2696
|
await testFollowUpChatAllTargetCountShouldLaunchUnlimited();
|
|
2697
|
+
await testFollowUpChatPassedTargetCountShouldLaunchWithPassedCount();
|
|
2579
2698
|
await testCompletedPipelineShouldFailWhenChatLaunchFails();
|
|
2580
2699
|
await testCompletedPipelineShouldFailWhenChatRunFails();
|
|
2581
2700
|
console.log("pipeline tests passed");
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { mkdir } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { isDomProfileConsistentWithCard, NETWORK_RESUME_RETRY_WAIT_MS } from './services/resume-network.js';
|
|
5
|
-
import {
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { isDomProfileConsistentWithCard, NETWORK_RESUME_RETRY_WAIT_MS } from './services/resume-network.js';
|
|
5
|
+
import { DEFAULT_GREETING_TEXT } from './services/profile-store.js';
|
|
6
|
+
import { createCustomerAliases, createCustomerKey } from './utils/customer-key.js';
|
|
6
7
|
|
|
7
8
|
function runToken(date = new Date()) {
|
|
8
9
|
return date.toISOString().replace(/[:.]/g, '-');
|
|
@@ -37,12 +38,16 @@ function shouldContinue(summary, targetCount) {
|
|
|
37
38
|
return summary.inspected < targetCount;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function hasResumeRequestSentMessage(state = {}) {
|
|
41
|
-
const lastText = normalizeText(state?.lastText || '');
|
|
42
|
-
const recent = Array.isArray(state?.recent) ? state.recent : [];
|
|
43
|
-
if (lastText.includes('简历请求已发送')) return true;
|
|
44
|
-
return recent.some((item) => normalizeText(item).includes('简历请求已发送'));
|
|
45
|
-
}
|
|
41
|
+
function hasResumeRequestSentMessage(state = {}) {
|
|
42
|
+
const lastText = normalizeText(state?.lastText || '');
|
|
43
|
+
const recent = Array.isArray(state?.recent) ? state.recent : [];
|
|
44
|
+
if (lastText.includes('简历请求已发送')) return true;
|
|
45
|
+
return recent.some((item) => normalizeText(item).includes('简历请求已发送'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveGreetingText(profile = {}) {
|
|
49
|
+
return normalizeText(profile?.greetingText || '') || DEFAULT_GREETING_TEXT;
|
|
50
|
+
}
|
|
46
51
|
|
|
47
52
|
const CANDIDATE_LIST_WAIT_AFTER_CONTEXT_MS = 5000;
|
|
48
53
|
const CANDIDATE_LIST_WAIT_POLL_MS = 500;
|
|
@@ -1323,13 +1328,13 @@ export class BossChatApp {
|
|
|
1323
1328
|
return baseResult;
|
|
1324
1329
|
}
|
|
1325
1330
|
|
|
1326
|
-
const greetingText =
|
|
1331
|
+
const greetingText = resolveGreetingText(profile);
|
|
1327
1332
|
this.logger.log(`候选人通过,先发送消息:${greetingText}`);
|
|
1328
1333
|
await this.checkpoint();
|
|
1329
|
-
const editorState = await this.page.setEditorMessage(greetingText);
|
|
1330
|
-
if (!
|
|
1331
|
-
throw new Error('CHAT_EDITOR_MESSAGE_MISMATCH');
|
|
1332
|
-
}
|
|
1334
|
+
const editorState = await this.page.setEditorMessage(greetingText);
|
|
1335
|
+
if (!normalizeText(editorState?.value || '').includes(normalizeText(greetingText))) {
|
|
1336
|
+
throw new Error('CHAT_EDITOR_MESSAGE_MISMATCH');
|
|
1337
|
+
}
|
|
1333
1338
|
this.logger.log(
|
|
1334
1339
|
`招呼语写入输入框:activeSubmit=${Boolean(editorState?.activeSubmit)} | valueLen=${String(editorState?.value || '').length}`,
|
|
1335
1340
|
);
|