@reconcrap/boss-recommend-mcp 2.1.9 → 2.1.11

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 CHANGED
@@ -82,12 +82,13 @@ 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;不启动筛选。若现在运行,返回 `READY + cron_ready=true` 后继续调用 `run_recommend` / `start_recommend_pipeline_run`;只有定时任务才继续 schedule)
86
- - `schedule_recommend_pipeline_run`(创建 package-owned 定时任务;保存已 READY 的完整 payload,启动 detached scheduler,到点后直接调用 `start_recommend_pipeline_run`)
85
+ - `run_recommend`(`start_recommend_pipeline_run` 的短别名;用户已经确认且要现在启动时优先调用)
86
+ - `start_recommend_pipeline_run`(异步启动;先经过前置门禁,通过后返回 `ACCEPTED + run_id`)
87
+ - `prepare_recommend_pipeline_run`(只校验完整 payload;不启动筛选。主要用于显式预检或定时任务前校验;若现在运行,READY 后继续调用 `run_recommend` / `start_recommend_pipeline_run`)
88
+ - `schedule_recommend_pipeline_run`(只用于用户明确要求稍后/cron/定时;保存已 READY 的完整 payload,启动 detached scheduler,到点后直接调用 `start_recommend_pipeline_run`)
87
89
  - `get_recommend_scheduled_run`(查询 package-owned 定时任务;到点后会显示内层 `run_id` 和 run 快照)
