@reconcrap/boss-recommend-mcp 1.2.10 → 1.3.0

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.
Files changed (34) hide show
  1. package/README.md +82 -1
  2. package/package.json +2 -1
  3. package/skills/boss-chat/README.md +5 -0
  4. package/skills/boss-chat/SKILL.md +69 -0
  5. package/skills/boss-recommend-pipeline/SKILL.md +40 -4
  6. package/src/adapters.js +19 -5
  7. package/src/boss-chat.js +436 -0
  8. package/src/cli.js +294 -129
  9. package/src/index.js +459 -108
  10. package/src/pipeline.js +605 -8
  11. package/src/run-state.js +5 -0
  12. package/src/test-adapters-runtime.js +69 -0
  13. package/src/test-boss-chat.js +399 -0
  14. package/src/test-index-async.js +238 -4
  15. package/src/test-pipeline.js +408 -1
  16. package/vendor/boss-chat-cli/README.md +134 -0
  17. package/vendor/boss-chat-cli/package.json +53 -0
  18. package/vendor/boss-chat-cli/src/app.js +769 -0
  19. package/vendor/boss-chat-cli/src/browser/chat-page.js +2681 -0
  20. package/vendor/boss-chat-cli/src/cli.js +1350 -0
  21. package/vendor/boss-chat-cli/src/mcp/server.js +149 -0
  22. package/vendor/boss-chat-cli/src/mcp/tool-runtime.js +193 -0
  23. package/vendor/boss-chat-cli/src/runtime/async-run-state.js +260 -0
  24. package/vendor/boss-chat-cli/src/runtime/interaction.js +102 -0
  25. package/vendor/boss-chat-cli/src/runtime/run-control.js +102 -0
  26. package/vendor/boss-chat-cli/src/services/chrome-client.js +97 -0
  27. package/vendor/boss-chat-cli/src/services/llm.js +352 -0
  28. package/vendor/boss-chat-cli/src/services/profile-store.js +157 -0
  29. package/vendor/boss-chat-cli/src/services/report-store.js +19 -0
  30. package/vendor/boss-chat-cli/src/services/resume-capture.js +554 -0
  31. package/vendor/boss-chat-cli/src/services/state-store.js +217 -0
  32. package/vendor/boss-chat-cli/src/utils/customer-key.js +82 -0
  33. package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +902 -56
  34. package/vendor/boss-recommend-screen-cli/test-recoverable-resume-failures.cjs +387 -1
package/src/index.js CHANGED
@@ -8,11 +8,20 @@ import {
8
8
  getFeaturedCalibrationResolution,
9
9
  runRecommendCalibration
10
10
  } from "./adapters.js";
11
+ import {
12
+ cancelBossChatRun,
13
+ getBossChatHealthCheck,
14
+ getBossChatRun,
15
+ pauseBossChatRun,
16
+ resumeBossChatRun,
17
+ startBossChatRun
18
+ } from "./boss-chat.js";
11
19
  import { runRecommendPipeline } from "./pipeline.js";
12
20
  import { runRecommendSelfHeal } from "./self-heal.js";
