@reconcrap/boss-recommend-mcp 1.2.10 → 1.3.1
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 +82 -1
- package/package.json +2 -1
- package/skills/boss-chat/README.md +5 -0
- package/skills/boss-chat/SKILL.md +69 -0
- package/skills/boss-recommend-pipeline/SKILL.md +40 -4
- package/src/adapters.js +19 -5
- package/src/boss-chat.js +436 -0
- package/src/cli.js +294 -129
- package/src/index.js +459 -108
- package/src/pipeline.js +605 -8
- package/src/run-state.js +5 -0
- package/src/test-adapters-runtime.js +69 -0
- package/src/test-boss-chat.js +399 -0
- package/src/test-index-async.js +238 -4
- package/src/test-pipeline.js +408 -1
- package/vendor/boss-chat-cli/README.md +134 -0
- package/vendor/boss-chat-cli/package.json +53 -0
- package/vendor/boss-chat-cli/src/app.js +783 -0
- package/vendor/boss-chat-cli/src/browser/chat-page.js +2698 -0
- package/vendor/boss-chat-cli/src/cli.js +1350 -0
- package/vendor/boss-chat-cli/src/mcp/server.js +149 -0
- package/vendor/boss-chat-cli/src/mcp/tool-runtime.js +193 -0
- package/vendor/boss-chat-cli/src/runtime/async-run-state.js +260 -0
- package/vendor/boss-chat-cli/src/runtime/interaction.js +102 -0
- package/vendor/boss-chat-cli/src/runtime/run-control.js +102 -0
- package/vendor/boss-chat-cli/src/services/chrome-client.js +97 -0
- package/vendor/boss-chat-cli/src/services/llm.js +352 -0
- package/vendor/boss-chat-cli/src/services/profile-store.js +157 -0
- package/vendor/boss-chat-cli/src/services/report-store.js +19 -0
- package/vendor/boss-chat-cli/src/services/resume-capture.js +554 -0
- package/vendor/boss-chat-cli/src/services/state-store.js +217 -0
- package/vendor/boss-chat-cli/src/utils/customer-key.js +82 -0
- package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +902 -56
- package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +387 -1
package/src/pipeline.js
CHANGED
|
@@ -13,6 +13,13 @@ import {
|
|
|
13
13
|
runRecommendScreenCli,
|
|
14
14
|
switchRecommendTab
|
|
15
15
|
} from "./adapters.js";
|
|
16
|
+
import {
|
|
17
|
+
cancelBossChatRun,
|
|
18
|
+
getBossChatRun,
|
|
19
|
+
pauseBossChatRun,
|
|
20
|
+
resumeBossChatRun,
|
|
21
|
+
startBossChatRun
|
|
22
|
+
} from "./boss-chat.js";
|
|
16
23
|
|
|
17
24
|
const FORCED_RECENT_NOT_VIEW_ON_SCREEN_RECOVERY = "近14天没有";
|
|
18
25
|
const MAX_SCREEN_AUTO_RECOVERY_ATTEMPTS = 5;
|
|
@@ -20,6 +27,7 @@ const MAX_SEARCH_NO_IFRAME_RETRY_ATTEMPTS = 1;
|
|
|
20
27
|
const SEARCH_NO_IFRAME_RETRY_DELAY_MS = 1200;
|
|
21
28
|
const MAX_SEARCH_FILTER_AUTO_RETRY_ATTEMPTS = 2;
|
|
22
29
|
const SEARCH_FILTER_AUTO_RETRY_DELAY_MS = 1200;
|
|
30
|
+
const BOSS_CHAT_FOLLOW_UP_POLL_MS = 1500;
|
|
23
31
|
const SEARCH_FILTER_RETRY_TOKENS = [
|
|
24
32
|
"FILTER_CONFIRM_FAILED",
|
|
25
33
|
"FILTER_DOM_CLASS_VERIFY_FAILED",
|
|
@@ -53,6 +61,11 @@ function normalizeText(value) {
|
|
|
53
61
|
return String(value || "").replace(/\s+/g, " ").trim();
|
|
54
62
|
}
|
|
55
63
|
|
|
64
|
+
function parsePositiveIntegerValue(value) {
|
|
65
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
66
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
function sleep(ms) {
|
|
57
70
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
58
71
|
}
|
|
@@ -331,11 +344,12 @@ function buildNeedInputResponse(parsedResult) {
|
|
|
331
344
|
selected_page: parsedResult.proposed_page_scope || parsedResult.page_scope || "recommend",
|
|
332
345
|
search_params: parsedResult.searchParams,
|
|
333
346
|
screen_params: parsedResult.screenParams,
|
|
347
|
+
follow_up: parsedResult.follow_up || null,
|
|
334
348
|
pending_questions: parsedResult.pending_questions,
|
|
335
349
|
review: parsedResult.review,
|
|
336
350
|
error: {
|
|
337
351
|
code: "MISSING_REQUIRED_FIELDS",
|
|
338
|
-
message:
|
|
352
|
+
message: buildNeedInputMessage(parsedResult.missing_fields),
|
|
339
353
|
retryable: true
|
|
340
354
|
}
|
|
341
355
|
};
|
|
@@ -353,20 +367,203 @@ function buildNeedConfirmationResponse(parsedResult) {
|
|
|
353
367
|
post_action: parsedResult.proposed_post_action || parsedResult.screenParams.post_action,
|
|
354
368
|
max_greet_count: parsedResult.proposed_max_greet_count || parsedResult.screenParams.max_greet_count
|
|
355
369
|
},
|
|
370
|
+
follow_up: parsedResult.follow_up || null,
|
|
356
371
|
pending_questions: parsedResult.pending_questions,
|
|
357
372
|
review: parsedResult.review
|
|
358
373
|
};
|
|
359
374
|
}
|
|
360
375
|
|
|
361
|
-
function
|
|
376
|
+
function normalizeFollowUpChatInput(followUp = null, defaults = null) {
|
|
377
|
+
const raw = followUp?.chat;
|
|
378
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
379
|
+
return {
|
|
380
|
+
requested: false,
|
|
381
|
+
missing_fields: [],
|
|
382
|
+
pending_questions: [],
|
|
383
|
+
summary: null,
|
|
384
|
+
input: null
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const defaultCriteria = normalizeText(defaults?.criteria || "");
|
|
389
|
+
const defaultStartFromRaw = normalizeText(defaults?.start_from || "").toLowerCase();
|
|
390
|
+
const defaultStartFrom = defaultStartFromRaw === "all" ? "all" : "unread";
|
|
391
|
+
const defaultTargetCount = parsePositiveIntegerValue(defaults?.target_count);
|
|
392
|
+
|
|
393
|
+
const explicitCriteria = normalizeText(raw.criteria);
|
|
394
|
+
const explicitStartFromRaw = normalizeText(raw.start_from).toLowerCase();
|
|
395
|
+
const explicitStartFrom = explicitStartFromRaw === "all" ? "all" : explicitStartFromRaw === "unread" ? "unread" : "";
|
|
396
|
+
const explicitTargetCount = parsePositiveIntegerValue(raw.target_count);
|
|
397
|
+
|
|
398
|
+
const hasExplicitCriteria = Boolean(explicitCriteria);
|
|
399
|
+
const hasExplicitStartFrom = Boolean(explicitStartFrom);
|
|
400
|
+
const hasExplicitTargetCount = Number.isInteger(explicitTargetCount) && explicitTargetCount > 0;
|
|
401
|
+
|
|
402
|
+
const criteria = explicitCriteria || defaultCriteria;
|
|
403
|
+
const startFrom = explicitStartFrom || defaultStartFrom;
|
|
404
|
+
const targetCount = explicitTargetCount || defaultTargetCount;
|
|
405
|
+
|
|
406
|
+
const profile = normalizeText(raw.profile) || "default";
|
|
407
|
+
const summary = {
|
|
408
|
+
profile,
|
|
409
|
+
criteria: criteria || null,
|
|
410
|
+
start_from: startFrom || null,
|
|
411
|
+
target_count: targetCount,
|
|
412
|
+
dry_run: raw.dry_run === true,
|
|
413
|
+
no_state: raw.no_state === true,
|
|
414
|
+
safe_pacing: typeof raw.safe_pacing === "boolean" ? raw.safe_pacing : null,
|
|
415
|
+
batch_rest_enabled: typeof raw.batch_rest_enabled === "boolean" ? raw.batch_rest_enabled : null
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const missing_fields = [];
|
|
419
|
+
const pending_questions = [];
|
|
420
|
+
|
|
421
|
+
if (!hasExplicitCriteria) {
|
|
422
|
+
missing_fields.push("follow_up.chat.criteria");
|
|
423
|
+
pending_questions.push({
|
|
424
|
+
field: "follow_up.chat.criteria",
|
|
425
|
+
question: "请填写 boss-chat follow-up 的筛选 criteria(自然语言,必填)。",
|
|
426
|
+
value: criteria || null
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (!hasExplicitStartFrom) {
|
|
430
|
+
missing_fields.push("follow_up.chat.start_from");
|
|
431
|
+
pending_questions.push({
|
|
432
|
+
field: "follow_up.chat.start_from",
|
|
433
|
+
question: "请确认 boss-chat follow-up 从未读还是全部聊天列表开始。",
|
|
434
|
+
value: summary.start_from,
|
|
435
|
+
options: [
|
|
436
|
+
{ label: "未读", value: "unread" },
|
|
437
|
+
{ label: "全部", value: "all" }
|
|
438
|
+
]
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
if (!hasExplicitTargetCount) {
|
|
442
|
+
missing_fields.push("follow_up.chat.target_count");
|
|
443
|
+
pending_questions.push({
|
|
444
|
+
field: "follow_up.chat.target_count",
|
|
445
|
+
question: "请填写 boss-chat follow-up 本次处理人数上限(正整数,必填)。",
|
|
446
|
+
value: summary.target_count
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
requested: true,
|
|
452
|
+
missing_fields,
|
|
453
|
+
pending_questions,
|
|
454
|
+
summary,
|
|
455
|
+
input: {
|
|
456
|
+
profile,
|
|
457
|
+
criteria: criteria || null,
|
|
458
|
+
start_from: startFrom || null,
|
|
459
|
+
target_count: targetCount,
|
|
460
|
+
dry_run: raw.dry_run === true,
|
|
461
|
+
no_state: raw.no_state === true,
|
|
462
|
+
safe_pacing: typeof raw.safe_pacing === "boolean" ? raw.safe_pacing : undefined,
|
|
463
|
+
batch_rest_enabled: typeof raw.batch_rest_enabled === "boolean" ? raw.batch_rest_enabled : undefined
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function mergeParsedFollowUp(parsedResult, followUpChat) {
|
|
469
|
+
if (!followUpChat?.requested) {
|
|
470
|
+
return {
|
|
471
|
+
...parsedResult,
|
|
472
|
+
follow_up: null,
|
|
473
|
+
follow_up_chat: followUpChat || null
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const pending_questions = [
|
|
477
|
+
...(Array.isArray(parsedResult?.pending_questions) ? parsedResult.pending_questions : []),
|
|
478
|
+
...followUpChat.pending_questions
|
|
479
|
+
];
|
|
480
|
+
return {
|
|
481
|
+
...parsedResult,
|
|
482
|
+
missing_fields: dedupe([
|
|
483
|
+
...(Array.isArray(parsedResult?.missing_fields) ? parsedResult.missing_fields : []),
|
|
484
|
+
...followUpChat.missing_fields
|
|
485
|
+
]),
|
|
486
|
+
pending_questions,
|
|
487
|
+
review: {
|
|
488
|
+
...(parsedResult?.review || {}),
|
|
489
|
+
follow_up: {
|
|
490
|
+
chat: followUpChat.summary
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
follow_up: {
|
|
494
|
+
chat: followUpChat.summary
|
|
495
|
+
},
|
|
496
|
+
follow_up_chat: followUpChat
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function buildResolvedFollowUpChatInput(followUpChat, { selectedJob, debugPort }) {
|
|
501
|
+
return {
|
|
502
|
+
profile: followUpChat?.input?.profile || "default",
|
|
503
|
+
job: selectedJob?.title || selectedJob?.label || selectedJob?.value || null,
|
|
504
|
+
start_from: followUpChat?.input?.start_from || null,
|
|
505
|
+
criteria: followUpChat?.input?.criteria || null,
|
|
506
|
+
target_count: followUpChat?.input?.target_count || null,
|
|
507
|
+
port: Number.isFinite(debugPort) ? debugPort : null,
|
|
508
|
+
dry_run: followUpChat?.input?.dry_run === true,
|
|
509
|
+
no_state: followUpChat?.input?.no_state === true,
|
|
510
|
+
safe_pacing: followUpChat?.input?.safe_pacing,
|
|
511
|
+
batch_rest_enabled: followUpChat?.input?.batch_rest_enabled
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function buildBossChatFollowUpStatus({ payload, runId, fallbackInput = null, startMessage = null }) {
|
|
516
|
+
const run = payload?.run || null;
|
|
517
|
+
const progress = run?.progress && typeof run.progress === "object" ? run.progress : {};
|
|
518
|
+
return {
|
|
519
|
+
enabled: true,
|
|
520
|
+
run_id: normalizeText(payload?.run_id || run?.runId || runId) || null,
|
|
521
|
+
state: normalizeText(run?.state || payload?.state || payload?.status).toLowerCase() || null,
|
|
522
|
+
profile: normalizeText(fallbackInput?.profile) || "default",
|
|
523
|
+
job: normalizeText(fallbackInput?.job) || null,
|
|
524
|
+
start_from: normalizeText(fallbackInput?.start_from) || null,
|
|
525
|
+
criteria: normalizeText(fallbackInput?.criteria) || null,
|
|
526
|
+
target_count: parsePositiveIntegerValue(fallbackInput?.target_count),
|
|
527
|
+
port: parsePositiveIntegerValue(fallbackInput?.port),
|
|
528
|
+
progress: {
|
|
529
|
+
inspected: Number.isInteger(progress.inspected) ? progress.inspected : 0,
|
|
530
|
+
passed: Number.isInteger(progress.passed) ? progress.passed : 0,
|
|
531
|
+
requested: Number.isInteger(progress.requested) ? progress.requested : 0,
|
|
532
|
+
skipped: Number.isInteger(progress.skipped) ? progress.skipped : 0,
|
|
533
|
+
errors: Number.isInteger(progress.errors) ? progress.errors : 0
|
|
534
|
+
},
|
|
535
|
+
last_message: normalizeText(run?.lastMessage || payload?.message || startMessage) || null,
|
|
536
|
+
error: run?.error || payload?.error || null,
|
|
537
|
+
result: run?.result || null
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function buildNeedInputMessage(missingFields = []) {
|
|
542
|
+
if (!Array.isArray(missingFields) || missingFields.length === 0) {
|
|
543
|
+
return "缺少必要字段,请先补充后再继续。";
|
|
544
|
+
}
|
|
545
|
+
if (missingFields.length === 1 && missingFields[0] === "criteria") {
|
|
546
|
+
return "缺少必要的筛选 criteria,请先补充或通过 overrides.criteria 明确传入。";
|
|
547
|
+
}
|
|
548
|
+
return `缺少必要字段:${missingFields.join(", ")}。请先补充后再继续。`;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function buildFinalReviewQuestion({ searchParams, screenParams, selectedJob, selectedPage, followUpChat }) {
|
|
362
552
|
return {
|
|
363
553
|
field: "final_review",
|
|
364
|
-
question:
|
|
554
|
+
question: followUpChat
|
|
555
|
+
? "开始执行前,请最后确认全部参数(岗位/页面/筛选条件/筛选 criteria/目标通过人数/post_action/max_greet_count/boss-chat follow-up)无误。"
|
|
556
|
+
: "开始执行搜索和筛选前,请最后确认全部参数(岗位/页面/筛选条件/筛选 criteria/目标通过人数/post_action/max_greet_count)无误。",
|
|
365
557
|
value: {
|
|
366
558
|
job: selectedJob?.title || selectedJob?.label || selectedJob?.value || null,
|
|
367
559
|
page_scope: selectedPage || "recommend",
|
|
368
560
|
search_params: searchParams,
|
|
369
|
-
screen_params: screenParams
|
|
561
|
+
screen_params: screenParams,
|
|
562
|
+
follow_up: followUpChat
|
|
563
|
+
? {
|
|
564
|
+
chat: followUpChat
|
|
565
|
+
}
|
|
566
|
+
: null
|
|
370
567
|
},
|
|
371
568
|
options: [
|
|
372
569
|
{ label: "参数无误,开始执行", value: "confirm" },
|
|
@@ -375,6 +572,41 @@ function buildFinalReviewQuestion({ searchParams, screenParams, selectedJob, sel
|
|
|
375
572
|
};
|
|
376
573
|
}
|
|
377
574
|
|
|
575
|
+
function cloneJsonSafe(value) {
|
|
576
|
+
if (value === null || value === undefined) return null;
|
|
577
|
+
try {
|
|
578
|
+
return JSON.parse(JSON.stringify(value));
|
|
579
|
+
} catch {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function buildScreenInputSummary({
|
|
585
|
+
instruction,
|
|
586
|
+
selectedPage,
|
|
587
|
+
selectedJob,
|
|
588
|
+
userSearchParams,
|
|
589
|
+
effectiveSearchParams,
|
|
590
|
+
screenParams,
|
|
591
|
+
followUp
|
|
592
|
+
}) {
|
|
593
|
+
return {
|
|
594
|
+
instruction: normalizeText(instruction || "") || null,
|
|
595
|
+
selected_page: selectedPage || "recommend",
|
|
596
|
+
selected_job: selectedJob
|
|
597
|
+
? {
|
|
598
|
+
value: normalizeText(selectedJob.value || "") || null,
|
|
599
|
+
title: normalizeText(selectedJob.title || "") || null,
|
|
600
|
+
label: normalizeText(selectedJob.label || "") || null
|
|
601
|
+
}
|
|
602
|
+
: null,
|
|
603
|
+
user_search_params: cloneJsonSafe(userSearchParams),
|
|
604
|
+
effective_search_params: cloneJsonSafe(effectiveSearchParams),
|
|
605
|
+
screen_params: cloneJsonSafe(screenParams),
|
|
606
|
+
follow_up: cloneJsonSafe(followUp)
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
378
610
|
function buildFailedResponse(code, message, extra = {}) {
|
|
379
611
|
return {
|
|
380
612
|
status: "FAILED",
|
|
@@ -395,6 +627,40 @@ function buildPausedResponse(message, extra = {}) {
|
|
|
395
627
|
};
|
|
396
628
|
}
|
|
397
629
|
|
|
630
|
+
function buildRecommendFollowUpEnvelope(recommendResult, chatPayload = null) {
|
|
631
|
+
return {
|
|
632
|
+
search_params: recommendResult?.search_params || null,
|
|
633
|
+
screen_params: recommendResult?.screen_params || null,
|
|
634
|
+
result: recommendResult?.result || null,
|
|
635
|
+
partial_result: recommendResult?.result || null,
|
|
636
|
+
follow_up: chatPayload
|
|
637
|
+
? {
|
|
638
|
+
chat: chatPayload
|
|
639
|
+
}
|
|
640
|
+
: null
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function buildFollowUpFailedResponse(code, message, recommendResult, chatPayload) {
|
|
645
|
+
return {
|
|
646
|
+
status: "FAILED",
|
|
647
|
+
error: {
|
|
648
|
+
code,
|
|
649
|
+
message,
|
|
650
|
+
retryable: true
|
|
651
|
+
},
|
|
652
|
+
...buildRecommendFollowUpEnvelope(recommendResult, chatPayload)
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function buildFollowUpPausedResponse(message, recommendResult, chatPayload) {
|
|
657
|
+
return {
|
|
658
|
+
status: "PAUSED",
|
|
659
|
+
message: normalizeText(message || "") || "Recommend 流水线已暂停。",
|
|
660
|
+
...buildRecommendFollowUpEnvelope(recommendResult, chatPayload)
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
398
664
|
class PipelineAbortError extends Error {
|
|
399
665
|
constructor(message = "Pipeline execution aborted") {
|
|
400
666
|
super(message);
|
|
@@ -430,6 +696,9 @@ function createPipelineRuntime(runtime = null) {
|
|
|
430
696
|
const isPauseRequested = typeof runtime?.isPauseRequested === "function"
|
|
431
697
|
? runtime.isPauseRequested
|
|
432
698
|
: () => false;
|
|
699
|
+
const isCancelRequested = typeof runtime?.isCancelRequested === "function"
|
|
700
|
+
? runtime.isCancelRequested
|
|
701
|
+
: () => false;
|
|
433
702
|
|
|
434
703
|
function setStage(stage, message = null) {
|
|
435
704
|
safeInvokeRuntimeCallback(runtime?.onStage, {
|
|
@@ -463,6 +732,13 @@ function createPipelineRuntime(runtime = null) {
|
|
|
463
732
|
});
|
|
464
733
|
}
|
|
465
734
|
|
|
735
|
+
function followUp(payload) {
|
|
736
|
+
safeInvokeRuntimeCallback(runtime?.onFollowUp, {
|
|
737
|
+
...(payload || {}),
|
|
738
|
+
at: new Date().toISOString()
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
|
|
466
742
|
function adapterRuntime(stage) {
|
|
467
743
|
return {
|
|
468
744
|
signal,
|
|
@@ -477,10 +753,12 @@ function createPipelineRuntime(runtime = null) {
|
|
|
477
753
|
signal,
|
|
478
754
|
heartbeatIntervalMs,
|
|
479
755
|
isPauseRequested,
|
|
756
|
+
isCancelRequested,
|
|
480
757
|
setStage,
|
|
481
758
|
heartbeat,
|
|
482
759
|
output,
|
|
483
760
|
progress,
|
|
761
|
+
followUp,
|
|
484
762
|
adapterRuntime
|
|
485
763
|
};
|
|
486
764
|
}
|
|
@@ -498,6 +776,14 @@ function isPauseRequested(runtimeHooks) {
|
|
|
498
776
|
}
|
|
499
777
|
}
|
|
500
778
|
|
|
779
|
+
function isCancelRequested(runtimeHooks) {
|
|
780
|
+
try {
|
|
781
|
+
return runtimeHooks?.isCancelRequested?.() === true;
|
|
782
|
+
} catch {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
501
787
|
function buildChromeSetupGuidance({ debugPort, pageState }) {
|
|
502
788
|
const expectedUrl = pageState?.expected_url || "https://www.zhipin.com/web/chat/recommend";
|
|
503
789
|
const loginUrl = pageState?.login_url || "https://www.zhipin.com/web/user/?ka=bticket";
|
|
@@ -536,6 +822,233 @@ function buildChromeSetupGuidance({ debugPort, pageState }) {
|
|
|
536
822
|
};
|
|
537
823
|
}
|
|
538
824
|
|
|
825
|
+
async function runBossChatFollowUpPhase({
|
|
826
|
+
workspaceRoot,
|
|
827
|
+
followUpChat,
|
|
828
|
+
selectedJob,
|
|
829
|
+
debugPort,
|
|
830
|
+
recommendResult,
|
|
831
|
+
resume,
|
|
832
|
+
runtimeHooks,
|
|
833
|
+
startChatRun,
|
|
834
|
+
getChatRun,
|
|
835
|
+
pauseChatRun,
|
|
836
|
+
resumeChatRun,
|
|
837
|
+
cancelChatRun
|
|
838
|
+
}) {
|
|
839
|
+
const recommendSummary = recommendResult?.result || null;
|
|
840
|
+
const resolvedChatInput = buildResolvedFollowUpChatInput(followUpChat, {
|
|
841
|
+
selectedJob,
|
|
842
|
+
debugPort
|
|
843
|
+
});
|
|
844
|
+
let chatRunId = normalizeText(resume?.chat_run_id || "");
|
|
845
|
+
const resumeFromChatPhase = resume?.resume === true && normalizeText(resume?.follow_up_phase) === "chat_followup";
|
|
846
|
+
let pauseRequested = false;
|
|
847
|
+
let cancelRequested = false;
|
|
848
|
+
let resumeIssued = false;
|
|
849
|
+
|
|
850
|
+
runtimeHooks.setStage(
|
|
851
|
+
"chat_followup",
|
|
852
|
+
chatRunId
|
|
853
|
+
? "Recommend 流水线已完成,准备恢复 boss-chat follow-up。"
|
|
854
|
+
: "Recommend 流水线已完成,开始执行 boss-chat follow-up。"
|
|
855
|
+
);
|
|
856
|
+
runtimeHooks.heartbeat("chat_followup", {
|
|
857
|
+
profile: resolvedChatInput.profile,
|
|
858
|
+
job: resolvedChatInput.job,
|
|
859
|
+
start_from: resolvedChatInput.start_from,
|
|
860
|
+
target_count: resolvedChatInput.target_count
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
if (!chatRunId) {
|
|
864
|
+
const startResult = await startChatRun({
|
|
865
|
+
workspaceRoot,
|
|
866
|
+
input: {
|
|
867
|
+
profile: resolvedChatInput.profile,
|
|
868
|
+
job: resolvedChatInput.job,
|
|
869
|
+
start_from: resolvedChatInput.start_from,
|
|
870
|
+
criteria: resolvedChatInput.criteria,
|
|
871
|
+
target_count: resolvedChatInput.target_count,
|
|
872
|
+
port: resolvedChatInput.port,
|
|
873
|
+
dry_run: resolvedChatInput.dry_run,
|
|
874
|
+
no_state: resolvedChatInput.no_state,
|
|
875
|
+
safe_pacing: resolvedChatInput.safe_pacing,
|
|
876
|
+
batch_rest_enabled: resolvedChatInput.batch_rest_enabled
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
if (startResult?.status !== "ACCEPTED" || !normalizeText(startResult?.run_id)) {
|
|
880
|
+
return buildFollowUpFailedResponse(
|
|
881
|
+
startResult?.error?.code || "BOSS_CHAT_FOLLOW_UP_LAUNCH_FAILED",
|
|
882
|
+
startResult?.error?.message || "boss-chat follow-up 启动失败。",
|
|
883
|
+
recommendResult,
|
|
884
|
+
{
|
|
885
|
+
enabled: true,
|
|
886
|
+
input: resolvedChatInput,
|
|
887
|
+
launch_result: startResult || null
|
|
888
|
+
}
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
chatRunId = normalizeText(startResult.run_id);
|
|
892
|
+
runtimeHooks.followUp({
|
|
893
|
+
stage: "chat_followup",
|
|
894
|
+
last_message: startResult.message || "boss-chat follow-up 已启动。",
|
|
895
|
+
recommend_payload: recommendResult,
|
|
896
|
+
recommend_result: recommendSummary,
|
|
897
|
+
follow_up: {
|
|
898
|
+
chat: {
|
|
899
|
+
...buildBossChatFollowUpStatus({
|
|
900
|
+
payload: startResult,
|
|
901
|
+
runId: chatRunId,
|
|
902
|
+
fallbackInput: resolvedChatInput,
|
|
903
|
+
startMessage: startResult.message || "boss-chat follow-up 已启动。"
|
|
904
|
+
}),
|
|
905
|
+
input: resolvedChatInput
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
while (true) {
|
|
912
|
+
ensurePipelineNotAborted(runtimeHooks.signal);
|
|
913
|
+
|
|
914
|
+
const chatStatusPayload = await getChatRun({
|
|
915
|
+
workspaceRoot,
|
|
916
|
+
input: {
|
|
917
|
+
profile: resolvedChatInput.profile,
|
|
918
|
+
runId: chatRunId
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
const chatStatus = buildBossChatFollowUpStatus({
|
|
922
|
+
payload: chatStatusPayload,
|
|
923
|
+
runId: chatRunId,
|
|
924
|
+
fallbackInput: resolvedChatInput
|
|
925
|
+
});
|
|
926
|
+
const chatState = normalizeText(chatStatus.state).toLowerCase();
|
|
927
|
+
|
|
928
|
+
runtimeHooks.followUp({
|
|
929
|
+
stage: "chat_followup",
|
|
930
|
+
last_message: chatStatus.last_message || "boss-chat follow-up 进行中。",
|
|
931
|
+
recommend_payload: recommendResult,
|
|
932
|
+
recommend_result: recommendSummary,
|
|
933
|
+
follow_up: {
|
|
934
|
+
chat: {
|
|
935
|
+
...chatStatus,
|
|
936
|
+
input: resolvedChatInput
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
if (isCancelRequested(runtimeHooks) && !cancelRequested && !["completed", "failed", "canceled"].includes(chatState)) {
|
|
942
|
+
cancelRequested = true;
|
|
943
|
+
await cancelChatRun({
|
|
944
|
+
workspaceRoot,
|
|
945
|
+
input: {
|
|
946
|
+
profile: resolvedChatInput.profile,
|
|
947
|
+
runId: chatRunId
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
await sleep(500);
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (
|
|
955
|
+
isPauseRequested(runtimeHooks)
|
|
956
|
+
&& !pauseRequested
|
|
957
|
+
&& !cancelRequested
|
|
958
|
+
&& !["paused", "completed", "failed", "canceled"].includes(chatState)
|
|
959
|
+
) {
|
|
960
|
+
pauseRequested = true;
|
|
961
|
+
await pauseChatRun({
|
|
962
|
+
workspaceRoot,
|
|
963
|
+
input: {
|
|
964
|
+
profile: resolvedChatInput.profile,
|
|
965
|
+
runId: chatRunId
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
await sleep(500);
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (resumeFromChatPhase && !isPauseRequested(runtimeHooks) && !isCancelRequested(runtimeHooks) && !resumeIssued) {
|
|
973
|
+
if (chatState === "paused") {
|
|
974
|
+
resumeIssued = true;
|
|
975
|
+
await resumeChatRun({
|
|
976
|
+
workspaceRoot,
|
|
977
|
+
input: {
|
|
978
|
+
profile: resolvedChatInput.profile,
|
|
979
|
+
runId: chatRunId
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
await sleep(500);
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
if (chatState === "running" || chatState === "queued") {
|
|
986
|
+
resumeIssued = true;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (chatState === "completed") {
|
|
991
|
+
return {
|
|
992
|
+
...recommendResult,
|
|
993
|
+
follow_up: {
|
|
994
|
+
chat: {
|
|
995
|
+
...chatStatus,
|
|
996
|
+
input: resolvedChatInput
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
message: "Recommend 流水线已完成,boss-chat follow-up 也已执行完成。"
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (chatState === "failed") {
|
|
1004
|
+
return buildFollowUpFailedResponse(
|
|
1005
|
+
chatStatus.error?.code || "BOSS_CHAT_FOLLOW_UP_FAILED",
|
|
1006
|
+
chatStatus.error?.message || "boss-chat follow-up 执行失败。",
|
|
1007
|
+
recommendResult,
|
|
1008
|
+
{
|
|
1009
|
+
...chatStatus,
|
|
1010
|
+
input: resolvedChatInput
|
|
1011
|
+
}
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (chatState === "canceled") {
|
|
1016
|
+
if (isCancelRequested(runtimeHooks)) {
|
|
1017
|
+
return buildFollowUpPausedResponse(
|
|
1018
|
+
"Recommend 流水线已取消,boss-chat follow-up 已停止。",
|
|
1019
|
+
recommendResult,
|
|
1020
|
+
{
|
|
1021
|
+
...chatStatus,
|
|
1022
|
+
input: resolvedChatInput
|
|
1023
|
+
}
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
return buildFollowUpFailedResponse(
|
|
1027
|
+
"BOSS_CHAT_FOLLOW_UP_CANCELED",
|
|
1028
|
+
"boss-chat follow-up 已取消。",
|
|
1029
|
+
recommendResult,
|
|
1030
|
+
{
|
|
1031
|
+
...chatStatus,
|
|
1032
|
+
input: resolvedChatInput
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (chatState === "paused" && isPauseRequested(runtimeHooks)) {
|
|
1038
|
+
return buildFollowUpPausedResponse(
|
|
1039
|
+
"Recommend 流水线已暂停,可使用 resume 继续 boss-chat follow-up。",
|
|
1040
|
+
recommendResult,
|
|
1041
|
+
{
|
|
1042
|
+
...chatStatus,
|
|
1043
|
+
input: resolvedChatInput
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
await sleep(BOSS_CHAT_FOLLOW_UP_POLL_MS);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
539
1052
|
const defaultDependencies = {
|
|
540
1053
|
attemptPipelineAutoRepair,
|
|
541
1054
|
parseRecommendInstruction,
|
|
@@ -548,11 +1061,16 @@ const defaultDependencies = {
|
|
|
548
1061
|
runPipelinePreflight,
|
|
549
1062
|
runRecommendSearchCli,
|
|
550
1063
|
runRecommendScreenCli,
|
|
1064
|
+
startBossChatRun,
|
|
1065
|
+
getBossChatRun,
|
|
1066
|
+
pauseBossChatRun,
|
|
1067
|
+
resumeBossChatRun,
|
|
1068
|
+
cancelBossChatRun,
|
|
551
1069
|
switchRecommendTab
|
|
552
1070
|
};
|
|
553
1071
|
|
|
554
1072
|
export async function runRecommendPipeline(
|
|
555
|
-
{ workspaceRoot, instruction, confirmation, overrides, resume = null },
|
|
1073
|
+
{ workspaceRoot, instruction, confirmation, overrides, followUp = null, resume = null },
|
|
556
1074
|
dependencies = defaultDependencies,
|
|
557
1075
|
runtime = null
|
|
558
1076
|
) {
|
|
@@ -570,13 +1088,26 @@ export async function runRecommendPipeline(
|
|
|
570
1088
|
runPipelinePreflight: runPreflight,
|
|
571
1089
|
runRecommendSearchCli: searchCli,
|
|
572
1090
|
runRecommendScreenCli: screenCli,
|
|
1091
|
+
startBossChatRun: startChatRun,
|
|
1092
|
+
getBossChatRun: getChatRun,
|
|
1093
|
+
pauseBossChatRun: pauseChatRun,
|
|
1094
|
+
resumeBossChatRun: resumeChatRun,
|
|
1095
|
+
cancelBossChatRun: cancelChatRun,
|
|
573
1096
|
switchRecommendTab: switchTab
|
|
574
1097
|
} = resolvedDependencies;
|
|
575
1098
|
const runtimeHooks = createPipelineRuntime(runtime);
|
|
576
1099
|
ensurePipelineNotAborted(runtimeHooks.signal);
|
|
577
1100
|
|
|
578
1101
|
const startedAt = Date.now();
|
|
579
|
-
const
|
|
1102
|
+
const instructionParsed = parseInstruction({ instruction, confirmation, overrides });
|
|
1103
|
+
const parsed = mergeParsedFollowUp(
|
|
1104
|
+
instructionParsed,
|
|
1105
|
+
normalizeFollowUpChatInput(followUp, {
|
|
1106
|
+
criteria: instructionParsed?.screenParams?.criteria || null,
|
|
1107
|
+
target_count: instructionParsed?.screenParams?.target_count || null,
|
|
1108
|
+
start_from: "unread"
|
|
1109
|
+
})
|
|
1110
|
+
);
|
|
580
1111
|
const selectedPage = resolvePipelinePageScope(parsed, confirmation, overrides);
|
|
581
1112
|
|
|
582
1113
|
if (parsed.missing_fields.length > 0) {
|
|
@@ -598,6 +1129,41 @@ export async function runRecommendPipeline(
|
|
|
598
1129
|
return buildNeedConfirmationResponse(parsed);
|
|
599
1130
|
}
|
|
600
1131
|
|
|
1132
|
+
const resumeFromChatPhase = (
|
|
1133
|
+
resume?.resume === true
|
|
1134
|
+
&& normalizeText(resume?.follow_up_phase) === "chat_followup"
|
|
1135
|
+
&& normalizeText(resume?.chat_run_id)
|
|
1136
|
+
);
|
|
1137
|
+
if (resumeFromChatPhase) {
|
|
1138
|
+
if (!parsed.follow_up_chat?.requested || !resume?.recommend_result) {
|
|
1139
|
+
return buildFailedResponse(
|
|
1140
|
+
"BOSS_CHAT_FOLLOW_UP_RESUME_CONTEXT_MISSING",
|
|
1141
|
+
"缺少 boss-chat follow-up 恢复上下文,无法继续。"
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
const preflight = runPreflight(workspaceRoot, { pageScope: selectedPage });
|
|
1145
|
+
return runBossChatFollowUpPhase({
|
|
1146
|
+
workspaceRoot,
|
|
1147
|
+
followUpChat: parsed.follow_up_chat,
|
|
1148
|
+
selectedJob: resume.recommend_result?.selected_job || null,
|
|
1149
|
+
debugPort: preflight.debug_port,
|
|
1150
|
+
recommendResult: {
|
|
1151
|
+
status: "COMPLETED",
|
|
1152
|
+
search_params: resume.recommend_search_params || parsed.searchParams,
|
|
1153
|
+
screen_params: resume.recommend_screen_params || parsed.screenParams,
|
|
1154
|
+
result: resume.recommend_result,
|
|
1155
|
+
message: "Recommend 流水线已完成,正在恢复 boss-chat follow-up。"
|
|
1156
|
+
},
|
|
1157
|
+
resume,
|
|
1158
|
+
runtimeHooks,
|
|
1159
|
+
startChatRun,
|
|
1160
|
+
getChatRun,
|
|
1161
|
+
pauseChatRun,
|
|
1162
|
+
resumeChatRun,
|
|
1163
|
+
cancelChatRun
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
|
|
601
1167
|
ensurePipelineNotAborted(runtimeHooks.signal);
|
|
602
1168
|
runtimeHooks.setStage("preflight", "开始执行 preflight 检查。");
|
|
603
1169
|
runtimeHooks.heartbeat("preflight");
|
|
@@ -874,6 +1440,7 @@ export async function runRecommendPipeline(
|
|
|
874
1440
|
post_action: parsed.proposed_post_action || parsed.screenParams.post_action,
|
|
875
1441
|
max_greet_count: parsed.proposed_max_greet_count || parsed.screenParams.max_greet_count
|
|
876
1442
|
},
|
|
1443
|
+
follow_up: parsed.follow_up || null,
|
|
877
1444
|
pending_questions: pendingQuestions,
|
|
878
1445
|
review: parsed.review,
|
|
879
1446
|
job_options: jobOptions
|
|
@@ -892,7 +1459,8 @@ export async function runRecommendPipeline(
|
|
|
892
1459
|
max_greet_count: parsed.proposed_max_greet_count || parsed.screenParams.max_greet_count
|
|
893
1460
|
},
|
|
894
1461
|
selectedJob,
|
|
895
|
-
selectedPage
|
|
1462
|
+
selectedPage,
|
|
1463
|
+
followUpChat: parsed.follow_up?.chat || null
|
|
896
1464
|
}));
|
|
897
1465
|
return {
|
|
898
1466
|
status: "NEED_CONFIRMATION",
|
|
@@ -905,6 +1473,7 @@ export async function runRecommendPipeline(
|
|
|
905
1473
|
post_action: parsed.proposed_post_action || parsed.screenParams.post_action,
|
|
906
1474
|
max_greet_count: parsed.proposed_max_greet_count || parsed.screenParams.max_greet_count
|
|
907
1475
|
},
|
|
1476
|
+
follow_up: parsed.follow_up || null,
|
|
908
1477
|
selected_job: selectedJob,
|
|
909
1478
|
pending_questions: pendingQuestions,
|
|
910
1479
|
review: parsed.review,
|
|
@@ -1192,6 +1761,15 @@ export async function runRecommendPipeline(
|
|
|
1192
1761
|
workspaceRoot,
|
|
1193
1762
|
screenParams: parsed.screenParams,
|
|
1194
1763
|
pageScope: selectedPage,
|
|
1764
|
+
inputSummary: buildScreenInputSummary({
|
|
1765
|
+
instruction,
|
|
1766
|
+
selectedPage,
|
|
1767
|
+
selectedJob,
|
|
1768
|
+
userSearchParams: parsed.searchParams,
|
|
1769
|
+
effectiveSearchParams,
|
|
1770
|
+
screenParams: parsed.screenParams,
|
|
1771
|
+
followUp: parsed.follow_up || null
|
|
1772
|
+
}),
|
|
1195
1773
|
resume: currentResumeConfig,
|
|
1196
1774
|
runtime: runtimeHooks.adapterRuntime("screen")
|
|
1197
1775
|
});
|
|
@@ -1475,7 +2053,7 @@ export async function runRecommendPipeline(
|
|
|
1475
2053
|
greet_count: screenSummary.greet_count ?? 0
|
|
1476
2054
|
});
|
|
1477
2055
|
|
|
1478
|
-
|
|
2056
|
+
const recommendResult = {
|
|
1479
2057
|
status: "COMPLETED",
|
|
1480
2058
|
search_params: effectiveSearchParams,
|
|
1481
2059
|
screen_params: parsed.screenParams,
|
|
@@ -1503,5 +2081,24 @@ export async function runRecommendPipeline(
|
|
|
1503
2081
|
? "Recommend 流水线已完成。本次 post_action=none:符合条件的人选仅记录到 CSV,不执行收藏或打招呼。"
|
|
1504
2082
|
: "Recommend 流水线已完成。post_action 在运行开始时已一次性确认;若选择打招呼并设置上限,超出上限后会自动改为收藏。"
|
|
1505
2083
|
};
|
|
2084
|
+
|
|
2085
|
+
if (!parsed.follow_up_chat?.requested) {
|
|
2086
|
+
return recommendResult;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
return runBossChatFollowUpPhase({
|
|
2090
|
+
workspaceRoot,
|
|
2091
|
+
followUpChat: parsed.follow_up_chat,
|
|
2092
|
+
selectedJob,
|
|
2093
|
+
debugPort: preflight.debug_port,
|
|
2094
|
+
recommendResult,
|
|
2095
|
+
resume,
|
|
2096
|
+
runtimeHooks,
|
|
2097
|
+
startChatRun,
|
|
2098
|
+
getChatRun,
|
|
2099
|
+
pauseChatRun,
|
|
2100
|
+
resumeChatRun,
|
|
2101
|
+
cancelChatRun
|
|
2102
|
+
});
|
|
1506
2103
|
}
|
|
1507
2104
|
}
|