88
- - `run_recommend`(`start_recommend_pipeline_run` 的短别名;适合 Trae/Trae-CN 在 prepare READY 后正式启动)
89
- - `start_recommend_pipeline_run`(异步启动;同样先经过前置门禁,通过后返回 run_id)
90
- - `get_recommend_pipeline_run`(轮询 run_id 状态)
90
+ - `get_recommend_pipeline_run`(用已知 `run_id` 轮询状态)
91
+ - `list_recommend_pipeline_runs`(只读列出最近 run 摘要并返回 `latest_run`;忘记 `run_id` 时用它恢复,不要读磁盘 JSON
91
92
  - `cancel_recommend_pipeline_run`(取消运行中任务)
92
93
  - `pause_recommend_pipeline_run`(请求暂停 run;会在当前候选人处理完成后进入 paused)
93
94
  - `resume_recommend_pipeline_run`(继续 paused run;沿用同 run_id 与同 CSV)
@@ -99,6 +100,7 @@ MCP 工具:
99
100
  - `resume_recruit_pipeline_run`
100
101
  - `cancel_recruit_pipeline_run`
101
102
  - `boss_chat_health_check`
103
+ - `list_boss_chat_jobs`(只读读取聊天页岗位列表;chat-only 获取 `job_options` 的首选别名,不会启动任务)
102
104
  - `prepare_boss_chat_run`
103
105
  - `start_boss_chat_run`
104
106
  - `get_boss_chat_run`
@@ -265,7 +267,7 @@ BOSS_RECOMMEND_MCP_CONFIG_TARGETS # JSON 数组或系统 path 分隔路径列
265
267
  BOSS_RECOMMEND_EXTERNAL_SKILL_DIRS # JSON 数组或系统 path 分隔路径列表,指定额外 skills 根目录
266
268
  ```
267
269
 
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`,不要改用 terminal/shell/run_command/CLI/manual JSON-RPC,也不要用短延迟 `schedule_recommend_pipeline_run` 冒充立即启动。`prepare` 能返回结果就证明该宿主已经可以调用本 MCP server。
270
+ 推荐运行入口是 MCP 工具 `run_recommend` / `start_recommend_pipeline_run`。在 Trae/Trae-CN 这类普通 MCP 宿主中,用户完成总确认并要求现在运行时,应直接调用 `run_recommend` 或 `start_recommend_pipeline_run`。`prepare_recommend_pipeline_run` 只做显式预检或定时任务前校验;如果已经调用过 prepare 且返回 `READY + cron_ready=true`,下一步仍然必须调用 `run_recommend` / `start_recommend_pipeline_run`,不要改用 terminal/shell/run_command/PowerShell/CLI/manual JSON-RPC,也不要用短延迟 `schedule_recommend_pipeline_run` 冒充立即启动。`prepare` 能返回结果就证明该宿主已经可以调用本 MCP server。
269
271
 
270
272
  只有宿主是 QClaw 这类真正 shell-only agent、没有把 MCP tools 暴露给模型、且当前会话从未成功调用过 `boss-recommend/prepare_recommend_pipeline_run` 时,才使用 CDP-only CLI fallback。CLI fallback 也必须显式传本次用户确认的 rest level:
271
273
 
@@ -423,7 +425,7 @@ node src/cli.js chat prepare-run --slow-live --port 9222
423
425
  - `baseUrl` / `apiKey` / `model` 不再单独传入,固定复用 recommend 的 `screening-config.json`
424
426
  - `greeting_text` 默认优先级:本次显式值 > `screening-config.json.greetingMessage` > 内置默认招呼语(`Hi同学,能麻烦发下简历吗?`)
425
427
  - 若缺少 `follow_up.chat` 必填项,pipeline 会返回 `NEED_INPUT`
426
- - 如需聊天页筛选,请调用 `prepare_boss_chat_run` 获取岗位列表,再调用 `start_boss_chat_run`。
428
+ - 如需聊天页筛选,请调用 `list_boss_chat_jobs` 或 `prepare_boss_chat_run` 获取岗位列表,再调用 `start_boss_chat_run`。chat-only、未读、全部聊天、求简历任务不要调用 `list_recommend_jobs` / `run_recommend` / `start_recommend_pipeline_run`;这些 recommend 工具会对明确的 chat/search 误路由 fail closed。
427
429
  - `boss-chat` 状态统一写入 `~/.boss-recommend-mcp/boss-chat`(或 `BOSS_CHAT_HOME` 指定目录),不再依赖工作区 `cwd`
428
430
 
429
431
  ## Chat-only
@@ -437,6 +439,7 @@ node src/cli.js chat prepare-run --slow-live --port 9222
437
439
  - `boss-recommend-mcp chat get-run|pause-run|resume-run|cancel-run`
438
440
  - MCP:
439
441
  - `boss_chat_health_check`
442
+ - `list_boss_chat_jobs`
440
443
  - `prepare_boss_chat_run`
441
444
  - `start_boss_chat_run`
442
445
  - `get_boss_chat_run`
@@ -449,14 +452,15 @@ node src/cli.js chat prepare-run --slow-live --port 9222
449
452
 
450
453
  chat-only 交互建议:
451
454
 
452
- - 先调用一次 `prepare_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
455
+ - 先调用一次 `list_boss_chat_jobs` 或 `prepare_boss_chat_run`(可不带参数),服务会先导航到 `https://www.zhipin.com/web/chat/index` 并返回 `NEED_INPUT`,其中包含岗位 `job_options` 与待补字段。
453
456
  - 然后基于 `job_options` 让用户选择 `job`,并补齐 `start_from`、`target_count`、`criteria` 后调用 `start_boss_chat_run` 启动任务。
454
457
  - `greeting_text` 可选;未传时使用 `screening-config.json.greetingMessage`,若未配置则使用默认招呼语(`Hi同学,能麻烦发下简历吗?`)。
455
458
  - `target_count` 支持正整数、`all`、`-1`;若用户给出 `全部候选人` / `所有候选人`,会自动按不限(扫到底)处理。
456
459
 
457
460
  Trae-CN / 长对话防循环建议:
458
461
 
459
- - 固定流程:`boss_chat_health_check` -> `prepare_boss_chat_run(空参可)` -> 一次性补齐 `job/start_from/target_count/criteria` -> `start_boss_chat_run`。
462
+ - 固定流程:`boss_chat_health_check` -> `list_boss_chat_jobs(空参可)` / `prepare_boss_chat_run(空参可)` -> 一次性补齐 `job/start_from/target_count/criteria` -> `start_boss_chat_run`。
463
+ - chat-only 场景严禁调用 `list_recommend_jobs`、`run_recommend` 或 `start_recommend_pipeline_run`。
460
464
  - `start_boss_chat_run` 的工具 schema 已把 `job/start_from/target_count/criteria` 标记为必填;不要用它获取岗位列表。
461
465
  - 若 `pending_questions` / UI 选项里出现“扫到底(必须传 `target_count="all"`)”,下一次工具调用请直接照抄 `"target_count": "all"`,不要只保留“扫到底”这层自然语言语义。
462
466
  - `start_boss_chat_run` 返回 `ACCEPTED` 后直接结束当前回合,不要自动轮询。
@@ -477,10 +481,11 @@ Trae-CN / 长对话防循环建议:
477
481
 
478
482
  说明:
479
483
 
480
- - `run_recommend` 与 `start_recommend_pipeline_run` 是同一个异步 MCP 启动入口,但不会跳过同步确认流程。
481
- - `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run` 只做参数门禁;它不启动筛选。普通 MCP 宿主现在运行时,prepare READY 后继续调用 `run_recommend` / `start_recommend_pipeline_run`,不要改用 CLI fallback。
484
+ - `run_recommend` 与 `start_recommend_pipeline_run` 是同一个异步 MCP 启动入口,但不会跳过同步确认流程;普通 MCP 宿主现在运行时优先直接调用它们。
485
+ - `prepare_recommend_pipeline_run` / `boss-recommend-mcp prepare-run` 只做参数门禁;它不启动筛选。普通 MCP 宿主只有在显式预检或定时任务准备时才需要先调用 prepare;prepare READY 后继续调用 `run_recommend` / `start_recommend_pipeline_run`,不要改用 CLI fallback。
482
486
  - `prepare_recommend_pipeline_run` 的 READY 响应会带 `prepared_only=true`、`run_started=false`、`recommended_next_tool=start_recommend_pipeline_run`、`alternate_next_tool=run_recommend` 和 `next_action.do_not_call_prepare_again=true`;agent 应直接照这个字段继续下一步。
483
487
  - `schedule_recommend_pipeline_run` / `boss-recommend-mcp schedule-run` 是推荐页定时启动的唯一推荐路径;它创建真实 package-owned detached scheduler,并返回 `schedule_id`。
488
+ - 如果忘记了 `run_id`,调用 `list_recommend_pipeline_runs` 获取 `latest_run.run_id`;不要用 PowerShell、`Get-Content`、terminal、CLI 或手动读取 `~/.boss-recommend-mcp/runs/*.json` 来恢复状态。
484
489
  - 定时心跳默认 120 秒一次;`updated_at` 仍会在阶段或进度变化时刷新。
485
490
  - 每个 run 会持久化到 `~/.boss-recommend-mcp/runs/<run_id>.json`(可通过 `BOSS_RECOMMEND_HOME` 覆盖)。
486
491
  - screen 阶段会持久化 checkpoint:`~/.boss-recommend-mcp/runs/<run_id>.checkpoint.json`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -16,7 +16,7 @@ Please run a Boss chat-only task (do not switch to recommend flow).
16
16
 
17
17
  Execution order:
18
18
  1) Call boss_chat_health_check.