13
- import {
14
- RUN_MODE_ASYNC,
15
- RUN_STAGE_PREFLIGHT,
21
+ import {
22
+ RUN_MODE_ASYNC,
23
+ RUN_STAGE_CHAT_FOLLOWUP,
24
+ RUN_STAGE_PREFLIGHT,
16
25
  RUN_STATE_CANCELED,
17
26
  RUN_STATE_COMPLETED,
18
27
  RUN_STATE_FAILED,
@@ -41,6 +50,12 @@ const TOOL_RESUME_RUN = "resume_recommend_pipeline_run";
41
50
  const TOOL_RUN_FEATURED_CALIBRATION = "run_featured_calibration";
42
51
  const TOOL_GET_FEATURED_CALIBRATION_STATUS = "get_featured_calibration_status";
43
52
  const TOOL_RUN_RECOMMEND_SELF_HEAL = "run_recommend_self_heal";
53
+ const TOOL_BOSS_CHAT_HEALTH_CHECK = "boss_chat_health_check";
54
+ const TOOL_BOSS_CHAT_START_RUN = "start_boss_chat_run";
55
+ const TOOL_BOSS_CHAT_GET_RUN = "get_boss_chat_run";
56
+ const TOOL_BOSS_CHAT_PAUSE_RUN = "pause_boss_chat_run";
57
+ const TOOL_BOSS_CHAT_RESUME_RUN = "resume_boss_chat_run";
58
+ const TOOL_BOSS_CHAT_CANCEL_RUN = "cancel_boss_chat_run";
44
59
 
45
60
  const SERVER_NAME = "boss-recommend-mcp";
46
61
  const FRAMING_UNKNOWN = "unknown";
@@ -64,10 +79,41 @@ function parsePositiveInteger(raw, fallback) {
64
79
  return Number.isFinite(value) && value > 0 ? value : fallback;
65
80
  }
66
81
 
67
- function getDefaultPollAfterSec() {
68
- const fromEnv = parsePositiveInteger(process.env.BOSS_RECOMMEND_POLL_AFTER_SEC, 10);
69
- return Math.max(5, Math.min(15, fromEnv));
70
- }
82
+ function getDefaultPollAfterSec() {
83
+ const fromEnv = parsePositiveInteger(process.env.BOSS_RECOMMEND_POLL_AFTER_SEC, 1800);
84
+ return Math.max(60, fromEnv);
85
+ }
86
+
87
+ function getLongRunPollAfterSec() {
88
+ const fromEnv = parsePositiveInteger(process.env.BOSS_RECOMMEND_LONG_POLL_AFTER_SEC, 1800);
89
+ return Math.max(60, fromEnv);
90
+ }
91
+
92
+ function getRecommendedPollAfterSec(args = {}) {
93
+ return hasFollowUpChatRequest(args)
94
+ ? getLongRunPollAfterSec()
95
+ : getDefaultPollAfterSec();
96
+ }
97
+
98
+ function hasFollowUpChatRequest(args = {}) {
99
+ const directChat = args?.follow_up?.chat && typeof args.follow_up.chat === "object"
100
+ ? args.follow_up.chat
101
+ : null;
102
+ const overrideChat = args?.overrides?.follow_up?.chat && typeof args.overrides.follow_up.chat === "object"
103
+ ? args.overrides.follow_up.chat
104
+ : null;
105
+ return Boolean(directChat || overrideChat);
106
+ }
107
+
108
+ function getDefaultAcceptedMessage(args = {}) {
109
+ if (hasFollowUpChatRequest(args)) {
110
+ return "异步流水线已启动(detached)。recommend+chat 联动任务可能耗时较长,默认建议至少每 30 分钟查询一次 get_recommend_pipeline_run;若手动查询时已完成,将立即进入聊天衔接。";
111
+ }
112
+ const fromEnv = parsePositiveInteger(process.env.BOSS_RECOMMEND_POLL_AFTER_SEC, 1800);
113
+ const recommendedSeconds = Math.max(60, fromEnv);
114
+ const recommendedMinutes = Math.max(1, Math.round(recommendedSeconds / 60));
115
+ return `异步流水线已启动(detached)。默认不自动轮询;如需进度请按需调用 get_recommend_pipeline_run(建议至少每 ${recommendedMinutes} 分钟查询一次)。`;
116
+ }
71
117
 
72
118
  function getRunArtifacts(runId) {
73
119
  const normalizedRunId = normalizeText(runId);
@@ -77,14 +123,15 @@ function getRunArtifacts(runId) {
77
123
  };
78
124
  }
79
125
 
80
- function buildRunContext(workspaceRoot, args = {}) {
81
- return {
82
- workspace_root: path.resolve(workspaceRoot),
83
- instruction: String(args?.instruction || ""),
84
- confirmation: args?.confirmation && typeof args.confirmation === "object" ? args.confirmation : {},
85
- overrides: args?.overrides && typeof args.overrides === "object" ? args.overrides : {}
86
- };
87
- }
126
+ function buildRunContext(workspaceRoot, args = {}) {
127
+ return {
128
+ workspace_root: path.resolve(workspaceRoot),
129
+ instruction: String(args?.instruction || ""),
130
+ confirmation: args?.confirmation && typeof args.confirmation === "object" ? args.confirmation : {},
131
+ overrides: args?.overrides && typeof args.overrides === "object" ? args.overrides : {},
132
+ follow_up: args?.follow_up && typeof args.follow_up === "object" ? args.follow_up : null
133
+ };
134
+ }
88
135
 
89
136
  function resolveRunContext(snapshot) {
90
137
  const workspaceRoot = normalizeText(snapshot?.context?.workspace_root || "");
@@ -95,16 +142,19 @@ function resolveRunContext(snapshot) {
95
142
  return {
96
143
  workspaceRoot,
97
144
  args: {
98
- instruction,
99
- confirmation: snapshot?.context?.confirmation && typeof snapshot.context.confirmation === "object"
100
- ? snapshot.context.confirmation
101
- : {},
102
- overrides: snapshot?.context?.overrides && typeof snapshot.context.overrides === "object"
103
- ? snapshot.context.overrides
104
- : {}
105
- }
106
- };
107
- }
145
+ instruction,
146
+ confirmation: snapshot?.context?.confirmation && typeof snapshot.context.confirmation === "object"
147
+ ? snapshot.context.confirmation
148
+ : {},
149
+ overrides: snapshot?.context?.overrides && typeof snapshot.context.overrides === "object"
150
+ ? snapshot.context.overrides
151
+ : {},
152
+ follow_up: snapshot?.context?.follow_up && typeof snapshot.context.follow_up === "object"
153
+ ? snapshot.context.follow_up
154
+ : null
155
+ }
156
+ };
157
+ }
108
158
 
