@reconcrap/boss-recommend-mcp 2.1.21 → 2.1.23
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 +5 -2
- package/bin/boss-recommend-mcp.js +4 -4
- package/config/screening-config.example.json +33 -33
- package/package.json +2 -1
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +42 -42
- package/skills/boss-chat/SKILL.md +106 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +219 -214
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +127 -127
- package/src/chat-runtime-config.js +775 -775
- package/src/cli.js +573 -573
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +2416 -2385
- package/src/core/capture/index.js +1201 -1201
- package/src/core/cv-acquisition/index.js +238 -238
- package/src/core/cv-capture-target/index.js +299 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +1315 -1306
- package/src/core/reporting/legacy-csv.js +334 -332
- package/src/core/run/index.js +44 -27
- package/src/core/run/timing.js +33 -33
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +973 -973
- package/src/core/self-heal/viewport.js +564 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +113 -113
- package/src/domains/chat/index.js +7 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +122 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +571 -571
- package/src/domains/common/account-rights-panel.js +314 -314
- package/src/domains/common/recovery-settle.js +159 -159
- package/src/domains/recommend/actions.js +472 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +228 -159
- package/src/domains/recommend/detail.js +655 -646
- package/src/domains/recommend/filters.js +748 -377
- package/src/domains/recommend/index.js +4 -3
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -0
- package/src/domains/recommend/refresh.js +575 -351
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +1616 -878
- package/src/domains/recommend/scopes.js +246 -246
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +236 -236
- package/src/domains/recruit/detail.js +588 -588
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +866 -866
- package/src/domains/recruit/refresh.js +45 -45
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1620 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +121 -4
- package/src/parser.js +376 -8
- package/src/recommend-mcp.js +1174 -914
- package/src/recommend-scheduler.js +535 -469
- package/src/recruit-mcp.js +2121 -2121
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: "boss-recruit-pipeline"
|
|
3
|
-
description: "Use when users want Boss search/recruit-page screening via the unified boss-recommend-mcp package. Replaces the legacy boss-recruit-mcp skill."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Boss Recruit Pipeline Skill
|
|
7
|
-
|
|
8
|
-
## Goal
|
|
9
|
-
|
|
10
|
-
当用户要在 Boss 搜索页 / 招聘搜索页筛人时,必须走 `@reconcrap/boss-recommend-mcp` 2.x 内置的 recruit/search MCP 工具,不要安装或调用旧的 `@reconcrap/boss-recruit-mcp`。
|
|
11
|
-
|
|
12
|
-
## Tool Routing
|
|
13
|
-
|
|
14
|
-
Trae/Trae-CN split-server config exposes these under the `boss-recruit` MCP server. Search/recruit tasks should call `boss-recruit/<tool>` when the host shows server-qualified tool names.
|
|
15
|
-
|
|
16
|
-
- 同步启动:`run_recruit_pipeline`
|
|
17
|
-
- 异步启动:`start_recruit_pipeline_run`
|
|
18
|
-
- 查询进度:`get_recruit_pipeline_run`
|
|
19
|
-
- 暂停:`pause_recruit_pipeline_run`
|
|
20
|
-
- 继续:`resume_recruit_pipeline_run`
|
|
21
|
-
- 取消:`cancel_recruit_pipeline_run`
|
|
22
|
-
|
|
23
|
-
If the visible tool surface only offers `boss-recommend/*` for a search/recruit task, stop and report a tool-surface/config error. Do not call `boss-recommend/list_recommend_jobs`, `boss-recommend/run_recommend`, or `boss-recommend/start_recommend_pipeline_run` as a fallback for search.
|
|
24
|
-
|
|
25
|
-
## Hard Rules
|
|
26
|
-
|
|
27
|
-
- 只在用户明确说搜索页、search、recruit、招聘搜索、`/web/chat/search` 时使用本 skill。
|
|
28
|
-
- 如果用户说推荐页、recommend、`/web/chat/recommend`,必须交给 `boss-recommend-pipeline`。
|
|
29
|
-
- 如果用户说聊天页、未读、全部聊天、求简历,必须交给 `boss-chat`。
|
|
30
|
-
- 禁止调用旧包:`@reconcrap/boss-recruit-mcp`、`boss-recruit-mcp`、旧本地 recruit repo、旧 vendor 脚本。
|
|
31
|
-
- 浏览器自动化必须走 CDP-only 2.x MCP 工具;不得要求用户启用 legacy page-JS 或 `Runtime.evaluate` 路径。
|
|
32
|
-
- 启动 search/recruit run 时,若本机默认 `127.0.0.1:9222` Chrome DevTools 端口不可连,工具会自动打开 Chrome 并导航到 `https://www.zhipin.com/web/chat/search`。
|
|
33
|
-
- 只有工具返回 `BOSS_LOGIN_REQUIRED` / `requires_login=true` 时,才要求用户在自动打开的 Chrome 窗口人工登录 Boss 后重试;不要把“没开 9222 Chrome”当作缺参。
|
|
34
|
-
- 若本机找不到 Chrome,可提示用户设置 `BOSS_MCP_CHROME_PATH` 或 `BOSS_RECOMMEND_CHROME_PATH`;非本机 debug host 不自动启动。
|
|
35
|
-
- 若用户未提供岗位,必须先询问岗位。搜索页岗位选择在关键词输入框旁边;不要猜测默认岗位。
|
|
36
|
-
- 搜索页任务不要调用 `list_recommend_jobs` 获取岗位;推荐页岗位列表和搜索页岗位选择不是同一个工具面。用户已经给出岗位时直接传 `overrides.job`。
|
|
37
|
-
- 若用户提供城市、学历、学校、关键词、过滤已看、人选目标数、筛选条件等参数,必须逐项传入或确认。
|
|
38
|
-
- 搜索页和推荐页一样支持多选筛选条件;不要把多选降级成单选。
|
|
39
|
-
- 每次 run 必须明确询问用户本次休息强度 `rest_level`:`low`(旧策略)/ `medium`(约 5 小时或 700 人累计休息 30 分钟)/ `high`(约 5 小时或 700 人累计休息 1 小时);不得默认使用配置文件里的值替用户决定。
|
|
40
|
-
- 启动前展示一次包含岗位、关键词、城市、学历、学校标签、是否过滤已看、是否过滤近期同事触达、criteria、目标人数、后置动作和休息强度的总确认;用户确认后,`confirmation` 只需要 `{ "final_confirmed": true }`。
|
|
41
|
-
- 不要让工具重写用户的 `criteria`。用户给出 `筛选条件` / `筛选标准` / `硬条件` 时,逐字写入 `overrides.criteria`;不要传系统简化版。
|
|
42
|
-
- 用户说学校类型“不限”时,在 `overrides.school_tag` 显式传 `"不限"` 或在 `overrides.schools` 传 `[]`;不要因为 criteria 里出现 `985/211/双一流` 就把它们当作搜索页学校过滤器。
|
|
43
|
-
- 用户说只看未查看“不限”时,在 `overrides.recent_not_view` 显式传 `"不限"` 或在 `overrides.filter_recent_viewed` 传 `false`。
|
|
44
|
-
- “只看未查看/过滤已看”只控制 Boss 的“过滤近14天查看”筛选;“是否过滤近期同事触达”是单独输入,写入 `overrides.filter_recent_colleague_contacted` 或兼容字段 `overrides.skip_recent_colleague_contacted`。用户说近期同事触达“不限/不过滤”时传 `false`;用户说过滤近期同事触达/跳过同事已联系时传 `true`。
|
|
45
|
-
|
|
46
|
-
## Required Inputs
|
|
47
|
-
|
|
48
|
-
- `job`
|
|
49
|
-
- `keyword` 或用户明确的搜索意图
|
|
50
|
-
- `criteria`
|
|
51
|
-
- `target_count`
|
|
52
|
-
- `rest_level`: `low|medium|high`
|
|
53
|
-
|
|
54
|
-
常用可选项:
|
|
55
|
-
|
|
56
|
-
- `city`
|
|
57
|
-
- `degree`
|
|
58
|
-
- `school_tag`
|
|
59
|
-
- `recent_not_view`
|
|
60
|
-
- `filter_recent_colleague_contacted`
|
|
61
|
-
- `port`
|
|
62
|
-
|
|
63
|
-
## Confirmation Flow
|
|
64
|
-
|
|
65
|
-
1. 先从用户原始消息里提取参数;缺什么只问什么。不要先调用 MCP 让工具猜缺参。
|
|
66
|
-
2. `criteria` 必须来自用户明确提供的筛选条件,或来自你向用户追问后得到的完整自然语言标准。禁止把岗位、关键词、学历等字段拼成 criteria。
|
|
67
|
-
3. 如果用户一开始已经给齐 `job`、`keyword`、`criteria`、`target_count`、`rest_level`,并且可选筛选项也已明确或确认不限,直接展示一次总确认。
|
|
68
|
-
4. 用户确认后调用 `boss-recruit/start_recruit_pipeline_run` 或 `boss-recruit/run_recruit_pipeline`,传:
|
|
69
|
-
- `confirmation: { "final_confirmed": true }`
|
|
70
|
-
- 所有规范化字段放在 `overrides`
|
|
71
|
-
- 完整 criteria 放在 `overrides.criteria`
|
|
72
|
-
- 本次休息强度放在 `human_behavior.restLevel`
|
|
73
|
-
5. 工具返回 `NEED_INPUT` 时,按 `pending_questions` 只补具体缺口;不要接受或转述工具生成的简化 criteria。
|
|
74
|
-
|
|
75
|
-
启动工具时,把用户确认的休息强度写入 `human_behavior.restLevel`,例如:
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"confirmation": { "final_confirmed": true },
|
|
80
|
-
"human_behavior": { "restLevel": "medium" }
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Response Style
|
|
85
|
-
|
|
86
|
-
- 用结构化中文确认参数。
|
|
87
|
-
- 缺参时只补缺口,不要改写用户的筛选条件。
|
|
88
|
-
- 拿到 `ACCEPTED + run_id` 后默认停止本轮,不主动高频轮询。
|
|
89
|
-
- 查询、暂停、恢复、取消时必须复用同一个 `run_id`。
|
|
1
|
+
---
|
|
2
|
+
name: "boss-recruit-pipeline"
|
|
3
|
+
description: "Use when users want Boss search/recruit-page screening via the unified boss-recommend-mcp package. Replaces the legacy boss-recruit-mcp skill."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Boss Recruit Pipeline Skill
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
当用户要在 Boss 搜索页 / 招聘搜索页筛人时,必须走 `@reconcrap/boss-recommend-mcp` 2.x 内置的 recruit/search MCP 工具,不要安装或调用旧的 `@reconcrap/boss-recruit-mcp`。
|
|
11
|
+
|
|
12
|
+
## Tool Routing
|
|
13
|
+
|
|
14
|
+
Trae/Trae-CN split-server config exposes these under the `boss-recruit` MCP server. Search/recruit tasks should call `boss-recruit/<tool>` when the host shows server-qualified tool names.
|
|
15
|
+
|
|
16
|
+
- 同步启动:`run_recruit_pipeline`
|
|
17
|
+
- 异步启动:`start_recruit_pipeline_run`
|
|
18
|
+
- 查询进度:`get_recruit_pipeline_run`
|
|
19
|
+
- 暂停:`pause_recruit_pipeline_run`
|
|
20
|
+
- 继续:`resume_recruit_pipeline_run`
|
|
21
|
+
- 取消:`cancel_recruit_pipeline_run`
|
|
22
|
+
|
|
23
|
+
If the visible tool surface only offers `boss-recommend/*` for a search/recruit task, stop and report a tool-surface/config error. Do not call `boss-recommend/list_recommend_jobs`, `boss-recommend/run_recommend`, or `boss-recommend/start_recommend_pipeline_run` as a fallback for search.
|
|
24
|
+
|
|
25
|
+
## Hard Rules
|
|
26
|
+
|
|
27
|
+
- 只在用户明确说搜索页、search、recruit、招聘搜索、`/web/chat/search` 时使用本 skill。
|
|
28
|
+
- 如果用户说推荐页、recommend、`/web/chat/recommend`,必须交给 `boss-recommend-pipeline`。
|
|
29
|
+
- 如果用户说聊天页、未读、全部聊天、求简历,必须交给 `boss-chat`。
|
|
30
|
+
- 禁止调用旧包:`@reconcrap/boss-recruit-mcp`、`boss-recruit-mcp`、旧本地 recruit repo、旧 vendor 脚本。
|
|
31
|
+
- 浏览器自动化必须走 CDP-only 2.x MCP 工具;不得要求用户启用 legacy page-JS 或 `Runtime.evaluate` 路径。
|
|
32
|
+
- 启动 search/recruit run 时,若本机默认 `127.0.0.1:9222` Chrome DevTools 端口不可连,工具会自动打开 Chrome 并导航到 `https://www.zhipin.com/web/chat/search`。
|
|
33
|
+
- 只有工具返回 `BOSS_LOGIN_REQUIRED` / `requires_login=true` 时,才要求用户在自动打开的 Chrome 窗口人工登录 Boss 后重试;不要把“没开 9222 Chrome”当作缺参。
|
|
34
|
+
- 若本机找不到 Chrome,可提示用户设置 `BOSS_MCP_CHROME_PATH` 或 `BOSS_RECOMMEND_CHROME_PATH`;非本机 debug host 不自动启动。
|
|
35
|
+
- 若用户未提供岗位,必须先询问岗位。搜索页岗位选择在关键词输入框旁边;不要猜测默认岗位。
|
|
36
|
+
- 搜索页任务不要调用 `list_recommend_jobs` 获取岗位;推荐页岗位列表和搜索页岗位选择不是同一个工具面。用户已经给出岗位时直接传 `overrides.job`。
|
|
37
|
+
- 若用户提供城市、学历、学校、关键词、过滤已看、人选目标数、筛选条件等参数,必须逐项传入或确认。
|
|
38
|
+
- 搜索页和推荐页一样支持多选筛选条件;不要把多选降级成单选。
|
|
39
|
+
- 每次 run 必须明确询问用户本次休息强度 `rest_level`:`low`(旧策略)/ `medium`(约 5 小时或 700 人累计休息 30 分钟)/ `high`(约 5 小时或 700 人累计休息 1 小时);不得默认使用配置文件里的值替用户决定。
|
|
40
|
+
- 启动前展示一次包含岗位、关键词、城市、学历、学校标签、是否过滤已看、是否过滤近期同事触达、criteria、目标人数、后置动作和休息强度的总确认;用户确认后,`confirmation` 只需要 `{ "final_confirmed": true }`。
|
|
41
|
+
- 不要让工具重写用户的 `criteria`。用户给出 `筛选条件` / `筛选标准` / `硬条件` 时,逐字写入 `overrides.criteria`;不要传系统简化版。
|
|
42
|
+
- 用户说学校类型“不限”时,在 `overrides.school_tag` 显式传 `"不限"` 或在 `overrides.schools` 传 `[]`;不要因为 criteria 里出现 `985/211/双一流` 就把它们当作搜索页学校过滤器。
|
|
43
|
+
- 用户说只看未查看“不限”时,在 `overrides.recent_not_view` 显式传 `"不限"` 或在 `overrides.filter_recent_viewed` 传 `false`。
|
|
44
|
+
- “只看未查看/过滤已看”只控制 Boss 的“过滤近14天查看”筛选;“是否过滤近期同事触达”是单独输入,写入 `overrides.filter_recent_colleague_contacted` 或兼容字段 `overrides.skip_recent_colleague_contacted`。用户说近期同事触达“不限/不过滤”时传 `false`;用户说过滤近期同事触达/跳过同事已联系时传 `true`。
|
|
45
|
+
|
|
46
|
+
## Required Inputs
|
|
47
|
+
|
|
48
|
+
- `job`
|
|
49
|
+
- `keyword` 或用户明确的搜索意图
|
|
50
|
+
- `criteria`
|
|
51
|
+
- `target_count`
|
|
52
|
+
- `rest_level`: `low|medium|high`
|
|
53
|
+
|
|
54
|
+
常用可选项:
|
|
55
|
+
|
|
56
|
+
- `city`
|
|
57
|
+
- `degree`
|
|
58
|
+
- `school_tag`
|
|
59
|
+
- `recent_not_view`
|
|
60
|
+
- `filter_recent_colleague_contacted`
|
|
61
|
+
- `port`
|
|
62
|
+
|
|
63
|
+
## Confirmation Flow
|
|
64
|
+
|
|
65
|
+
1. 先从用户原始消息里提取参数;缺什么只问什么。不要先调用 MCP 让工具猜缺参。
|
|
66
|
+
2. `criteria` 必须来自用户明确提供的筛选条件,或来自你向用户追问后得到的完整自然语言标准。禁止把岗位、关键词、学历等字段拼成 criteria。
|
|
67
|
+
3. 如果用户一开始已经给齐 `job`、`keyword`、`criteria`、`target_count`、`rest_level`,并且可选筛选项也已明确或确认不限,直接展示一次总确认。
|
|
68
|
+
4. 用户确认后调用 `boss-recruit/start_recruit_pipeline_run` 或 `boss-recruit/run_recruit_pipeline`,传:
|
|
69
|
+
- `confirmation: { "final_confirmed": true }`
|
|
70
|
+
- 所有规范化字段放在 `overrides`
|
|
71
|
+
- 完整 criteria 放在 `overrides.criteria`
|
|
72
|
+
- 本次休息强度放在 `human_behavior.restLevel`
|
|
73
|
+
5. 工具返回 `NEED_INPUT` 时,按 `pending_questions` 只补具体缺口;不要接受或转述工具生成的简化 criteria。
|
|
74
|
+
|
|
75
|
+
启动工具时,把用户确认的休息强度写入 `human_behavior.restLevel`,例如:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"confirmation": { "final_confirmed": true },
|
|
80
|
+
"human_behavior": { "restLevel": "medium" }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Response Style
|
|
85
|
+
|
|
86
|
+
- 用结构化中文确认参数。
|
|
87
|
+
- 缺参时只补缺口,不要改写用户的筛选条件。
|
|
88
|
+
- 拿到 `ACCEPTED + run_id` 后默认停止本轮,不主动高频轮询。
|
|
89
|
+
- 查询、暂停、恢复、取消时必须复用同一个 `run_id`。
|
package/src/chat-mcp.js
CHANGED
|
@@ -66,11 +66,11 @@ const RUN_MODE_ASYNC = "async";
|
|
|
66
66
|
const DETACHED_WORKER_SCRIPT = fileURLToPath(new URL("./detached-worker.js", import.meta.url));
|
|
67
67
|
const DETACHED_WORKER_POLL_MS = 1000;
|
|
68
68
|
|
|
69
|
-
const CHAT_REQUIRED_FIELDS = Object.freeze([
|
|
70
|
-
"job",
|
|
71
|
-
"start_from",
|
|
72
|
-
"target_count"
|
|
73
|
-
]);
|
|
69
|
+
const CHAT_REQUIRED_FIELDS = Object.freeze([
|
|
70
|
+
"job",
|
|
71
|
+
"start_from",
|
|
72
|
+
"target_count"
|
|
73
|
+
]);
|
|
74
74
|
|
|
75
75
|
const TERMINAL_STATUSES = new Set([
|
|
76
76
|
RUN_STATUS_COMPLETED,
|
|
@@ -756,8 +756,8 @@ function buildLegacyChatResult(snapshot) {
|
|
|
756
756
|
};
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
function normalizeRunSnapshot(snapshot) {
|
|
760
|
-
if (!snapshot) return null;
|
|
759
|
+
function normalizeRunSnapshot(snapshot) {
|
|
760
|
+
if (!snapshot) return null;
|
|
761
761
|
const meta = getChatRunMeta(snapshot.runId);
|
|
762
762
|
const artifacts = getChatRunArtifacts(snapshot.runId);
|
|
763
763
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
@@ -812,61 +812,61 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
812
812
|
},
|
|
813
813
|
result: legacyResult,
|
|
814
814
|
artifacts
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
function plainRecord(value) {
|
|
819
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
function mergePersistedChatControlRequest(normalized, existing) {
|
|
823
|
-
const control = {
|
|
824
|
-
...(normalized?.control || {})
|
|
825
|
-
};
|
|
826
|
-
if (!normalized) return control;
|
|
827
|
-
const existingControl = plainRecord(existing?.control);
|
|
828
|
-
if (TERMINAL_STATUSES.has(normalized.state)) return control;
|
|
829
|
-
if (existingControl.cancel_requested === true) {
|
|
830
|
-
return {
|
|
831
|
-
...control,
|
|
832
|
-
pause_requested: true,
|
|
833
|
-
pause_requested_at: existingControl.pause_requested_at || control.pause_requested_at || new Date().toISOString(),
|
|
834
|
-
pause_requested_by: existingControl.pause_requested_by || control.pause_requested_by || "cancel_boss_chat_run",
|
|
835
|
-
cancel_requested: true
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
if (existingControl.pause_requested === true && normalized.state !== RUN_STATUS_PAUSED) {
|
|
839
|
-
return {
|
|
840
|
-
...control,
|
|
841
|
-
pause_requested: true,
|
|
842
|
-
pause_requested_at: existingControl.pause_requested_at || control.pause_requested_at || new Date().toISOString(),
|
|
843
|
-
pause_requested_by: existingControl.pause_requested_by || control.pause_requested_by || "pause_boss_chat_run"
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
if (existingControl.pause_requested === false && normalized.state === RUN_STATUS_PAUSED) {
|
|
847
|
-
return {
|
|
848
|
-
...control,
|
|
849
|
-
pause_requested: false,
|
|
850
|
-
pause_requested_at: null,
|
|
851
|
-
pause_requested_by: null,
|
|
852
|
-
cancel_requested: false
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
return control;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function persistChatRunSnapshot(snapshot, {
|
|
859
|
-
persistActiveCheckpoint = false
|
|
860
|
-
} = {}) {
|
|
861
|
-
const normalized = normalizeRunSnapshot(snapshot);
|
|
862
|
-
if (!normalized?.run_id) return normalized;
|
|
863
|
-
const artifacts = getChatRunArtifacts(normalized.run_id);
|
|
864
|
-
if (!artifacts) return normalized;
|
|
865
|
-
const existing = readJsonFile(artifacts.run_state_path);
|
|
866
|
-
normalized.control = mergePersistedChatControlRequest(normalized, existing);
|
|
867
|
-
if (persistActiveCheckpoint) {
|
|
868
|
-
persistChatCheckpointSnapshot(normalized);
|
|
869
|
-
}
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function plainRecord(value) {
|
|
819
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function mergePersistedChatControlRequest(normalized, existing) {
|
|
823
|
+
const control = {
|
|
824
|
+
...(normalized?.control || {})
|
|
825
|
+
};
|
|
826
|
+
if (!normalized) return control;
|
|
827
|
+
const existingControl = plainRecord(existing?.control);
|
|
828
|
+
if (TERMINAL_STATUSES.has(normalized.state)) return control;
|
|
829
|
+
if (existingControl.cancel_requested === true) {
|
|
830
|
+
return {
|
|
831
|
+
...control,
|
|
832
|
+
pause_requested: true,
|
|
833
|
+
pause_requested_at: existingControl.pause_requested_at || control.pause_requested_at || new Date().toISOString(),
|
|
834
|
+
pause_requested_by: existingControl.pause_requested_by || control.pause_requested_by || "cancel_boss_chat_run",
|
|
835
|
+
cancel_requested: true
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
if (existingControl.pause_requested === true && normalized.state !== RUN_STATUS_PAUSED) {
|
|
839
|
+
return {
|
|
840
|
+
...control,
|
|
841
|
+
pause_requested: true,
|
|
842
|
+
pause_requested_at: existingControl.pause_requested_at || control.pause_requested_at || new Date().toISOString(),
|
|
843
|
+
pause_requested_by: existingControl.pause_requested_by || control.pause_requested_by || "pause_boss_chat_run"
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
if (existingControl.pause_requested === false && normalized.state === RUN_STATUS_PAUSED) {
|
|
847
|
+
return {
|
|
848
|
+
...control,
|
|
849
|
+
pause_requested: false,
|
|
850
|
+
pause_requested_at: null,
|
|
851
|
+
pause_requested_by: null,
|
|
852
|
+
cancel_requested: false
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
return control;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function persistChatRunSnapshot(snapshot, {
|
|
859
|
+
persistActiveCheckpoint = false
|
|
860
|
+
} = {}) {
|
|
861
|
+
const normalized = normalizeRunSnapshot(snapshot);
|
|
862
|
+
if (!normalized?.run_id) return normalized;
|
|
863
|
+
const artifacts = getChatRunArtifacts(normalized.run_id);
|
|
864
|
+
if (!artifacts) return normalized;
|
|
865
|
+
const existing = readJsonFile(artifacts.run_state_path);
|
|
866
|
+
normalized.control = mergePersistedChatControlRequest(normalized, existing);
|
|
867
|
+
if (persistActiveCheckpoint) {
|
|
868
|
+
persistChatCheckpointSnapshot(normalized);
|
|
869
|
+
}
|
|
870
870
|
const payload = {
|
|
871
871
|
run_id: normalized.run_id,
|
|
872
872
|
mode: normalized.mode,
|
|
@@ -1138,13 +1138,13 @@ function buildChatNextCallExample(args, missingFields, normalized) {
|
|
|
1138
1138
|
return Object.keys(example).length ? example : null;
|
|
1139
1139
|
}
|
|
1140
1140
|
|
|
1141
|
-
function getMissingChatStartFields(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1142
|
-
const missing = [];
|
|
1143
|
-
if (!normalized.job) missing.push("job");
|
|
1144
|
-
if (!["unread", "all"].includes(normalized.startFrom)) missing.push("start_from");
|
|
1145
|
-
if (!normalized.target.provided || normalized.target.parseError) missing.push("target_count");
|
|
1146
|
-
return missing;
|
|
1147
|
-
}
|
|
1141
|
+
function getMissingChatStartFields(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1142
|
+
const missing = [];
|
|
1143
|
+
if (!normalized.job) missing.push("job");
|
|
1144
|
+
if (!["unread", "all"].includes(normalized.startFrom)) missing.push("start_from");
|
|
1145
|
+
if (!normalized.target.provided || normalized.target.parseError) missing.push("target_count");
|
|
1146
|
+
return missing;
|
|
1147
|
+
}
|
|
1148
1148
|
|
|
1149
1149
|
function buildTargetCountDiagnostics(args, missingFields, normalized) {
|
|
1150
1150
|
if (!missingFields.includes("target_count")) return {};
|
|
@@ -1208,10 +1208,10 @@ function buildPendingChatQuestions({ args, missingFields, normalized, jobOptions
|
|
|
1208
1208
|
parse_error: normalized.target.parseError || null
|
|
1209
1209
|
};
|
|
1210
1210
|
}
|
|
1211
|
-
return {
|
|
1212
|
-
field,
|
|
1213
|
-
question: `请提供 ${field}。`,
|
|
1214
|
-
value: null
|
|
1211
|
+
return {
|
|
1212
|
+
field,
|
|
1213
|
+
question: `请提供 ${field}。`,
|
|
1214
|
+
value: null
|
|
1215
1215
|
};
|
|
1216
1216
|
});
|
|
1217
1217
|
}
|
|
@@ -1219,21 +1219,21 @@ function buildPendingChatQuestions({ args, missingFields, normalized, jobOptions
|
|
|
1219
1219
|
async function buildNeedInputResponse({ args, missingFields, normalized }) {
|
|
1220
1220
|
const diagnostics = buildTargetCountDiagnostics(args, missingFields, normalized);
|
|
1221
1221
|
return {
|
|
1222
|
-
status: "NEED_INPUT",
|
|
1223
|
-
required_fields: CHAT_REQUIRED_FIELDS.slice(),
|
|
1224
|
-
missing_fields: missingFields,
|
|
1225
|
-
criteria_optional: true,
|
|
1226
|
-
empty_criteria_mode: "collect_cv",
|
|
1227
|
-
...diagnostics,
|
|
1222
|
+
status: "NEED_INPUT",
|
|
1223
|
+
required_fields: CHAT_REQUIRED_FIELDS.slice(),
|
|
1224
|
+
missing_fields: missingFields,
|
|
1225
|
+
criteria_optional: true,
|
|
1226
|
+
empty_criteria_mode: "collect_cv",
|
|
1227
|
+
...diagnostics,
|
|
1228
1228
|
pending_questions: buildPendingChatQuestions({ args, missingFields, normalized }),
|
|
1229
1229
|
job_options: [],
|
|
1230
|
-
error: {
|
|
1231
|
-
code: "MISSING_REQUIRED_FIELDS",
|
|
1232
|
-
message: "缺少必要字段。请补齐 job、start_from、target_count 后再启动 Boss chat CDP-only run。criteria 可留空;留空时会进入收集简历模式。",
|
|
1233
|
-
retryable: true
|
|
1234
|
-
}
|
|
1235
|
-
};
|
|
1236
|
-
}
|
|
1230
|
+
error: {
|
|
1231
|
+
code: "MISSING_REQUIRED_FIELDS",
|
|
1232
|
+
message: "缺少必要字段。请补齐 job、start_from、target_count 后再启动 Boss chat CDP-only run。criteria 可留空;留空时会进入收集简历模式。",
|
|
1233
|
+
retryable: true
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
1237
|
|
|
1238
1238
|
function shouldRequestChatResume(args = {}, context = {}) {
|
|
1239
1239
|
const action = normalizeText(args.post_action || args.action).toLowerCase();
|
|
@@ -1267,40 +1267,40 @@ function isDebugTestMode(args = {}) {
|
|
|
1267
1267
|
return args.debug_test_mode === true || args.allow_debug_test_mode === true;
|
|
1268
1268
|
}
|
|
1269
1269
|
|
|
1270
|
-
function normalizeScreeningModeArg(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1271
|
-
if (!normalized.criteria) return "collect_cv";
|
|
1272
|
-
const raw = normalizeText(args.screening_mode || args.screeningMode || "");
|
|
1273
|
-
if (args.use_llm === false) return "deterministic";
|
|
1274
|
-
return ["deterministic", "local", "local_scorer"].includes(raw.toLowerCase())
|
|
1275
|
-
? "deterministic"
|
|
1276
|
-
: "llm";
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
function collectChatDebugTestOptions(args = {}) {
|
|
1280
|
-
const reasons = [];
|
|
1281
|
-
if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
|
|
1282
|
-
if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
|
|
1283
|
-
if (args.dry_run === true || args.dry_run_request_cv === true) reasons.push("dry_run_request_cv");
|
|
1284
|
-
return reasons;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
function shouldUseChatLlm(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1288
|
-
return normalizeScreeningModeArg(args, normalized) === "llm";
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
function getRunOptions(args, normalized, session, { workspaceRoot = "", configResolution = null } = {}) {
|
|
1270
|
+
function normalizeScreeningModeArg(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1271
|
+
if (!normalized.criteria) return "collect_cv";
|
|
1272
|
+
const raw = normalizeText(args.screening_mode || args.screeningMode || "");
|
|
1273
|
+
if (args.use_llm === false) return "deterministic";
|
|
1274
|
+
return ["deterministic", "local", "local_scorer"].includes(raw.toLowerCase())
|
|
1275
|
+
? "deterministic"
|
|
1276
|
+
: "llm";
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function collectChatDebugTestOptions(args = {}) {
|
|
1280
|
+
const reasons = [];
|
|
1281
|
+
if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
|
|
1282
|
+
if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
|
|
1283
|
+
if (args.dry_run === true || args.dry_run_request_cv === true) reasons.push("dry_run_request_cv");
|
|
1284
|
+
return reasons;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function shouldUseChatLlm(args = {}, normalized = normalizeChatStartInput(args)) {
|
|
1288
|
+
return normalizeScreeningModeArg(args, normalized) === "llm";
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function getRunOptions(args, normalized, session, { workspaceRoot = "", configResolution = null } = {}) {
|
|
1292
1292
|
const slowLive = args.slow_live === true;
|
|
1293
1293
|
const isAllTarget = normalized.publicTargetCount === "all";
|
|
1294
1294
|
const processedLimit = parsePositiveInteger(
|
|
1295
1295
|
args.max_candidates,
|
|
1296
1296
|
isAllTarget ? CHAT_ALL_MAX_CANDIDATES : CHAT_ALL_MAX_CANDIDATES
|
|
1297
|
-
);
|
|
1298
|
-
const shouldRequestResume = shouldRequestChatResume(args);
|
|
1299
|
-
const screeningMode = normalizeScreeningModeArg(args, normalized);
|
|
1300
|
-
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1301
|
-
const resolvedConfig = configResolution || (useLlm ? resolveBossScreeningConfig(workspaceRoot) : { ok: false });
|
|
1302
|
-
const humanBehavior = resolveHumanBehaviorForRun(args, resolvedConfig?.config || {});
|
|
1303
|
-
return {
|
|
1297
|
+
);
|
|
1298
|
+
const shouldRequestResume = shouldRequestChatResume(args);
|
|
1299
|
+
const screeningMode = normalizeScreeningModeArg(args, normalized);
|
|
1300
|
+
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1301
|
+
const resolvedConfig = configResolution || (useLlm ? resolveBossScreeningConfig(workspaceRoot) : { ok: false });
|
|
1302
|
+
const humanBehavior = resolveHumanBehaviorForRun(args, resolvedConfig?.config || {});
|
|
1303
|
+
return {
|
|
1304
1304
|
client: session.client,
|
|
1305
1305
|
targetUrl: CHAT_TARGET_URL,
|
|
1306
1306
|
job: normalized.job,
|
|
@@ -1339,7 +1339,7 @@ function getRunOptions(args, normalized, session, { workspaceRoot = "", configRe
|
|
|
1339
1339
|
llmImageDetail: normalizeText(
|
|
1340
1340
|
args.llm_image_detail || resolvedConfig.config?.llmImageDetail || resolvedConfig.config?.imageDetail
|
|
1341
1341
|
) || "low",
|
|
1342
|
-
screeningMode,
|
|
1342
|
+
screeningMode,
|
|
1343
1343
|
listMaxScrolls: parsePositiveInteger(args.list_max_scrolls, 200),
|
|
1344
1344
|
listStableSignatureLimit: parsePositiveInteger(args.list_stable_signature_limit, 2),
|
|
1345
1345
|
listWheelDeltaY: parsePositiveInteger(args.list_wheel_delta_y, 850),
|
|
@@ -1410,7 +1410,7 @@ async function startBossChatRunInternal(args = {}, { workspaceRoot = "", runId =
|
|
|
1410
1410
|
}
|
|
1411
1411
|
|
|
1412
1412
|
const shouldRequestResume = shouldRequestChatResume(args);
|
|
1413
|
-
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1413
|
+
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1414
1414
|
const debugTestOptions = collectChatDebugTestOptions(args);
|
|
1415
1415
|
if (debugTestOptions.length && !isDebugTestMode(args)) {
|
|
1416
1416
|
return {
|
|
@@ -1575,11 +1575,11 @@ export async function prepareBossChatRunTool({ workspaceRoot = "", args = {} } =
|
|
|
1575
1575
|
status: missingFields.length ? "NEED_INPUT" : "READY",
|
|
1576
1576
|
stage: "chat_run_setup",
|
|
1577
1577
|
page_url: session.navigation?.url || session.target?.url || CHAT_TARGET_URL,
|
|
1578
|
-
required_fields: CHAT_REQUIRED_FIELDS.slice(),
|
|
1579
|
-
missing_fields: missingFields,
|
|
1580
|
-
criteria_optional: true,
|
|
1581
|
-
empty_criteria_mode: "collect_cv",
|
|
1582
|
-
job_options: jobOptions,
|
|
1578
|
+
required_fields: CHAT_REQUIRED_FIELDS.slice(),
|
|
1579
|
+
missing_fields: missingFields,
|
|
1580
|
+
criteria_optional: true,
|
|
1581
|
+
empty_criteria_mode: "collect_cv",
|
|
1582
|
+
job_options: jobOptions,
|
|
1583
1583
|
selected_job: selectedJob,
|
|
1584
1584
|
selected_job_label: jobs?.selected_label || selectedJob?.label || "",
|
|
1585
1585
|
job_options_source: jobs?.source || "",
|
|
@@ -1591,10 +1591,10 @@ export async function prepareBossChatRunTool({ workspaceRoot = "", args = {} } =
|
|
|
1591
1591
|
jobOptions
|
|
1592
1592
|
}),
|
|
1593
1593
|
...diagnostics,
|
|
1594
|
-
...(nextCallExample ? { next_call_example: nextCallExample } : {}),
|
|
1595
|
-
message: missingFields.length
|
|
1596
|
-
? "已通过 CDP-only 读取 Boss 聊天页岗位列表,请补齐 job / start_from / target_count。criteria 可留空;留空会进入收集简历模式。"
|
|
1597
|
-
: "Boss chat CDP-only preflight is ready. Use start_boss_chat_run to start screening or collect CVs.",
|
|
1594
|
+
...(nextCallExample ? { next_call_example: nextCallExample } : {}),
|
|
1595
|
+
message: missingFields.length
|
|
1596
|
+
? "已通过 CDP-only 读取 Boss 聊天页岗位列表,请补齐 job / start_from / target_count。criteria 可留空;留空会进入收集简历模式。"
|
|
1597
|
+
: "Boss chat CDP-only preflight is ready. Use start_boss_chat_run to start screening or collect CVs.",
|
|
1598
1598
|
runtime_evaluate_used: false,
|
|
1599
1599
|
method_summary: methodSummary(session.methodLog || []),
|
|
1600
1600
|
method_log: session.methodLog || [],
|
|
@@ -1762,7 +1762,7 @@ export async function startBossChatDetachedRunTool({ workspaceRoot = "", args =
|
|
|
1762
1762
|
});
|
|
1763
1763
|
}
|
|
1764
1764
|
|
|
1765
|
-
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1765
|
+
const useLlm = shouldUseChatLlm(args, normalized);
|
|
1766
1766
|
const debugTestOptions = collectChatDebugTestOptions(args);
|
|
1767
1767
|
if (debugTestOptions.length && !isDebugTestMode(args)) {
|
|
1768
1768
|
return {
|