19
- 2) Call prepare_boss_chat_run once (empty params allowed) to fetch job_options and missing fields.
19
+ 2) Call list_boss_chat_jobs once (empty params allowed), or prepare_boss_chat_run once, to fetch job_options and missing fields.
20
20
  3) Ask for these required fields in one shot: job, start_from (unread/all), target_count, criteria, rest_level (low/medium/high).
21
21
  4) After user reply, call start_boss_chat_run exactly once to start the run.
22
22
  5) If ACCEPTED, reply only with run_id and "task started"; no auto polling.
@@ -24,6 +24,8 @@ Execution order:
24
24
  Anti-loop rules:
25
25
  - Do not repeat the same sentence across turns.
26
26
  - On validation errors, list all missing/invalid fields once.
27
+ - Do not call list_recommend_jobs for chat-only tasks; it is recommend-page only and will switch the browser to /web/chat/recommend.
28
+ - Do not call run_recommend or start_recommend_pipeline_run for chat-only tasks; use start_boss_chat_run.
27
29
  - Do not use start_boss_chat_run for preflight. It is only for the final start call and must include job/start_from/target_count/criteria.
28
30
  - Do not call start_boss_chat_run repeatedly in one turn.
29
31
  - Do not call get_boss_chat_run unless user explicitly asks for progress.
@@ -14,6 +14,7 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
14
14
  ## Tool Routing