109
159
  function isRunPauseRequested(runId) {
110
160
  const snapshot = readRunState(runId);
@@ -292,13 +342,79 @@ function createRunInputSchema() {
292
342
  type: "string",
293
343
  enum: ["favorite", "greet", "none"]
294
344
  }
295
- },
296
- additionalProperties: false
297
- }
298
- },
299
- required: ["instruction"],
300
- additionalProperties: false
301
- };
345
+ },
346
+ additionalProperties: false
347
+ },
348
+ follow_up: {
349
+ type: "object",
350
+ properties: {
351
+ chat: {
352
+ type: "object",
353
+ properties: {
354
+ profile: { type: "string" },
355
+ criteria: { type: "string" },
356
+ start_from: {
357
+ type: "string",
358
+ enum: ["unread", "all"]
359
+ },
360
+ target_count: {
361
+ type: "integer",
362
+ minimum: 1
363
+ },
364
+ dry_run: { type: "boolean" },
365
+ no_state: { type: "boolean" },
366
+ safe_pacing: { type: "boolean" },
367
+ batch_rest_enabled: { type: "boolean" }
368
+ },
369
+ additionalProperties: false
370
+ }
371
+ },
372
+ additionalProperties: false
373
+ }
374
+ },
375
+ required: ["instruction"],
376
+ additionalProperties: false
377
+ };
378
+ }
379
+
380
+ function createBossChatStartInputSchema() {
381
+ return {
382
+ type: "object",
383
+ properties: {
384
+ profile: {
385
+ type: "string",
386
+ description: "可选,boss-chat profile 名称,默认 default"
387
+ },
388
+ job: {
389
+ type: "string",
390
+ description: "岗位,支持岗位名/编号/value"
391
+ },
392
+ start_from: {
393
+ type: "string",
394
+ enum: ["unread", "all"],
395
+ description: "从未读或全部聊天列表开始"
396
+ },
397
+ criteria: {
398
+ type: "string",
399
+ description: "boss-chat 的筛选 criteria"
400
+ },
401
+ target_count: {
402
+ type: "integer",
403
+ minimum: 1,
404
+ description: "本次处理人数上限;chat-only 模式必填(可先不传,服务会返回 NEED_INPUT 引导补齐)"
405
+ },
406
+ port: {
407
+ type: "integer",
408
+ minimum: 1,
409
+ description: "可选,覆盖 Chrome 调试端口;未传时读取 screening-config.json.debugPort"
410
+ },
411
+ dry_run: { type: "boolean" },
412
+ no_state: { type: "boolean" },
413
+ safe_pacing: { type: "boolean" },
414
+ batch_rest_enabled: { type: "boolean" }
415
+ },
416
+ additionalProperties: false
417
+ };
302
418
  }
303
419
 
