@reconcrap/boss-recommend-mcp 2.1.4 → 2.1.6
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 +10 -10
- package/package.json +1 -1
- package/skills/boss-recommend-pipeline/README.md +1 -1
- package/skills/boss-recommend-pipeline/SKILL.md +13 -13
- package/skills/boss-recruit-pipeline/SKILL.md +1 -4
- package/src/domains/recommend/actions.js +2 -3
- package/src/domains/recommend/run-service.js +32 -42
- package/src/index.js +26 -20
- package/src/parser.js +7 -33
- package/src/recommend-mcp.js +2 -3
package/README.md
CHANGED
|
@@ -82,9 +82,10 @@ curl -fsSL https://raw.githubusercontent.com/reconcrap-cpu/boss-recommend-mcp/ma
|
|
|
82
82
|
MCP 工具:
|
|
83
83
|
|
|
84
84
|
- `list_recommend_jobs`(只读读取推荐页岗位下拉框,返回可直接用于 cron/一次性任务的 `job_names`)
|
|
85
|
-
- `prepare_recommend_pipeline_run`(只校验完整 payload
|
|
85
|
+
- `prepare_recommend_pipeline_run`(只校验完整 payload;不启动筛选。若现在运行,返回 `READY + cron_ready=true` 后继续调用 `run_recommend` / `start_recommend_pipeline_run`;只有定时任务才继续 schedule)
|
|
86
86
|
- `schedule_recommend_pipeline_run`(创建 package-owned 定时任务;保存已 READY 的完整 payload,启动 detached scheduler,到点后直接调用 `start_recommend_pipeline_run`)
|
|
87
87
|
- `get_recommend_scheduled_run`(查询 package-owned 定时任务;到点后会显示内层 `run_id` 和 run 快照)
|
|
88
|
+
- `run_recommend`(`start_recommend_pipeline_run` 的短别名;适合 Trae/Trae-CN 在 prepare READY 后正式启动)
|
|
88
89
|
- `start_recommend_pipeline_run`(异步启动;同样先经过前置门禁,通过后返回 run_id)
|
|
89
90
|
- `get_recommend_pipeline_run`(轮询 run_id 状态)
|
|
90
91
|
- `cancel_recommend_pipeline_run`(取消运行中任务)
|
|
@@ -160,16 +161,16 @@ boss-recommend-mcp schedule-status --schedule-id <schedule_id>
|
|
|
160
161
|
- 学历支持单选与多选语义:如“本科及以上”会展开为 `本科/硕士/博士`;如“大专、本科”只勾选这两项
|
|
161
162
|
- 执行前会先补齐筛选值、岗位、后置动作和休息强度,然后只做一次总确认
|
|
162
163
|
- 页面就绪(已登录且在 recommend 页)后,会先提取岗位栏全部岗位,使用精确岗位名填入 run payload,再进入总确认
|
|
163
|
-
- 在真正开始 search/screen 或创建 cron 前,总确认需包含岗位、全部筛选参数、criteria、target_count、post_action
|
|
164
|
+
- 在真正开始 search/screen 或创建 cron 前,总确认需包含岗位、全部筛选参数、criteria、target_count、post_action、可选 max_greet_count、restLevel 和定时信息(如适用)
|
|
164
165
|
- npm 全局安装后会自动执行 install:生成 skill、导出 MCP 模板,并自动尝试写入已检测到的外部 agent MCP 配置(含 Trae / trae-cn / Cursor / Claude / OpenClaw)
|
|
165
166
|
- 2.x installer 会迁移已存在的 legacy Boss MCP 配置:把 `boss-recommend` 指向统一 `@reconcrap/boss-recommend-mcp`,并从同一个 `mcp.json` 中移除旧 `boss-recruit-mcp` / standalone `boss-chat` / 本地 legacy Boss 路径;写入前会生成 `.boss-mcp-migration-*.bak` 备份
|
|
166
167
|
- 2.x installer 会刷新外部 agent skills:`boss-recommend-pipeline`、`boss-recruit-pipeline`、`boss-chat` 都来自当前包,旧 recruit/chat skill 会被覆盖为统一 MCP 路由
|
|
167
168
|
- npm / npx 安装后会自动初始化 `screening-config.json` 模板(优先写入 workspace 的 `config/`,不可写时回退到用户目录)
|
|
168
169
|
- npm 安装流程会预创建运行目录(跨平台):`~/.boss-recommend-mcp`、`~/.boss-recommend-mcp/runs`、`~/.boss-recommend-mcp/boss-chat` 及其 `logs/runs/profiles/reports/artifacts/state`
|
|
169
|
-
- `post_action`、`target_count`
|
|
170
|
+
- `post_action`、`target_count` 和可选 `max_greet_count` 通过同一次总确认锁定
|
|
170
171
|
- 新流程中 `confirmation.final_confirmed=true` 是总确认;旧版逐字段 `*_confirmed` JSON 仍兼容但不是推荐写法
|
|
171
172
|
- 一旦确认 `post_action`,本次运行内所有通过人选都统一按该动作执行
|
|
172
|
-
-
|
|
173
|
+
- 若达到可选 `max_greet_count` 但流程仍需继续,后续通过人选会继续筛选但不再执行打招呼动作
|
|
173
174
|
- 不会对每位候选人重复确认
|
|
174
175
|
- 推荐页详情处理完成后,会强制关闭详情页并确认已关闭
|
|
175
176
|
- 简历提取优先使用 Network 响应;没有可解析 Network CV 时,回退到完整滚动截图序列再交给多模态模型判断
|
|
@@ -264,7 +265,7 @@ BOSS_RECOMMEND_MCP_CONFIG_TARGETS # JSON 数组或系统 path 分隔路径列
|
|
|
264
265
|
BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS # JSON 数组或系统 path 分隔路径列表,指定额外 skills 根目录
|
|
265
266
|
```
|
|
266
267
|
|
|
267
|
-
推荐运行入口是 MCP 工具 `start_recommend_pipeline_run
|
|
268
|
+
推荐运行入口是 MCP 工具 `run_recommend` / `start_recommend_pipeline_run`。在 Trae/Trae-CN 这类普通 MCP 宿主中,`prepare_recommend_pipeline_run` 返回 `READY + cron_ready=true` 后,若用户要现在运行,应继续调用 `run_recommend` 或 `start_recommend_pipeline_run`,不要改用 shell/CLI。只有宿主是 QClaw 这类 shell-only agent、没有把 MCP tools 暴露给模型时,才使用 CDP-only CLI fallback:
|
|
268
269
|
|
|
269
270
|
```bash
|
|
270
271
|
npx -y @reconcrap/boss-recommend-mcp@latest run --detached --instruction-file boss-recommend-instruction.txt --overrides-file boss-recommend-overrides.json --confirmation-file boss-recommend-confirmation.json --slow-live --port 9222
|
|
@@ -464,7 +465,7 @@ Trae-CN / 长对话防循环建议:
|
|
|
464
465
|
|
|
465
466
|
当宿主 agent 对“长时间无回包”敏感(容易误判失败)时,建议改用异步工具:
|
|
466
467
|
|
|
467
|
-
1. 调用 `start_recommend_pipeline_run`。
|
|
468
|
+
1. 调用 `run_recommend`(短别名)或 `start_recommend_pipeline_run`。
|
|
468
469
|
2. 若返回 `NEED_INPUT/NEED_CONFIRMATION/FAILED`,按同步流程先补齐前置条件(登录、页面就绪、岗位确认、最终确认)。
|
|
469
470
|
3. 仅当门禁通过时,接口才会返回 `ACCEPTED + run_id`;默认不自动轮询,建议按需调用 `get_recommend_pipeline_run`(长任务至少每 30 分钟一次,除非用户明确要求更频繁)。
|
|
470
471
|
4. 若需临时中断,调用 `pause_recommend_pipeline_run`;接口会先返回 `PAUSE_REQUESTED`,随后在安全边界进入 `paused`。
|
|
@@ -474,8 +475,8 @@ Trae-CN / 长对话防循环建议:
|
|
|
474
475
|
|
|
475
476
|
说明:
|
|
476
477
|
|
|
477
|
-
- `start_recommend_pipeline_run`
|
|
478
|
-
- `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run`
|
|
478
|
+
- `run_recommend` 与 `start_recommend_pipeline_run` 是同一个异步 MCP 启动入口,但不会跳过同步确认流程。
|
|
479
|
+
- `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run` 只做参数门禁;它不启动筛选。普通 MCP 宿主现在运行时,prepare READY 后继续调用 `run_recommend` / `start_recommend_pipeline_run`,不要改用 CLI fallback。
|
|
479
480
|
- `schedule_recommend_pipeline_run` / `boss-recommend-mcp schedule-run` 是推荐页定时启动的唯一推荐路径;它创建真实 package-owned detached scheduler,并返回 `schedule_id`。
|
|
480
481
|
- 定时心跳默认 120 秒一次;`updated_at` 仍会在阶段或进度变化时刷新。
|
|
481
482
|
- 每个 run 会持久化到 `~/.boss-recommend-mcp/runs/<run_id>.json`(可通过 `BOSS_RECOMMEND_HOME` 覆盖)。
|
|
@@ -501,8 +502,7 @@ Trae-CN / 长对话防循环建议:
|
|
|
501
502
|
"criteria": "候选人需要有 AI Agent 或 MCP 工具开发经验",
|
|
502
503
|
"job": "算法工程师(视频/图像模型方向) _ 杭州",
|
|
503
504
|
"target_count": 20,
|
|
504
|
-
"post_action": "greet"
|
|
505
|
-
"max_greet_count": 10
|
|
505
|
+
"post_action": "greet"
|
|
506
506
|
},
|
|
507
507
|
"human_behavior": {
|
|
508
508
|
"restLevel": "medium"
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- 先确认推荐页 filters
|
|
8
8
|
- 再确认筛选 criteria
|
|
9
|
-
- 再确认本次运行统一动作 `
|
|
9
|
+
- 再确认本次运行统一动作 `greet` 或 `none`
|
|
10
10
|
- 每次运行都要让用户明确选择休息强度 `low` / `medium` / `high`,并传入 `human_behavior.restLevel`
|
|
11
11
|
- 只确认一次 `post_action`,不要逐个候选人反复确认
|
|
12
12
|
- 运行中临时中断请使用 `pause_recommend_pipeline_run`(按 run_id),不要靠自然语言“暂停/继续”指令
|
|
@@ -7,7 +7,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
7
7
|
|
|
8
8
|
## Goal
|
|
9
9
|
|
|
10
|
-
当用户要在 Boss 推荐页筛人时,必须走 `start_recommend_pipeline_run`;若是稍后/cron 启动,必须走 `schedule_recommend_pipeline_run`。先补齐缺失值并读取岗位列表,然后展示一次包含岗位、筛选项、criteria
|
|
10
|
+
当用户要在 Boss 推荐页筛人时,必须走 MCP 工具 `run_recommend`(短别名)或 `start_recommend_pipeline_run`;若是稍后/cron 启动,必须走 `schedule_recommend_pipeline_run`。先补齐缺失值并读取岗位列表,然后展示一次包含岗位、筛选项、criteria、目标人数、后置动作、可选最大招呼数、休息强度和定时信息的总确认;用户确认后设置 `final_confirmed=true` 即可启动或创建定时任务。2.0 CDP-only 路径不再支持 legacy recommend -> chat 自动衔接;若用户要聊天页筛选或求简历,必须在推荐页任务完成后显式改用 `boss-chat` 工具。
|
|
11
11
|
|
|
12
12
|
## Hard Rules (Must Follow)
|
|
13
13
|
|
|
@@ -31,7 +31,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
31
31
|
|
|
32
32
|
- **参数确认**
|
|
33
33
|
- `criteria` 必须是用户开放式自然语言;禁止“严格/宽松执行”等预设替代。
|
|
34
|
-
- `post_action=greet`
|
|
34
|
+
- `max_greet_count` 仅是可选的 `post_action=greet` 上限;禁止未告知用户就自动默认为 `target_count`。
|
|
35
35
|
- 正式执行前必须 `final_confirmed=true`。
|
|
36
36
|
- 真实筛选禁止传 `detail_limit: 0`;recommend 默认必须打开候选人详情/CV。只有用户明确要求“卡片-only 调试”时,才允许同时传 `detail_limit: 0` 和 `allow_card_only_screening: true`。
|
|
37
37
|
|
|
@@ -49,8 +49,8 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
49
49
|
- `school_tag`、`degree`、`gender`、`recent_not_view`
|
|
50
50
|
- `criteria`(开放文本)
|
|
51
51
|
- `target_count`(可空)
|
|
52
|
-
- `post_action`:`
|
|
53
|
-
- `max_greet_count
|
|
52
|
+
- `post_action`:`greet|none`
|
|
53
|
+
- `max_greet_count`(可选,仅当 `post_action=greet`)
|
|
54
54
|
- `rest_level`:`low|medium|high`
|
|
55
55
|
- `job`(来自 `list_recommend_jobs` / `job_options` 的精确岗位名)
|
|
56
56
|
- cron/定时任务的启动时间(如适用)
|
|
@@ -84,7 +84,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
84
84
|
- `degree`: `不限/初中及以下/中专/中技/高中/大专/本科/硕士/博士`
|
|
85
85
|
- `gender`: `不限/男/女`
|
|
86
86
|
- `recent_not_view`: `不限/近14天没有`
|
|
87
|
-
- `post_action`: `
|
|
87
|
+
- `post_action`: `greet/none`
|
|
88
88
|
- `rest_level`: `low/medium/high`
|
|
89
89
|
|
|
90
90
|
## Tool Usage
|
|
@@ -93,9 +93,9 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
93
93
|
- 用途:当用户需要为 cron / 一次性自动任务提前填写完整参数时,先用它读取推荐页岗位下拉框的全部可用岗位名;默认会复用/自动打开本机 9222 Chrome 并导航到推荐页。
|
|
94
94
|
- 输出:优先把 `job_names` 里的值作为后续 `overrides.job` / `confirmation.job_value`。
|
|
95
95
|
- 限制:只读岗位列表,不启动筛选任务;若返回 `BOSS_LOGIN_REQUIRED`,必须让用户在自动打开的 Chrome 完成登录后重试,本次 cron 不得创建。
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
96
|
+
- 准备/门禁工具:`prepare_recommend_pipeline_run`
|
|
97
|
+
- 用途:只校验参数是否完整,不启动筛选任务。
|
|
98
|
+
- 要求:若用户要“现在启动”,返回 `status=READY` 且 `cron_ready=true` 后,下一步必须调用 MCP 工具 `run_recommend` 或 `start_recommend_pipeline_run`,禁止改用 CLI fallback。只有用户要“稍后/cron/定时启动”时,才继续创建定时任务。
|
|
99
99
|
- 若返回 `NEED_INPUT` / `NEED_CONFIRMATION` / `FAILED`:继续补齐 `pending_questions` 或修复登录/页面/config;不得先创建 cron。
|
|
100
100
|
- Cron 创建工具:`schedule_recommend_pipeline_run`
|
|
101
101
|
- 用途:保存已经 READY 的完整 payload,并启动 package-owned detached scheduler;到点后由包内 worker 直接调用 `start_recommend_pipeline_run`。
|
|
@@ -103,7 +103,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
103
103
|
- 成功标准:必须返回 `status=SCHEDULED`、`schedule_created=true`、`schedule_id`、`run_at`。只有这个返回后,才可以告诉用户定时任务已创建。
|
|
104
104
|
- Cron 查询工具:`get_recommend_scheduled_run`
|
|
105
105
|
- 用途:用户问“任务是否启动/进度”时,先查 `schedule_id`。若到点后已启动,会返回内层 `run_id` 和 run 快照。
|
|
106
|
-
- 主工具:`start_recommend_pipeline_run`
|
|
106
|
+
- 主工具:`run_recommend` / `start_recommend_pipeline_run`
|
|
107
107
|
- 必填:`instruction`
|
|
108
108
|
- 关键输入:
|
|
109
109
|
- `confirmation`:新流程只需要 `{ "final_confirmed": true }`;旧版 `page_confirmed/page_value/.../job_confirmed/job_value` 仍兼容但不要主动制造逐项确认。
|
|
@@ -123,7 +123,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
|
|
|
123
123
|
创建 cron 时必须在设置 cron 的当下完成全部交互:
|
|
124
124
|
|
|
125
125
|
1. 锁定用户原始 instruction,不改写、不摘要,criteria 放入 `overrides.criteria` 时必须逐字保留。
|
|
126
|
-
2. 收集全部筛选项、`target_count`、`post_action
|
|
126
|
+
2. 收集全部筛选项、`target_count`、`post_action`、可选 `max_greet_count` 和本次 `rest_level`。
|
|
127
127
|
3. 调用 `list_recommend_jobs`;若 Chrome 未打开,工具会尝试自动打开本机 9222 Chrome 并进入推荐页。若返回 `BOSS_LOGIN_REQUIRED` 或页面不可用,停止 cron 创建,让用户登录/修复后重试。
|
|
128
128
|
4. 用 `job_names` 中的精确岗位名填入 `overrides.job`,展示包含启动时间的最终总确认;用户确认后写入 `final_confirmed=true`。
|
|
129
129
|
5. 调用 `prepare_recommend_pipeline_run` 传入将来要执行的完整 payload;只有 `READY + cron_ready=true` 才能继续。
|
|
@@ -189,7 +189,7 @@ npx -y @reconcrap/boss-recommend-mcp@latest schedule-status --schedule-id <sched
|
|
|
189
189
|
|
|
190
190
|
## QClaw / Shell-Only Fallback
|
|
191
191
|
|
|
192
|
-
当 QClaw/OpenClaw 变体只暴露 shell、没有原生 MCP tool list 时,禁止停在“请使用 `start_recommend_pipeline_run` 工具”。必须用 shell 继续启动。
|
|
192
|
+
当 QClaw/OpenClaw 变体只暴露 shell、没有原生 MCP tool list 时,禁止停在“请使用 `run_recommend` / `start_recommend_pipeline_run` 工具”。必须用 shell 继续启动。
|
|
193
193
|
|
|
194
194
|
推荐做法:
|
|
195
195
|
|
|
@@ -207,11 +207,11 @@ npx -y @reconcrap/boss-recommend-mcp@latest run --detached --instruction-file .\
|
|
|
207
207
|
兼容路径:
|
|
208
208
|
|
|
209
209
|
- 若 `--detached` 不可用,或返回 `RECOMMEND_CLI_RUN_UNSUPPORTED_CDP_ONLY`,说明 npm/QClaw 仍在使用旧包;先运行 `npx -y @reconcrap/boss-recommend-mcp@latest install --agent qclaw` 并重启 QClaw。
|
|
210
|
-
- 在包更新未生效时,可以使用已验证过的直接 MCP stdio JSON-RPC 方式调用 `start_recommend_pipeline_run`;该方式等价于原生 MCP tool 调用,不能改用 recruit/search 路径。
|
|
210
|
+
- 在包更新未生效时,可以使用已验证过的直接 MCP stdio JSON-RPC 方式调用 `run_recommend` / `start_recommend_pipeline_run`;该方式等价于原生 MCP tool 调用,不能改用 recruit/search 路径。
|
|
211
211
|
|
|
212
212
|
普通 MCP 可用时:
|
|
213
213
|
|
|
214
|
-
`start_recommend_pipeline_run`
|
|
214
|
+
`run_recommend` / `start_recommend_pipeline_run` 仍是首选。`prepare_recommend_pipeline_run` 返回 READY 后,若用户要现在启动,必须继续调用这两个 MCP 工具之一;不要声称“prepare 覆盖了 MCP run 调用”,也不要切到 CLI detached fallback。
|
|
215
215
|
|
|
216
216
|
禁止错误回退:
|
|
217
217
|
|
|
@@ -29,8 +29,7 @@ description: "Use when users want Boss search/recruit-page screening via the uni
|
|
|
29
29
|
- 只有工具返回 `BOSS_LOGIN_REQUIRED` / `requires_login=true` 时,才要求用户在自动打开的 Chrome 窗口人工登录 Boss 后重试;不要把“没开 9222 Chrome”当作缺参。
|
|
30
30
|
- 若本机找不到 Chrome,可提示用户设置 `BOSS_MCP_CHROME_PATH` 或 `BOSS_RECOMMEND_CHROME_PATH`;非本机 debug host 不自动启动。
|
|
31
31
|
- 若用户未提供岗位,必须先询问岗位。搜索页岗位选择在关键词输入框旁边;不要猜测默认岗位。
|
|
32
|
-
-
|
|
33
|
-
- `post_action=greet` 时必须确认 `max_greet_count`;不要默认等于 `target_count`。
|
|
32
|
+
- 若用户提供城市、学历、学校、关键词、过滤已看、人选目标数、筛选条件等参数,必须逐项传入或确认。
|
|
34
33
|
- 搜索页和推荐页一样支持多选筛选条件;不要把多选降级成单选。
|
|
35
34
|
- 每次 run 必须明确询问用户本次休息强度 `rest_level`:`low`(旧策略)/ `medium`(约 5 小时或 700 人累计休息 30 分钟)/ `high`(约 5 小时或 700 人累计休息 1 小时);不得默认使用配置文件里的值替用户决定。
|
|
36
35
|
|
|
@@ -48,8 +47,6 @@ description: "Use when users want Boss search/recruit-page screening via the uni
|
|
|
48
47
|
- `degree`
|
|
49
48
|
- `school_tag`
|
|
50
49
|
- `recent_not_view`
|
|
51
|
-
- `post_action`
|
|
52
|
-
- `max_greet_count`
|
|
53
50
|
- `port`
|
|
54
51
|
|
|
55
52
|
启动工具时,把用户确认的休息强度写入 `human_behavior.restLevel`,例如:
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { waitForRecommendDetail } from "./detail.js";
|
|
23
23
|
import { getRecommendRoots } from "./roots.js";
|
|
24
24
|
|
|
25
|
-
const POST_ACTIONS = new Set(["none", "
|
|
25
|
+
const POST_ACTIONS = new Set(["none", "greet"]);
|
|
26
26
|
const GREET_EXACT_LABEL_PATTERN = /^(?:打招呼|聊一聊|立即沟通(?:[\((]\d+\s*[//]\s*\d+[\))])?|沟通)$/i;
|
|
27
27
|
export const RECOMMEND_DETAIL_ACTION_TEXT_SELECTORS = Object.freeze([
|
|
28
28
|
"button",
|
|
@@ -94,7 +94,6 @@ function bestControl(controls, exactLabelPattern) {
|
|
|
94
94
|
export function normalizeRecommendPostAction(value) {
|
|
95
95
|
const normalized = normalizeText(value).toLowerCase();
|
|
96
96
|
if (["", "none", "skip", "no", "不执行", "无"].includes(normalized)) return "none";
|
|
97
|
-
if (["favorite", "fav", "collect", "收藏", "感兴趣"].includes(normalized)) return "favorite";
|
|
98
97
|
if (["greet", "chat", "打招呼", "直接沟通", "沟通"].includes(normalized)) return "greet";
|
|
99
98
|
return POST_ACTIONS.has(normalized) ? normalized : "";
|
|
100
99
|
}
|
|
@@ -110,7 +109,7 @@ export function resolveRecommendPostAction({
|
|
|
110
109
|
if (requested === "greet" && limit !== null && currentGreetCount >= limit) {
|
|
111
110
|
return {
|
|
112
111
|
requested,
|
|
113
|
-
effective: "
|
|
112
|
+
effective: "none",
|
|
114
113
|
reason: "greet_limit_reached",
|
|
115
114
|
greet_count: currentGreetCount,
|
|
116
115
|
max_greet_count: limit
|
|
@@ -283,22 +283,22 @@ async function runRecommendPostAction({
|
|
|
283
283
|
reason: ""
|
|
284
284
|
};
|
|
285
285
|
|
|
286
|
-
if (!screening?.passed) {
|
|
287
|
-
result.reason = "screening_not_passed";
|
|
288
|
-
return result;
|
|
289
|
-
}
|
|
290
|
-
if (plan.effective === "none") {
|
|
291
|
-
result.reason = "post_action_none";
|
|
292
|
-
return result;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const summary = actionDiscovery?.summary || {};
|
|
296
|
-
const control =
|
|
297
|
-
if (!control?.found) {
|
|
298
|
-
result.reason = `${plan.effective}_control_not_found`;
|
|
299
|
-
return result;
|
|
300
|
-
}
|
|
301
|
-
result.control = control;
|
|
286
|
+
if (!screening?.passed) {
|
|
287
|
+
result.reason = "screening_not_passed";
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
if (plan.effective === "none") {
|
|
291
|
+
result.reason = plan.reason === "greet_limit_reached" ? "greet_limit_reached" : "post_action_none";
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const summary = actionDiscovery?.summary || {};
|
|
296
|
+
const control = summary.greet;
|
|
297
|
+
if (!control?.found) {
|
|
298
|
+
result.reason = `${plan.effective}_control_not_found`;
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
result.control = control;
|
|
302
302
|
|
|
303
303
|
if (plan.effective === "greet" && control.continue_chat) {
|
|
304
304
|
result.reason = "already_connected_continue_chat";
|
|
@@ -315,15 +315,10 @@ async function runRecommendPostAction({
|
|
|
315
315
|
result.reason = "greet_control_not_available";
|
|
316
316
|
return result;
|
|
317
317
|
}
|
|
318
|
-
if (
|
|
319
|
-
result.reason =
|
|
320
|
-
result
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
if (control.disabled) {
|
|
324
|
-
result.reason = `${plan.effective}_control_disabled`;
|
|
325
|
-
return result;
|
|
326
|
-
}
|
|
318
|
+
if (control.disabled) {
|
|
319
|
+
result.reason = `${plan.effective}_control_disabled`;
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
327
322
|
if (!executePostAction) {
|
|
328
323
|
result.reason = "dry_run_post_action";
|
|
329
324
|
result.would_click = true;
|
|
@@ -358,23 +353,18 @@ async function runRecommendPostAction({
|
|
|
358
353
|
timeoutMs: 2500,
|
|
359
354
|
intervalMs: 300,
|
|
360
355
|
requireAny: false
|
|
361
|
-
});
|
|
362
|
-
const afterSummary = afterDiscovery?.summary || {};
|
|
363
|
-
const afterControl =
|
|
364
|
-
result.action_discovery_after = compactActionDiscovery(afterDiscovery);
|
|
365
|
-
result.control_after = afterControl || null;
|
|
366
|
-
if (plan.effective === "greet") {
|
|
367
|
-
result.verified_after_click = Boolean(
|
|
368
|
-
afterControl?.continue_chat
|
|
369
|
-
|| String(afterControl?.label || "").includes("继续沟通")
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
afterControl?.active
|
|
374
|
-
|| String(afterControl?.label || "").includes("已")
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
} catch (error) {
|
|
356
|
+
});
|
|
357
|
+
const afterSummary = afterDiscovery?.summary || {};
|
|
358
|
+
const afterControl = afterSummary.greet;
|
|
359
|
+
result.action_discovery_after = compactActionDiscovery(afterDiscovery);
|
|
360
|
+
result.control_after = afterControl || null;
|
|
361
|
+
if (plan.effective === "greet") {
|
|
362
|
+
result.verified_after_click = Boolean(
|
|
363
|
+
afterControl?.continue_chat
|
|
364
|
+
|| String(afterControl?.label || "").includes("继续沟通")
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
} catch (error) {
|
|
378
368
|
result.verify_error = {
|
|
379
369
|
message: error?.message || String(error)
|
|
380
370
|
};
|
package/src/index.js
CHANGED
|
@@ -98,6 +98,7 @@ const { version: SERVER_VERSION } = require("../package.json");
|
|
|
98
98
|
const TOOL_PREPARE_RUN = "prepare_recommend_pipeline_run";
|
|
99
99
|
const TOOL_SCHEDULE_RUN = "schedule_recommend_pipeline_run";
|
|
100
100
|
const TOOL_GET_SCHEDULED_RUN = "get_recommend_scheduled_run";
|
|
101
|
+
const TOOL_RUN_RECOMMEND = "run_recommend";
|
|
101
102
|
const TOOL_START_RUN = "start_recommend_pipeline_run";
|
|
102
103
|
const TOOL_GET_RUN = "get_recommend_pipeline_run";
|
|
103
104
|
const TOOL_CANCEL_RUN = "cancel_recommend_pipeline_run";
|
|
@@ -581,13 +582,13 @@ function createRunInputSchema() {
|
|
|
581
582
|
minimum: 1
|
|
582
583
|
},
|
|
583
584
|
post_action_confirmed: { type: "boolean" },
|
|
584
|
-
post_action_value: {
|
|
585
|
-
type: "string",
|
|
586
|
-
enum: ["
|
|
587
|
-
},
|
|
585
|
+
post_action_value: {
|
|
586
|
+
type: "string",
|
|
587
|
+
enum: ["greet", "none"]
|
|
588
|
+
},
|
|
588
589
|
final_confirmed: {
|
|
589
590
|
type: "boolean",
|
|
590
|
-
description: "用户已确认包含岗位、筛选项、criteria
|
|
591
|
+
description: "用户已确认包含岗位、筛选项、criteria、目标、动作、可选最大招呼数和 restLevel 的总览。"
|
|
591
592
|
},
|
|
592
593
|
job_confirmed: { type: "boolean" },
|
|
593
594
|
job_value: { type: "string" },
|
|
@@ -652,10 +653,10 @@ function createRunInputSchema() {
|
|
|
652
653
|
job: { type: "string" },
|
|
653
654
|
target_count: { type: "integer", minimum: 1 },
|
|
654
655
|
max_greet_count: { type: "integer", minimum: 1 },
|
|
655
|
-
post_action: {
|
|
656
|
-
type: "string",
|
|
657
|
-
enum: ["
|
|
658
|
-
}
|
|
656
|
+
post_action: {
|
|
657
|
+
type: "string",
|
|
658
|
+
enum: ["greet", "none"]
|
|
659
|
+
}
|
|
659
660
|
},
|
|
660
661
|
additionalProperties: false
|
|
661
662
|
},
|
|
@@ -781,26 +782,26 @@ function createRunInputSchema() {
|
|
|
781
782
|
},
|
|
782
783
|
execute_post_action: {
|
|
783
784
|
type: "boolean",
|
|
784
|
-
description: "可选,是否实际执行通过后的 recommend 后置动作
|
|
785
|
+
description: "可选,是否实际执行通过后的 recommend 后置动作 greet;默认 true"
|
|
785
786
|
},
|
|
786
787
|
dry_run_post_action: {
|
|
787
788
|
type: "boolean",
|
|
788
|
-
description: "可选,只验证 recommend
|
|
789
|
+
description: "可选,只验证 recommend 打招呼动作发现/配额/可点击路径,不实际点击"
|
|
789
790
|
},
|
|
790
791
|
action_timeout_ms: {
|
|
791
792
|
type: "integer",
|
|
792
793
|
minimum: 1000,
|
|
793
|
-
description: "可选,等待详情页
|
|
794
|
+
description: "可选,等待详情页 greet 控件出现的超时时间"
|
|
794
795
|
},
|
|
795
796
|
action_interval_ms: {
|
|
796
797
|
type: "integer",
|
|
797
798
|
minimum: 100,
|
|
798
|
-
description: "可选,轮询详情页
|
|
799
|
+
description: "可选,轮询详情页 greet 控件的间隔"
|
|
799
800
|
},
|
|
800
801
|
action_after_click_delay_ms: {
|
|
801
802
|
type: "integer",
|
|
802
803
|
minimum: 0,
|
|
803
|
-
description: "可选,点击
|
|
804
|
+
description: "可选,点击 greet 后等待页面状态稳定的时间"
|
|
804
805
|
},
|
|
805
806
|
no_filter: {
|
|
806
807
|
type: "boolean",
|
|
@@ -1134,7 +1135,7 @@ function createToolsSchema() {
|
|
|
1134
1135
|
},
|
|
1135
1136
|
{
|
|
1136
1137
|
name: TOOL_PREPARE_RUN,
|
|
1137
|
-
description: "只校验 Boss
|
|
1138
|
+
description: "只校验 Boss 推荐页流水线参数是否完整;不会启动筛选任务。若用户要现在运行,返回 READY/cron_ready=true 后必须继续调用 MCP 工具 run_recommend 或 start_recommend_pipeline_run;只有定时/稍后运行才调用 schedule_recommend_pipeline_run。不要因为 prepare READY 而改用 shell/CLI fallback。",
|
|
1138
1139
|
inputSchema: createRunInputSchema()
|
|
1139
1140
|
},
|
|
1140
1141
|
{
|
|
@@ -1154,11 +1155,16 @@ function createToolsSchema() {
|
|
|
1154
1155
|
additionalProperties: false
|
|
1155
1156
|
}
|
|
1156
1157
|
},
|
|
1158
|
+
{
|
|
1159
|
+
name: TOOL_RUN_RECOMMEND,
|
|
1160
|
+
description: "立即通过 MCP 启动 Boss 推荐页筛选。它是 start_recommend_pipeline_run 的短别名,适合 Trae/Trae-CN 等代理在 prepare_recommend_pipeline_run 返回 READY 后继续正式运行;MCP 内部会按需使用包内 detached worker,普通 MCP 可用时不要改用 CLI fallback。",
|
|
1161
|
+
inputSchema: createRunInputSchema()
|
|
1162
|
+
},
|
|
1157
1163
|
{
|
|
1158
1164
|
name: TOOL_START_RUN,
|
|
1159
|
-
description: "异步启动 Boss
|
|
1165
|
+
description: "立即通过 MCP 异步启动 Boss 推荐页流水线(含同步门禁预检);prepare_recommend_pipeline_run 返回 READY 后,如果用户要现在运行就调用本工具或 run_recommend。MCP 内部会按需使用包内 detached worker,普通 MCP 可用时不要改用 CLI fallback。",
|
|
1160
1166
|
inputSchema: createRunInputSchema()
|
|
1161
|
-
},
|
|
1167
|
+
},
|
|
1162
1168
|
{
|
|
1163
1169
|
name: TOOL_GET_RUN,
|
|
1164
1170
|
description: "按 run_id 查询异步/同步流水线运行状态快照。",
|
|
@@ -2437,7 +2443,7 @@ async function handleRunFeaturedCalibrationTool({ workspaceRoot, args }) {
|
|
|
2437
2443
|
calibration_resolution: getFeaturedCalibrationResolution(workspaceRoot),
|
|
2438
2444
|
guidance: {
|
|
2439
2445
|
current_workaround: "Use an existing favorite-calibration.json if present; get_featured_calibration_status reports whether it is usable.",
|
|
2440
|
-
next_development_task: "Implement CDP-only featured calibration with explicit user approval for any
|
|
2446
|
+
next_development_task: "Implement CDP-only featured calibration with explicit user approval for any calibration click."
|
|
2441
2447
|
}
|
|
2442
2448
|
};
|
|
2443
2449
|
}
|
|
@@ -2625,7 +2631,7 @@ async function handleRequest(message, workspaceRoot) {
|
|
|
2625
2631
|
const toolName = params?.name;
|
|
2626
2632
|
const args = params?.arguments || {};
|
|
2627
2633
|
|
|
2628
|
-
if (
|
|
2634
|
+
if ([TOOL_RUN_RECOMMEND, TOOL_START_RUN].includes(toolName)) {
|
|
2629
2635
|
const inputError = validateRunArgs(args);
|
|
2630
2636
|
if (inputError) {
|
|
2631
2637
|
return createJsonRpcError(id, -32602, inputError);
|
|
@@ -2689,7 +2695,7 @@ async function handleRequest(message, workspaceRoot) {
|
|
|
2689
2695
|
payload = await scheduleRecommendPipelineRunTool({ workspaceRoot, args });
|
|
2690
2696
|
} else if (toolName === TOOL_GET_SCHEDULED_RUN) {
|
|
2691
2697
|
payload = getRecommendScheduledRunTool({ args });
|
|
2692
|
-
} else if (
|
|
2698
|
+
} else if ([TOOL_RUN_RECOMMEND, TOOL_START_RUN].includes(toolName)) {
|
|
2693
2699
|
payload = await handleStartRunTool({ workspaceRoot, args });
|
|
2694
2700
|
} else if (toolName === TOOL_GET_RUN) {
|
|
2695
2701
|
payload = handleGetRunTool(args);
|
package/src/parser.js
CHANGED
|
@@ -32,9 +32,8 @@ const FILTER_CONFIRM_OPTIONS = [
|
|
|
32
32
|
{ label: "筛选项无误,继续", value: "confirm" },
|
|
33
33
|
{ label: "筛选项需要调整", value: "revise" }
|
|
34
34
|
];
|
|
35
|
-
const POST_ACTION_OPTIONS = ["
|
|
35
|
+
const POST_ACTION_OPTIONS = ["greet", "none"];
|
|
36
36
|
const POST_ACTION_LABELS = {
|
|
37
|
-
favorite: "收藏",
|
|
38
37
|
greet: "直接沟通",
|
|
39
38
|
none: "什么也不做"
|
|
40
39
|
};
|
|
@@ -306,7 +305,6 @@ function normalizeRecentNotView(value) {
|
|
|
306
305
|
function normalizePostAction(value) {
|
|
307
306
|
const normalized = normalizeText(value).toLowerCase();
|
|
308
307
|
if (!normalized) return null;
|
|
309
|
-
if (["favorite", "fav", "收藏"].includes(normalized)) return "favorite";
|
|
310
308
|
if (["greet", "chat", "打招呼", "直接沟通", "沟通"].includes(normalized)) return "greet";
|
|
311
309
|
if (["none", "noop", "no-op", "什么也不做", "不做任何操作", "不操作", "仅筛选", "只筛选"].includes(normalized)) {
|
|
312
310
|
return "none";
|
|
@@ -551,9 +549,7 @@ function resolvePostAction({ instruction, confirmation, overrides, finalConfirme
|
|
|
551
549
|
const confirmationValue = normalizePostAction(confirmation?.post_action_value);
|
|
552
550
|
const overrideValue = normalizePostAction(overrides?.post_action);
|
|
553
551
|
const instructionValue =
|
|
554
|
-
|
|
555
|
-
? "favorite"
|
|
556
|
-
: /打招呼|直接沟通|沟通/.test(instruction)
|
|
552
|
+
/打招呼|直接沟通|沟通/.test(instruction)
|
|
557
553
|
? "greet"
|
|
558
554
|
: /什么也不做|不做任何操作|不操作|仅筛选|只筛选/.test(instruction)
|
|
559
555
|
? "none"
|
|
@@ -596,37 +592,16 @@ function resolveMaxGreetCount({ instruction, confirmation, overrides, postAction
|
|
|
596
592
|
}
|
|
597
593
|
|
|
598
594
|
const overrideValue = parsePositiveIntegerValue(overrides?.max_greet_count);
|
|
599
|
-
const confirmed = confirmation?.max_greet_count_confirmed === true;
|
|
600
595
|
const confirmationValue = parsePositiveIntegerValue(confirmation?.max_greet_count_value);
|
|
601
596
|
const instructionValue = extractMaxGreetCount(instruction);
|
|
602
|
-
const targetCountHint = (
|
|
603
|
-
parsePositiveIntegerValue(confirmation?.target_count_value)
|
|
604
|
-
|| parsePositiveIntegerValue(overrides?.target_count)
|
|
605
|
-
|| extractTargetCount(instruction)
|
|
606
|
-
|| null
|
|
607
|
-
);
|
|
608
597
|
const proposed = confirmationValue || overrideValue || instructionValue || null;
|
|
609
|
-
const resolved =
|
|
610
|
-
? (confirmationValue || overrideValue || instructionValue || null)
|
|
611
|
-
: null;
|
|
612
|
-
const suspiciousAutoFill = Boolean(
|
|
613
|
-
!confirmed
|
|
614
|
-
&& !finalConfirmed
|
|
615
|
-
&& Number.isInteger(proposed)
|
|
616
|
-
&& proposed > 0
|
|
617
|
-
&& !Number.isInteger(instructionValue)
|
|
618
|
-
&& Number.isInteger(targetCountHint)
|
|
619
|
-
&& targetCountHint > 0
|
|
620
|
-
&& proposed === targetCountHint
|
|
621
|
-
);
|
|
622
|
-
const hasProposedValue = Number.isInteger(proposed) && proposed > 0;
|
|
623
|
-
const needsConfirmation = !hasProposedValue;
|
|
598
|
+
const resolved = confirmationValue || overrideValue || instructionValue || null;
|
|
624
599
|
|
|
625
600
|
return {
|
|
626
|
-
max_greet_count:
|
|
601
|
+
max_greet_count: resolved,
|
|
627
602
|
proposed_max_greet_count: proposed,
|
|
628
|
-
needs_max_greet_count_confirmation:
|
|
629
|
-
suspicious_auto_fill:
|
|
603
|
+
needs_max_greet_count_confirmation: false,
|
|
604
|
+
suspicious_auto_fill: false
|
|
630
605
|
};
|
|
631
606
|
}
|
|
632
607
|
|
|
@@ -838,7 +813,6 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
838
813
|
question: "请确认本次运行对通过人选统一执行的动作。",
|
|
839
814
|
value: postActionResolution.proposed_post_action,
|
|
840
815
|
options: [
|
|
841
|
-
{ label: POST_ACTION_LABELS.favorite, value: "favorite" },
|
|
842
816
|
{ label: POST_ACTION_LABELS.greet, value: "greet" },
|
|
843
817
|
{ label: POST_ACTION_LABELS.none, value: "none" }
|
|
844
818
|
]
|
|
@@ -850,7 +824,7 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
850
824
|
field: "max_greet_count",
|
|
851
825
|
question: maxGreetCountResolution.suspicious_auto_fill
|
|
852
826
|
? "检测到最大打招呼人数可能是自动默认值,请明确确认本次最多打招呼多少位候选人(必须为正整数)。"
|
|
853
|
-
: "
|
|
827
|
+
: "本次选择直接沟通时,最多打招呼多少位候选人?可留空表示不单独限制打招呼人数。",
|
|
854
828
|
value: maxGreetCountResolution.proposed_max_greet_count
|
|
855
829
|
});
|
|
856
830
|
}
|
package/src/recommend-mcp.js
CHANGED
|
@@ -1088,10 +1088,9 @@ function buildRequiredConfirmations(parsed, args = {}) {
|
|
|
1088
1088
|
if (parsed.needs_degree_confirmation) required.push("degree");
|
|
1089
1089
|
if (parsed.needs_gender_confirmation) required.push("gender");
|
|
1090
1090
|
if (parsed.needs_recent_not_view_confirmation) required.push("recent_not_view");
|
|
1091
|
-
if (parsed.needs_criteria_confirmation) required.push("criteria");
|
|
1092
|
-
if (parsed.needs_target_count_confirmation) required.push("target_count");
|
|
1091
|
+
if (parsed.needs_criteria_confirmation) required.push("criteria");
|
|
1092
|
+
if (parsed.needs_target_count_confirmation) required.push("target_count");
|
|
1093
1093
|
if (parsed.needs_post_action_confirmation) required.push("post_action");
|
|
1094
|
-
if (parsed.needs_max_greet_count_confirmation) required.push("max_greet_count");
|
|
1095
1094
|
if ((parsed.suspicious_fields || []).length) required.push("suspicious_fields");
|
|
1096
1095
|
|
|
1097
1096
|
const confirmation = args.confirmation || {};
|