15
15
 
16
16
  - 健康检查:`boss_chat_health_check`
17
+ - 只读获取聊天页岗位列表:`list_boss_chat_jobs`
17
18
  - 预备并获取岗位列表:`prepare_boss_chat_run`
18
19
  - 启动异步任务:`start_boss_chat_run`
19
20
  - 查询进度:`get_boss_chat_run`
@@ -69,6 +70,8 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
69
70
  - 若本机找不到 Chrome,可提示用户设置 `BOSS_MCP_CHROME_PATH` 或 `BOSS_RECOMMEND_CHROME_PATH`;非本机 debug host 不自动启动。
70
71
  - `job` / `start_from` / `criteria` 缺一不可;缺参时只补缺口。
71
72
  - `target_count` 在 chat-only 启动前也是必填项,不能默认省略。
73
+ - chat-only 岗位列表只能通过 `list_boss_chat_jobs` 或 `prepare_boss_chat_run` 获取;严禁调用 `list_recommend_jobs`,因为它会切到推荐页。
74
+ - chat-only 启动只能调用 `start_boss_chat_run`;严禁调用 `run_recommend` 或 `start_recommend_pipeline_run`。
72
75
  - 每次 run 必须明确询问用户本次休息强度 `rest_level`:`low`(旧策略)/ `medium`(约 5 小时或 700 人累计休息 30 分钟)/ `high`(约 5 小时或 700 人累计休息 1 小时);不得默认使用配置文件里的值替用户决定。
73
76
  - 当用户说“全部候选人/所有候选人”时,必须按“扫到底(unlimited)”处理,不要再追问正整数。
74
77
  - 参数名必须写 `target_count`(不要写“目标数量”等中文键名)。
@@ -77,7 +80,7 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
77
80
  - 若工具或提问选项里出现“扫到底(必须传 `target_count=\"all\"`)”之类字样,下一次工具调用时必须直接照抄这个字面量,不要只保留“扫到底”语义。
78
81
  - 禁止 agent 自行补全 `job/start_from/criteria` 并直接执行,必须由用户明确给出或确认。
79
82
  - chat-only 启动流程必须先进入聊天页并拉取岗位列表,再让用户从列表中选择 `job`。
80
- - 必须先用空参调用 `prepare_boss_chat_run` 获取 `job_options`;不要用 `start_boss_chat_run` 做预备调用。
83
+ - 必须先用空参调用 `list_boss_chat_jobs` 或 `prepare_boss_chat_run` 获取 `job_options`;不要用 `start_boss_chat_run` 做预备调用。
81
84
  - `start_boss_chat_run` 只能用于真正启动,必须一次性传齐 `job` / `start_from` / `target_count` / `criteria`。
82
85
  - 若 `start_boss_chat_run` 返回 `NEED_INPUT` 且 `missing_fields` 包含 `target_count`,说明你没有把用户选择写入工具参数;下一次调用必须照 `next_call_example` 原样补上 `"target_count": "all"` 或正整数,不要重复空调用。
83
86
  - 默认不自动轮询;只有用户要求查进度时才调用 `get_boss_chat_run`。
@@ -96,6 +99,6 @@ description: "Use when users want Boss chat-page screening/outreach via the bund
96
99
  ## Response Style
97
100
 
98
101
  - 用结构化中文。
99
- - 首轮建议先调用一次 `prepare_boss_chat_run`(可空参)获取 `job_options` 与 `pending_questions`。
102
+ - 首轮建议先调用一次 `list_boss_chat_jobs`(可空参)或 `prepare_boss_chat_run`(可空参)获取 `job_options` 与 `pending_questions`。
100
103
  - 缺参时必须逐项确认:`job`(来自岗位列表)、`start_from`(`unread|all`)、`target_count`、`criteria`、`rest_level`。
101
104
  - 若健康检查失败,明确提示共享配置文件 `screening-config.json` 不可用。
@@ -18,6 +18,7 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
18
18
  - recommend 失败时(如 `JOB_TRIGGER_NOT_FOUND/NO_RECOMMEND_IFRAME/BOSS_LOGIN_REQUIRED`)禁止降级到 recruit;先修 recommend 页面就绪/登录态。
19
19
 