304
420
  function createRunFeaturedCalibrationInputSchema() {
@@ -434,6 +550,77 @@ function createToolsSchema() {
434
550
  name: TOOL_RUN_RECOMMEND_SELF_HEAL,
435
551
  description: "手动运维自愈工具:扫描 Boss recommend 相关 selector / network 规则漂移,并在确认后应用高置信度修复。",
436
552
  inputSchema: createRunRecommendSelfHealInputSchema()
553
+ },
554
+ {
555
+ name: TOOL_BOSS_CHAT_HEALTH_CHECK,
556
+ description: "检查内置 boss-chat 运行时与共享 screening-config.json 是否可用。",
557
+ inputSchema: {
558
+ type: "object",
559
+ properties: {
560
+ port: {
561
+ type: "integer",
562
+ minimum: 1
563
+ }
564
+ },
565
+ additionalProperties: false
566
+ }
567
+ },
568
+ {
569
+ name: TOOL_BOSS_CHAT_START_RUN,
570
+ description: "异步启动一次 boss-chat 任务。若缺少必填参数会先返回 NEED_INPUT(含岗位列表与待确认字段)。",
571
+ inputSchema: createBossChatStartInputSchema()
572
+ },
573
+ {
574
+ name: TOOL_BOSS_CHAT_GET_RUN,
575
+ description: "查询 boss-chat run_id 的当前状态。",
576
+ inputSchema: {
577
+ type: "object",
578
+ properties: {
579
+ run_id: { type: "string" },
580
+ profile: { type: "string" }
581
+ },
582
+ required: ["run_id"],
583
+ additionalProperties: false
584
+ }
585
+ },
586
+ {
587
+ name: TOOL_BOSS_CHAT_PAUSE_RUN,
588
+ description: "暂停运行中的 boss-chat 任务。",
589
+ inputSchema: {
590
+ type: "object",
591
+ properties: {
592
+ run_id: { type: "string" },
593
+ profile: { type: "string" }
594
+ },
595
+ required: ["run_id"],
596
+ additionalProperties: false
597
+ }
598
+ },
599
+ {
600
+ name: TOOL_BOSS_CHAT_RESUME_RUN,
601
+ description: "继续已暂停的 boss-chat 任务。",
602
+ inputSchema: {
603
+ type: "object",
604
+ properties: {
605
+ run_id: { type: "string" },
606
+ profile: { type: "string" }
607
+ },
608
+ required: ["run_id"],
609
+ additionalProperties: false
610
+ }
611
+ },
612
+ {
613
+ name: TOOL_BOSS_CHAT_CANCEL_RUN,
614
+ description: "取消运行中的 boss-chat 任务。",
615
+ inputSchema: {
616
+ type: "object",
617
+ properties: {
618
+ run_id: { type: "string" },
619
+ profile: { type: "string" }
620
+ },
621
+ required: ["run_id"],
622
+ additionalProperties: false
623
+ }
437
624
  }
438
625
  ];
439
626
  }
@@ -456,13 +643,48 @@ function createToolResultResponse(id, payload, isError = false) {
456
643
  }
457
644
 
458
645
  function validateRunArgs(args) {
459
- if (!args || typeof args !== "object") {
460
- return "arguments must be an object";
461
- }
462
- if (!args.instruction || typeof args.instruction !== "string") {
646
+ if (!args || typeof args !== "object") {
647
+ return "arguments must be an object";
648
+ }
649
+ if (!args.instruction || typeof args.instruction !== "string") {
463
650
  return "instruction is required and must be a string";
464
- }
465
- return null;
651
+ }
652
+ return null;
653
+ }
654
+
655
+ function validateBossChatStartArgs(args) {
656
+ if (!args || typeof args !== "object" || Array.isArray(args)) {
657
+ return "arguments must be an object";
658
+ }
659
+ if (Object.prototype.hasOwnProperty.call(args, "job")) {
660
+ if (typeof args.job !== "string" || !normalizeText(args.job)) {
661
+ return "job must be a non-empty string when provided";
662
+ }
663
+ }
664
+ if (Object.prototype.hasOwnProperty.call(args, "start_from")) {
665
+ const startFrom = normalizeText(args.start_from).toLowerCase();
666
+ if (!["unread", "all"].includes(startFrom)) {
667
+ return "start_from must be one of: unread, all";
668
+ }
669
+ }
670
+ if (Object.prototype.hasOwnProperty.call(args, "criteria")) {
671
+ if (typeof args.criteria !== "string" || !normalizeText(args.criteria)) {
672
+ return "criteria must be a non-empty string when provided";
673
+ }
674
+ }
675
+ if (Object.prototype.hasOwnProperty.call(args, "target_count")) {
676
+ const targetCount = Number.parseInt(String(args.target_count), 10);
677
+ if (!Number.isFinite(targetCount) || targetCount <= 0) {
678
+ return "target_count must be a positive integer";
679
+ }
680
+ }
681
+ if (Object.prototype.hasOwnProperty.call(args, "port")) {
682
+ const port = Number.parseInt(String(args.port), 10);
683
+ if (!Number.isFinite(port) || port <= 0) {
684
+ return "port must be a positive integer";
685
+ }
686
+ }
687
+ return null;
466
688
  }
467
689
 
468
690
  function validateRunFeaturedCalibrationArgs(args) {
@@ -560,13 +782,14 @@ function buildAsyncPrecheckConfirmation(confirmation) {
560
782
  };
561
783
  }
562
784
 