20
20
  - **Trae-CN / 原生 MCP 启动强制规则**
21
+ - 用户完成总确认并要求“现在启动”时,优先直接调用 `boss-recommend/run_recommend` 或 `boss-recommend/start_recommend_pipeline_run`;不要为了即时启动先调用 prepare。
21
22
  - 如果当前会话里已经成功调用过 `boss-recommend/prepare_recommend_pipeline_run`,说明 Trae-CN 已经具备原生 MCP tool call 能力;下一步必须继续调用 `boss-recommend/start_recommend_pipeline_run` 或 `boss-recommend/run_recommend`。
22
23
  - `prepare_recommend_pipeline_run` 返回 `READY` 只代表参数校验通过,不代表已经启动;严禁再次调用 prepare 试图启动。
23
24
  - 在 Trae-CN、Trae、Codex、Cursor、Claude Code 等普通 MCP 宿主中,严禁用 `run_command`、终端、PowerShell、`npx ... run --detached`、手写 `tools/call` JSON-RPC 或任何 CLI fallback 启动 recommend run。
@@ -100,8 +101,18 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
100
101
  - 用途:当用户需要为 cron / 一次性自动任务提前填写完整参数时,先用它读取推荐页岗位下拉框的全部可用岗位名;默认会复用/自动打开本机 9222 Chrome 并导航到推荐页。
101
102
  - 输出:优先把 `job_names` 里的值作为后续 `overrides.job` / `confirmation.job_value`。
102
103
  - 限制:只读岗位列表,不启动筛选任务;若返回 `BOSS_LOGIN_REQUIRED`,必须让用户在自动打开的 Chrome 完成登录后重试,本次 cron 不得创建。
104
+ - 主工具:`run_recommend` / `start_recommend_pipeline_run`
105
+ - 用途:用户完成最终总确认并要“现在启动”时调用;这是即时运行的首选入口,不需要先 prepare。
106
+ - 必填:`instruction`
107
+ - 关键输入:
108
+ - `confirmation`:新流程只需要 `{ "final_confirmed": true }`;旧版 `page_confirmed/page_value/.../job_confirmed/job_value` 仍兼容但不要主动制造逐项确认。
109
+ - `overrides`:`page_scope/school_tag/degree/gender/recent_not_view/criteria/job/target_count/post_action/max_greet_count`
110
+ - `human_behavior`:必须包含本次用户确认的 `restLevel`(例如 `{ "restLevel": "medium" }`)
111
+ - 不要传 `follow_up.chat`;该路径属于 legacy-only 行为
112
+ - 若返回 `ACCEPTED + run_id`:记录 `run_id` 并停止本轮,不自动轮询。
113
+ - 若返回 `NEED_INPUT` 或 `NEED_CONFIRMATION`:只追问 `pending_questions`。
103
114
  - 准备/门禁工具:`prepare_recommend_pipeline_run`
104
- - 用途:只校验参数是否完整,不启动筛选任务。
115
+ - 用途:只校验参数是否完整,不启动筛选任务;主要用于用户明确要求预检,或 cron/稍后/定时启动前校验。
105
116
  - 要求:若用户要“现在启动”,返回 `status=READY` 且 `cron_ready=true` 后,下一步必须调用 MCP 工具 `run_recommend` 或 `start_recommend_pipeline_run`,禁止改用 terminal/shell/run_command/CLI/manual JSON-RPC。只有用户要“稍后/cron/定时启动”时,才继续创建定时任务。
106
117
  - READY 响应会带 `prepared_only=true`、`run_started=false`、`recommended_next_tool=start_recommend_pipeline_run`、`alternate_next_tool=run_recommend`、`next_action.do_not_call_prepare_again=true`、`agent_guidance.native_mcp_required_after_prepare=true`;必须照这些字段继续,不得再次调用 prepare。
107
118
  - 若返回 `NEED_INPUT` / `NEED_CONFIRMATION` / `FAILED`:继续补齐 `pending_questions` 或修复登录/页面/config;不得先创建 cron。
@@ -112,13 +123,10 @@ description: "Use when users want Boss recommend-page filtering/screening via bo
112
123
  - 成功标准:必须返回 `status=SCHEDULED`、`schedule_created=true`、`schedule_id`、`run_at`。只有这个返回后,才可以告诉用户定时任务已创建。
113
124
  - Cron 查询工具:`get_recommend_scheduled_run`
114
125
  - 用途:用户问“任务是否启动/进度”时,先查 `schedule_id`。若到点后已启动,会返回内层 `run_id` 和 run 快照。
115
- - 主工具:`run_recommend` / `start_recommend_pipeline_run`
116
- - 必填:`instruction`
117
- - 关键输入:
118
- - `confirmation`:新流程只需要 `{ "final_confirmed": true }`;旧版 `page_confirmed/page_value/.../job_confirmed/job_value` 仍兼容但不要主动制造逐项确认。
119
- - `overrides`:`page_scope/school_tag/degree/gender/recent_not_view/criteria/job/target_count/post_action/max_greet_count`
120
- - `human_behavior`:必须包含本次用户确认的 `restLevel`(例如 `{ "restLevel": "medium" }`)
121
- - 不要传 `follow_up.chat`;该路径属于 legacy-only 行为
126
+ - 状态恢复工具:`list_recommend_pipeline_runs`
127
+ - 用途:忘记 `run_id` 或用户问“刚才那个任务怎么样”但上下文里没有 run_id 时调用;读取最近 run 摘要并返回 `latest_run`。
128
+ - 限制:只返回紧凑摘要,不包含大体积候选人 `results`;拿到 `latest_run.run_id` 后再用 `get_recommend_pipeline_run` / `cancel_recommend_pipeline_run`。
129
+ - Trae-CN 中禁止用 `run_command`、PowerShell、`Get-Content`、CLI 或直接读取 `~/.boss-recommend-mcp/runs/*.json` 来恢复 run_id 或状态。
122
130
 
123
131
  最小策略:
124
132
 
@@ -164,6 +172,7 @@ npx -y @reconcrap/boss-recommend-mcp@latest schedule-status --schedule-id <sched
164
172
 
165
173
  - 用户未明确要求“持续跟进”时,不自动 `sleep + get_recommend_pipeline_run`。
166
174
  - 用户要求查进度时,再用 `get_recommend_pipeline_run`。
175
+ - 如果当前对话已经丢失 `run_id`,先调用 `list_recommend_pipeline_runs`,再用 `latest_run.run_id` 调用 `get_recommend_pipeline_run`;不要改用终端读 JSON。
167
176
  - **长任务轮询节奏(强制)**:
168
177
  - 推荐任务可能运行数小时,禁止高频轮询。
169
178
  - 默认最小轮询间隔为 **30 分钟**(除非用户明确要求更频繁)。
package/src/cli.js CHANGED
@@ -24,8 +24,12 @@ import {
24
24
  resumeBossChatRunTool
25
25
  } from "./chat-mcp.js";
26
26
  import {
27
+ cancelRecommendPipelineRunTool,
28
+ getRecommendPipelineRunTool,
27
29
  listRecommendJobsTool,
30
+ pauseRecommendPipelineRunTool,
28
31
  prepareRecommendPipelineRunTool,
32
+ resumeRecommendPipelineRunTool,
29
33
  startRecommendPipelineRunTool
30
34
  } from "./recommend-mcp.js";
31
35
  import {
@@ -2971,6 +2975,33 @@ function scheduleStatusCli(options = {}) {
2971
2975
  }
2972
2976
  }
2973
2977
 