563
- function buildAsyncPrecheckArgs(args) {
564
- return {
565
- instruction: args.instruction,
566
- confirmation: buildAsyncPrecheckConfirmation(args.confirmation),
567
- overrides: args.overrides
568
- };
569
- }
785
+ function buildAsyncPrecheckArgs(args) {
786
+ return {
787
+ instruction: args.instruction,
788
+ confirmation: buildAsyncPrecheckConfirmation(args.confirmation),
789
+ overrides: args.overrides,
790
+ follow_up: args.follow_up
791
+ };
792
+ }
570
793
 
571
794
  function isFinalReviewOnlyConfirmation(result) {
572
795
  if (result?.status !== "NEED_CONFIRMATION") return false;
@@ -619,6 +842,46 @@ function readCheckpointProgress(checkpointPath) {
619
842
  }
620
843
  }
621
844
 
845
+ function getBossChatRunIdFromSnapshot(snapshot) {
846
+ return normalizeText(
847
+ snapshot?.resume?.chat_run_id
848
+ || snapshot?.result?.follow_up?.chat?.run_id
849
+ || ""
850
+ );
851
+ }
852
+
853
+ function mergeFollowUpResult(currentResult, event = {}) {
854
+ const currentBase = currentResult && typeof currentResult === "object" ? currentResult : {};
855
+ const recommendPayload = event?.recommend_payload && typeof event.recommend_payload === "object"
856
+ ? event.recommend_payload
857
+ : null;
858
+ const baseResult = recommendPayload
859
+ ? {
860
+ ...currentBase,
861
+ ...recommendPayload
862
+ }
863
+ : { ...currentBase };
864
+ const followUp = event?.follow_up && typeof event.follow_up === "object" ? event.follow_up : {};
865
+ const currentFollowUp = baseResult.follow_up && typeof baseResult.follow_up === "object"
866
+ ? baseResult.follow_up
867
+ : {};
868
+ const nextChat = followUp.chat && typeof followUp.chat === "object"
869
+ ? {
870
+ ...(currentFollowUp.chat && typeof currentFollowUp.chat === "object" ? currentFollowUp.chat : {}),
871
+ ...followUp.chat
872
+ }
873
+ : currentFollowUp.chat;
874
+ return {
875
+ ...baseResult,
876
+ ...(event?.recommend_result ? { result: event.recommend_result } : {}),
877
+ follow_up: {
878
+ ...currentFollowUp,
879
+ ...followUp,
880
+ ...(nextChat ? { chat: nextChat } : {})
881
+ }
882
+ };
883
+ }
884
+
622
885
  function reconcileOrphanRunIfNeeded(runId, snapshot) {
623
886
  if (!snapshot || TERMINAL_RUN_STATES.has(snapshot.state)) {
624
887
  return snapshot;
@@ -730,10 +993,10 @@ function finalizeCanceledRun(runId, snapshot) {
730
993
  }) || readRunState(runId) || snapshot;
731
994
  }
732
995
 
733
- function createRuntimeCallbacks(runId, heartbeatIntervalMs) {
734
- let lastStage = RUN_STAGE_PREFLIGHT;
735
- let lastOutputPersistAt = 0;
736
- return {
996
+ function createRuntimeCallbacks(runId, heartbeatIntervalMs) {
997
+ let lastStage = RUN_STAGE_PREFLIGHT;
998
+ let lastOutputPersistAt = 0;
999
+ return {
737
1000
  heartbeatIntervalMs,
738
1001
  onStage(event) {
739
1002
  const stage = normalizeText(event?.stage) || RUN_STAGE_PREFLIGHT;
@@ -772,11 +1035,11 @@ function createRuntimeCallbacks(runId, heartbeatIntervalMs) {
772
1035
  last_message: message
773
1036
  });
774
1037
  },
775
- onProgress(event) {
776
- const stage = normalizeText(event?.stage) || lastStage;
777
- lastStage = stage || lastStage;
778
- safeUpdateRunState(runId, { stage: lastStage });
779
- safeUpdateRunProgress(
1038
+ onProgress(event) {
1039
+ const stage = normalizeText(event?.stage) || lastStage;
1040
+ lastStage = stage || lastStage;
1041
+ safeUpdateRunState(runId, { stage: lastStage });
1042
+ safeUpdateRunProgress(
780
1043
  runId,
781
1044
  {
782
1045
  processed: Number.isInteger(event?.processed) ? event.processed : undefined,
@@ -784,14 +1047,34 @@ function createRuntimeCallbacks(runId, heartbeatIntervalMs) {
784
1047
  skipped: Number.isInteger(event?.skipped) ? event.skipped : undefined,
785
1048
  greet_count: Number.isInteger(event?.greet_count) ? event.greet_count : undefined
786
1049
  },
787
- normalizeText(event?.line || "")
788
- );
789
- },
790
- getLastStage() {
791
- return lastStage;
792
- }
793
- };
794
- }
1050
+ normalizeText(event?.line || "")
1051
+ );
1052
+ },
1053
+ onFollowUp(event) {
1054
+ const stage = normalizeText(event?.stage) || RUN_STAGE_CHAT_FOLLOWUP;
1055
+ lastStage = stage || lastStage;
1056
+ safeUpdateRunState(runId, (current) => ({
1057
+ state: RUN_STATE_RUNNING,
1058
+ stage: lastStage,
1059
+ last_message: normalizeText(event?.last_message || current?.last_message || ""),
1060
+ resume: {
1061
+ ...current?.resume,
1062
+ follow_up_phase: RUN_STAGE_CHAT_FOLLOWUP,
1063
+ chat_run_id: normalizeText(
1064
+ event?.follow_up?.chat?.run_id
1065
+ || getBossChatRunIdFromSnapshot(current)
1066
+ || ""
1067
+ ) || null,
1068
+ chat_state: normalizeText(event?.follow_up?.chat?.state || current?.resume?.chat_state || "") || null
1069
+ },
1070
+ result: mergeFollowUpResult(current?.result, event)
1071
+ }));
1072
+ },
1073
+ getLastStage() {
1074
+ return lastStage;
1075
+ }
1076
+ };
1077
+ }
795
1078
 