2978
+ function isTerminalRecommendCliState(state) {
2979
+ return ["completed", "failed", "canceled"].includes(String(state || "").trim().toLowerCase());
2980
+ }
2981
+
2982
+ async function monitorDetachedRecommendCliRunControls(runId) {
2983
+ const normalizedRunId = String(runId || "").trim();
2984
+ if (!normalizedRunId) return;
2985
+ while (true) {
2986
+ const payload = getRecommendPipelineRunTool({
2987
+ args: {
2988
+ run_id: normalizedRunId
2989
+ }
2990
+ });
2991
+ const state = String(payload?.run?.state || payload?.run?.status || "").trim().toLowerCase();
2992
+ if (isTerminalRecommendCliState(state)) return;
2993
+ const control = payload?.run?.control || {};
2994
+ if (control.cancel_requested === true) {
2995
+ cancelRecommendPipelineRunTool({ args: { run_id: normalizedRunId } });
2996
+ } else if (control.pause_requested === true && state === "running") {
2997
+ pauseRecommendPipelineRunTool({ args: { run_id: normalizedRunId } });
2998
+ } else if (control.pause_requested === false && state === "paused") {
2999
+ resumeRecommendPipelineRunTool({ args: { run_id: normalizedRunId } });
3000
+ }
3001
+ await sleepMs(1000);
3002
+ }
3003
+ }
3004
+
2974
3005
  async function runPipelineOnce(options = {}) {
2975
3006
  const workspaceRoot = getWorkspaceRoot(options);
2976
3007
  const { args, port } = buildRecommendRunCliInput(options);
@@ -2987,11 +3018,13 @@ async function runPipelineOnce(options = {}) {
2987
3018
  workspace_root: workspaceRoot,
2988
3019
  port
2989
3020
  }
2990
- });
2991
- if (result.status !== "ACCEPTED") {
2992
- process.exitCode = 1;
2993
- }
2994
- }
3021
+ });
3022
+ if (result.status !== "ACCEPTED") {
3023
+ process.exitCode = 1;
3024
+ } else if (process.env[detachedRecommendRunChildEnv] === "1") {
3025
+ await monitorDetachedRecommendCliRunControls(result.run_id);
3026
+ }
3027
+ }
2995
3028
 
2996
3029
  function buildRecommendJobListCliInput(options = {}) {
2997
3030
  const targetUrlIncludes = String(options["target-url-includes"] || options.target_url_includes || "").trim();
@@ -28,14 +28,24 @@ function createRunId(prefix = "run") {
28
28
  return `${prefix}_${Date.now().toString(36)}_${random}`;
29
29
  }
30
30
 
31
- function clone(value) {
32
- return JSON.parse(JSON.stringify(value));
33
- }
34
-
35
- function createDeferred() {
36
- let resolve;
37
- let reject;
38
- const promise = new Promise((promiseResolve, promiseReject) => {
31
+ function clone(value) {
32
+ return JSON.parse(JSON.stringify(value));
33
+ }
34
+
35
+ function errorDiagnostic(error) {
36
+ if (!error) return null;
37
+ const diagnostic = {
38
+ name: error?.name || "Error",
39
+ message: error?.message || String(error)
40
+ };
41
+ if (error?.code) diagnostic.code = error.code;
42
+ return diagnostic;
43
+ }
44
+
45
+ function createDeferred() {
46
+ let resolve;
47
+ let reject;
48
+ const promise = new Promise((promiseResolve, promiseReject) => {
39
49
  resolve = promiseResolve;
40
50
  reject = promiseReject;
41
51
  });
@@ -188,23 +198,20 @@ export function createRunLifecycleManager({
188
198
  summary: summary || entry.run.summary
189
199
  });
190
200
  }
191
- } catch (error) {
192
- if (error instanceof RunCanceledError || entry.controller.signal.aborted || entry.cancelRequested) {
193
- setStatus(entry, RUN_STATUS_CANCELED, {
194
- completedAt: now(),
195
- error: null
196
- });
197
- return;
198
- }
199
- setStatus(entry, RUN_STATUS_FAILED, {
200
- completedAt: now(),
201
- error: {
202
- name: error?.name || "Error",
203
- message: error?.message || String(error)
204
- }
205
- });
206
- }
207
- }
201
+ } catch (error) {
202
+ if (error instanceof RunCanceledError || entry.controller.signal.aborted || entry.cancelRequested) {
203
+ setStatus(entry, RUN_STATUS_CANCELED, {
204
+ completedAt: now(),
205
+ error: error instanceof RunCanceledError ? null : errorDiagnostic(error)
206
+ });
207
+ return;
208
+ }
209
+ setStatus(entry, RUN_STATUS_FAILED, {
210
+ completedAt: now(),
211
+ error: errorDiagnostic(error)
212
+ });
213
+ }
214
+ }
208
215
 
209
216
  function startRun({ runId: requestedRunId = "", name, pid = process.pid, context = {}, progress = {}, checkpoint = {}, task }) {
210
217
  if (typeof task !== "function") {