796
1079
 
797
1080
  async function executeTrackedPipeline({
@@ -806,43 +1089,56 @@ async function executeTrackedPipeline({
806
1089
  const runtimeCallbacks = createRuntimeCallbacks(runId, heartbeatIntervalMs);
807
1090
  const artifacts = getRunArtifacts(runId);
808
1091
  const existingSnapshot = readRunState(runId);
809
- const resumeConfig = {
810
- resume: resumeRun === true,
811
- checkpoint_path: normalizeText(existingSnapshot?.resume?.checkpoint_path || artifacts.checkpoint_path),
812
- pause_control_path: normalizeText(existingSnapshot?.resume?.pause_control_path || artifacts.run_state_path),
813
- output_csv: normalizeText(existingSnapshot?.resume?.output_csv || "") || null,
814
- previous_completion_reason: getCompletionReasonFromResult(existingSnapshot?.result || null)
815
- };
816
- safeUpdateRunState(runId, {
817
- state: RUN_STATE_RUNNING,
818
- stage: RUN_STAGE_PREFLIGHT,
819
- last_message: resumeRun
820
- ? "流水线继续执行中,等待 preflight。"
821
- : "流水线已启动,等待 preflight。",
822
- resume: resumeConfig
823
- });
1092
+ const resumeConfig = {
1093
+ resume: resumeRun === true,
1094
+ checkpoint_path: normalizeText(existingSnapshot?.resume?.checkpoint_path || artifacts.checkpoint_path),
1095
+ pause_control_path: normalizeText(existingSnapshot?.resume?.pause_control_path || artifacts.run_state_path),
1096
+ output_csv: normalizeText(existingSnapshot?.resume?.output_csv || "") || null,
1097
+ follow_up_phase: normalizeText(existingSnapshot?.resume?.follow_up_phase || "") || null,
1098
+ chat_run_id: normalizeText(existingSnapshot?.resume?.chat_run_id || "") || null,
1099
+ chat_state: normalizeText(existingSnapshot?.resume?.chat_state || "") || null,
1100
+ recommend_result: existingSnapshot?.result?.result || null,
1101
+ recommend_search_params: existingSnapshot?.result?.search_params || null,
1102
+ recommend_screen_params: existingSnapshot?.result?.screen_params || null,
1103
+ previous_completion_reason: getCompletionReasonFromResult(existingSnapshot?.result || null)
1104
+ };
1105
+ safeUpdateRunState(runId, {
1106
+ state: RUN_STATE_RUNNING,
1107
+ stage: resumeConfig.follow_up_phase === RUN_STAGE_CHAT_FOLLOWUP ? RUN_STAGE_CHAT_FOLLOWUP : RUN_STAGE_PREFLIGHT,
1108
+ last_message: resumeRun
1109
+ ? (
1110
+ resumeConfig.follow_up_phase === RUN_STAGE_CHAT_FOLLOWUP
1111
+ ? "流水线继续执行中,准备恢复 boss-chat follow-up。"
1112
+ : "流水线继续执行中,等待 preflight。"
1113
+ )
1114
+ : "流水线已启动,等待 preflight。",
1115
+ resume: resumeConfig
1116
+ });
824
1117
 
825
1118
  let result;
826
1119
  try {
827
1120
  result = await runPipelineImpl(
828
1121
  {
829
1122
  workspaceRoot,
830
- instruction: args.instruction,
831
- confirmation: args.confirmation,
832
- overrides: args.overrides,
833
- resume: resumeConfig
834
- },
835
- undefined,
836
- {
837
- signal,
838
- heartbeatIntervalMs,
839
- isPauseRequested: () => isRunPauseRequested(runId),
840
- onStage: runtimeCallbacks.onStage,
841
- onHeartbeat: runtimeCallbacks.onHeartbeat,
842
- onOutput: runtimeCallbacks.onOutput,
843
- onProgress: runtimeCallbacks.onProgress
844
- }
845
- );
1123
+ instruction: args.instruction,
1124
+ confirmation: args.confirmation,
1125
+ overrides: args.overrides,
1126
+ followUp: args.follow_up,
1127
+ resume: resumeConfig
1128
+ },
1129
+ undefined,
1130
+ {
1131
+ signal,
1132
+ heartbeatIntervalMs,
1133
+ isPauseRequested: () => isRunPauseRequested(runId),
1134
+ isCancelRequested: () => isRunCancelRequested(runId),
1135
+ onStage: runtimeCallbacks.onStage,
1136
+ onHeartbeat: runtimeCallbacks.onHeartbeat,
1137
+ onOutput: runtimeCallbacks.onOutput,
1138
+ onProgress: runtimeCallbacks.onProgress,
1139
+ onFollowUp: runtimeCallbacks.onFollowUp
1140
+ }
1141
+ );
846
1142
  } catch (error) {
847
1143
  const canceled = Boolean(signal?.aborted) || error?.code === "PIPELINE_ABORTED";
848
1144
  if (canceled) {
@@ -1036,15 +1332,16 @@ async function handleStartRunTool({ workspaceRoot, args }) {
1036
1332
  let precheckResult;
1037
1333
  try {
1038
1334
  precheckResult = await runPipelineImpl(
1039
- {
1040
- workspaceRoot,
1041
- instruction: precheckArgs.instruction,
1042
- confirmation: precheckArgs.confirmation,
1043
- overrides: precheckArgs.overrides
1044
- },
1045
- undefined,
1046
- null
1047
- );
1335
+ {
1336
+ workspaceRoot,
1337
+ instruction: precheckArgs.instruction,
1338
+ confirmation: precheckArgs.confirmation,
1339
+ overrides: precheckArgs.overrides,
1340
+ followUp: precheckArgs.follow_up
1341
+ },
1342
+ undefined,
1343
+ null
1344
+ );
1048
1345
  } catch (error) {
1049
1346
  precheckResult = {
1050
1347
  status: "FAILED",
@@ -1110,8 +1407,8 @@ async function handleStartRunTool({ workspaceRoot, args }) {
1110
1407
  status: "ACCEPTED",
1111
1408
  run_id: runId,
1112
1409
  state: "queued",
1113
- poll_after_sec: getDefaultPollAfterSec(),
1114
- message: "异步流水线已启动(detached)。默认不自动轮询;如需进度请按需调用 get_recommend_pipeline_run。"
1410
+ poll_after_sec: getRecommendedPollAfterSec(args),
1411
+ message: getDefaultAcceptedMessage(args)
1115
1412
  };
1116
1413
  }
1117
1414
 
@@ -1367,8 +1664,10 @@ function handleResumeRunTool(args) {
1367
1664
  return {
1368
1665
  status: "RESUME_REQUESTED",
1369
1666
  run: started,
1370
- poll_after_sec: getDefaultPollAfterSec(),
1371
- message: "已恢复 Recommend 流水线(detached)。默认不自动轮询;如需进度请按需调用 get_recommend_pipeline_run。"
1667
+ poll_after_sec: getRecommendedPollAfterSec(executionContext?.args || {}),
1668
+ message: hasFollowUpChatRequest(executionContext?.args || {})
1669
+ ? "已恢复 Recommend 流水线(detached)。recommend+chat 联动任务建议按 30 分钟间隔查询状态;手动查询到完成时会立即衔接聊天流程。"
1670
+ : "已恢复 Recommend 流水线(detached)。默认不自动轮询;如需进度请按需调用 get_recommend_pipeline_run。"
1372
1671
  };
1373
1672
  }
1374
1673
 
@@ -1425,6 +1724,30 @@ async function handleRunRecommendSelfHealTool({ workspaceRoot, args }) {
1425
1724
  return runSelfHealImpl({ workspaceRoot, args });
1426
1725
  }
1427
1726
 
1727
+ function handleBossChatHealthCheckTool(workspaceRoot, args) {
1728
+ return getBossChatHealthCheck(workspaceRoot, args);
1729
+ }
1730
+
1731
+ async function handleBossChatStartRunTool({ workspaceRoot, args }) {
1732
+ return startBossChatRun({ workspaceRoot, input: args });
1733
+ }
1734
+
1735
+ async function handleBossChatGetRunTool({ workspaceRoot, args }) {
1736
+ return getBossChatRun({ workspaceRoot, input: args });
1737
+ }
1738
+
1739
+ async function handleBossChatPauseRunTool({ workspaceRoot, args }) {
1740
+ return pauseBossChatRun({ workspaceRoot, input: args });
1741
+ }
1742
+
1743
+ async function handleBossChatResumeRunTool({ workspaceRoot, args }) {
1744
+ return resumeBossChatRun({ workspaceRoot, input: args });
1745
+ }
1746
+
1747
+ async function handleBossChatCancelRunTool({ workspaceRoot, args }) {
1748
+ return cancelBossChatRun({ workspaceRoot, input: args });
1749
+ }
1750
+
1428
1751
  async function handleRequest(message, workspaceRoot) {
1429
1752
  if (!message || message.jsonrpc !== "2.0") {
1430
1753
  return createJsonRpcError(null, -32600, "Invalid JSON-RPC request");
@@ -1488,11 +1811,27 @@ async function handleRequest(message, workspaceRoot) {
1488
1811
  }
1489
1812
  }
1490
1813
 
1491
- if ([TOOL_GET_RUN, TOOL_CANCEL_RUN, TOOL_PAUSE_RUN, TOOL_RESUME_RUN].includes(toolName)) {
1814
+ if (toolName === TOOL_BOSS_CHAT_START_RUN) {
1815
+ const inputError = validateBossChatStartArgs(args);
1816
+ if (inputError) {
1817
+ return createJsonRpcError(id, -32602, inputError);
1818
+ }
1819
+ }
1820
+
1821
+ if ([
1822
+ TOOL_GET_RUN,
1823
+ TOOL_CANCEL_RUN,
1824
+ TOOL_PAUSE_RUN,
1825
+ TOOL_RESUME_RUN,
1826
+ TOOL_BOSS_CHAT_GET_RUN,
1827
+ TOOL_BOSS_CHAT_CANCEL_RUN,
1828
+ TOOL_BOSS_CHAT_PAUSE_RUN,
1829
+ TOOL_BOSS_CHAT_RESUME_RUN
1830
+ ].includes(toolName)) {
1492
1831
  if (!args || typeof args.run_id !== "string" || !normalizeText(args.run_id)) {
1493
1832
  return createJsonRpcError(id, -32602, "run_id is required and must be a string");
1494
1833
  }
1495
- }
1834
+ }
1496
1835
 
1497
1836
  try {
1498
1837
  let payload;
@@ -1512,6 +1851,18 @@ async function handleRequest(message, workspaceRoot) {
1512
1851
  payload = await handleRunFeaturedCalibrationTool({ workspaceRoot, args });
1513
1852
  } else if (toolName === TOOL_RUN_RECOMMEND_SELF_HEAL) {
1514
1853
  payload = await handleRunRecommendSelfHealTool({ workspaceRoot, args });
1854
+ } else if (toolName === TOOL_BOSS_CHAT_HEALTH_CHECK) {
1855
+ payload = handleBossChatHealthCheckTool(workspaceRoot, args);
1856
+ } else if (toolName === TOOL_BOSS_CHAT_START_RUN) {
1857
+ payload = await handleBossChatStartRunTool({ workspaceRoot, args });
1858
+ } else if (toolName === TOOL_BOSS_CHAT_GET_RUN) {
1859
+ payload = await handleBossChatGetRunTool({ workspaceRoot, args });
1860
+ } else if (toolName === TOOL_BOSS_CHAT_PAUSE_RUN) {
1861
+ payload = await handleBossChatPauseRunTool({ workspaceRoot, args });
1862
+ } else if (toolName === TOOL_BOSS_CHAT_RESUME_RUN) {
1863
+ payload = await handleBossChatResumeRunTool({ workspaceRoot, args });
1864
+ } else if (toolName === TOOL_BOSS_CHAT_CANCEL_RUN) {
1865
+ payload = await handleBossChatCancelRunTool({ workspaceRoot, args });
1515
1866
  } else {
1516
1867
  return createJsonRpcError(id, -32602, `Unknown tool: ${toolName || ""}`);
1517
1868
  }