@reconcrap/boss-recommend-mcp 2.0.45 → 2.0.47

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 (56) hide show
  1. package/bin/boss-recommend-mcp.js +4 -4
  2. package/config/screening-config.example.json +27 -27
  3. package/package.json +1 -1
  4. package/scripts/postinstall.cjs +44 -44
  5. package/skills/boss-chat/README.md +39 -39
  6. package/skills/boss-chat/SKILL.md +93 -93
  7. package/skills/boss-recommend-pipeline/README.md +12 -12
  8. package/skills/boss-recommend-pipeline/SKILL.md +180 -180
  9. package/skills/boss-recruit-pipeline/README.md +17 -17
  10. package/skills/boss-recruit-pipeline/SKILL.md +58 -58
  11. package/src/chat-mcp.js +1780 -1780
  12. package/src/chat-runtime-config.js +749 -749
  13. package/src/cli.js +3054 -3054
  14. package/src/core/boss-cards/index.js +199 -199
  15. package/src/core/browser/index.js +1453 -1446
  16. package/src/core/capture/index.js +1201 -1201
  17. package/src/core/cv-acquisition/index.js +238 -238
  18. package/src/core/cv-capture-target/index.js +299 -299
  19. package/src/core/greet-quota/index.js +54 -54
  20. package/src/core/infinite-list/index.js +1326 -1326
  21. package/src/core/reporting/legacy-csv.js +341 -341
  22. package/src/core/run/timing.js +33 -33
  23. package/src/core/screening/index.js +50 -3
  24. package/src/core/self-heal/index.js +973 -973
  25. package/src/core/self-heal/viewport.js +564 -564
  26. package/src/domains/chat/cards.js +137 -137
  27. package/src/domains/chat/constants.js +221 -221
  28. package/src/domains/chat/detail.js +1668 -1661
  29. package/src/domains/chat/index.js +7 -7
  30. package/src/domains/chat/jobs.js +592 -588
  31. package/src/domains/chat/page-guard.js +98 -98
  32. package/src/domains/chat/roots.js +56 -56
  33. package/src/domains/chat/run-service.js +1977 -1955
  34. package/src/domains/recommend/actions.js +457 -457
  35. package/src/domains/recommend/cards.js +243 -243
  36. package/src/domains/recommend/constants.js +165 -165
  37. package/src/domains/recommend/detail.js +36 -28
  38. package/src/domains/recommend/filters.js +610 -581
  39. package/src/domains/recommend/index.js +10 -10
  40. package/src/domains/recommend/jobs.js +316 -263
  41. package/src/domains/recommend/refresh.js +472 -472
  42. package/src/domains/recommend/roots.js +80 -80
  43. package/src/domains/recommend/run-service.js +75 -35
  44. package/src/domains/recommend/scopes.js +246 -245
  45. package/src/domains/recruit/actions.js +277 -277
  46. package/src/domains/recruit/cards.js +74 -74
  47. package/src/domains/recruit/constants.js +167 -167
  48. package/src/domains/recruit/detail.js +461 -460
  49. package/src/domains/recruit/index.js +9 -9
  50. package/src/domains/recruit/instruction-parser.js +451 -451
  51. package/src/domains/recruit/refresh.js +44 -44
  52. package/src/domains/recruit/roots.js +68 -68
  53. package/src/domains/recruit/run-service.js +1207 -1161
  54. package/src/domains/recruit/search.js +1202 -1149
  55. package/src/recommend-mcp.js +22 -22
  56. package/src/recruit-mcp.js +1338 -1338
@@ -1,80 +1,80 @@
1
- import {
2
- findIframeDocument,
3
- getDocumentRoot,
4
- querySelector,
5
- sleep
6
- } from "../../core/browser/index.js";
7
- import { RECOMMEND_IFRAME_SELECTORS } from "./constants.js";
8
-
9
- export async function getRecommendRoots(client, {
10
- iframeSelectors = RECOMMEND_IFRAME_SELECTORS,
11
- requireFrame = true
12
- } = {}) {
13
- const topRoot = await getDocumentRoot(client);
14
- const iframe = await findIframeDocument(client, topRoot.nodeId, iframeSelectors);
15
- if (!iframe && requireFrame) {
16
- throw new Error("recommendFrame iframe was not found");
17
- }
18
-
19
- return {
20
- topRoot,
21
- iframe,
22
- roots: [
23
- { name: "top", nodeId: topRoot.nodeId },
24
- iframe ? { name: "recommend-frame", nodeId: iframe.documentNodeId } : null
25
- ].filter(Boolean),
26
- rootNodes: {
27
- top: topRoot.nodeId,
28
- frame: iframe?.documentNodeId || 0,
29
- frameOwner: iframe?.nodeId || 0
30
- }
31
- };
32
- }
33
-
34
- export async function waitForRecommendRoots(client, {
35
- timeoutMs = 10000,
36
- intervalMs = 250,
37
- iframeSelectors = RECOMMEND_IFRAME_SELECTORS
38
- } = {}) {
39
- const started = Date.now();
40
- let lastState = null;
41
- while (Date.now() - started <= timeoutMs) {
42
- try {
43
- lastState = await getRecommendRoots(client, {
44
- iframeSelectors,
45
- requireFrame: false
46
- });
47
- } catch (error) {
48
- lastState = {
49
- error: error?.message || String(error),
50
- roots: [],
51
- rootNodes: {
52
- top: 0,
53
- frame: 0,
54
- frameOwner: 0
55
- }
56
- };
57
- }
58
- if (lastState.iframe?.documentNodeId) return lastState;
59
- await sleep(intervalMs);
60
- }
61
- return lastState;
62
- }
63
-
64
- export async function queryFirstAcrossRoots(client, roots, selectors) {
65
- for (const root of roots) {
66
- if (!root?.nodeId) continue;
67
- for (const selector of selectors) {
68
- const nodeId = await querySelector(client, root.nodeId, selector);
69
- if (nodeId) {
70
- return {
71
- root: root.name,
72
- root_node_id: root.nodeId,
73
- selector,
74
- node_id: nodeId
75
- };
76
- }
77
- }
78
- }
79
- return null;
80
- }
1
+ import {
2
+ findIframeDocument,
3
+ getDocumentRoot,
4
+ querySelector,
5
+ sleep
6
+ } from "../../core/browser/index.js";
7
+ import { RECOMMEND_IFRAME_SELECTORS } from "./constants.js";
8
+
9
+ export async function getRecommendRoots(client, {
10
+ iframeSelectors = RECOMMEND_IFRAME_SELECTORS,
11
+ requireFrame = true
12
+ } = {}) {
13
+ const topRoot = await getDocumentRoot(client);
14
+ const iframe = await findIframeDocument(client, topRoot.nodeId, iframeSelectors);
15
+ if (!iframe && requireFrame) {
16
+ throw new Error("recommendFrame iframe was not found");
17
+ }
18
+
19
+ return {
20
+ topRoot,
21
+ iframe,
22
+ roots: [
23
+ { name: "top", nodeId: topRoot.nodeId },
24
+ iframe ? { name: "recommend-frame", nodeId: iframe.documentNodeId } : null
25
+ ].filter(Boolean),
26
+ rootNodes: {
27
+ top: topRoot.nodeId,
28
+ frame: iframe?.documentNodeId || 0,
29
+ frameOwner: iframe?.nodeId || 0
30
+ }
31
+ };
32
+ }
33
+
34
+ export async function waitForRecommendRoots(client, {
35
+ timeoutMs = 10000,
36
+ intervalMs = 250,
37
+ iframeSelectors = RECOMMEND_IFRAME_SELECTORS
38
+ } = {}) {
39
+ const started = Date.now();
40
+ let lastState = null;
41
+ while (Date.now() - started <= timeoutMs) {
42
+ try {
43
+ lastState = await getRecommendRoots(client, {
44
+ iframeSelectors,
45
+ requireFrame: false
46
+ });
47
+ } catch (error) {
48
+ lastState = {
49
+ error: error?.message || String(error),
50
+ roots: [],
51
+ rootNodes: {
52
+ top: 0,
53
+ frame: 0,
54
+ frameOwner: 0
55
+ }
56
+ };
57
+ }
58
+ if (lastState.iframe?.documentNodeId) return lastState;
59
+ await sleep(intervalMs);
60
+ }
61
+ return lastState;
62
+ }
63
+
64
+ export async function queryFirstAcrossRoots(client, roots, selectors) {
65
+ for (const root of roots) {
66
+ if (!root?.nodeId) continue;
67
+ for (const selector of selectors) {
68
+ const nodeId = await querySelector(client, root.nodeId, selector);
69
+ if (nodeId) {
70
+ return {
71
+ root: root.name,
72
+ root_node_id: root.nodeId,
73
+ selector,
74
+ node_id: nodeId
75
+ };
76
+ }
77
+ }
78
+ }
79
+ return null;
80
+ }
@@ -46,13 +46,14 @@ import {
46
46
  screenCandidate
47
47
  } from "../../core/screening/index.js";
48
48
  import {
49
- closeRecommendDetail,
50
- createRecommendDetailNetworkRecorder,
51
- extractRecommendDetailCandidate,
52
- isStaleRecommendNodeError,
53
- openRecommendCardDetailWithFreshRetry,
54
- waitForRecommendDetailNetworkEvents
55
- } from "./detail.js";
49
+ closeRecommendDetail,
50
+ createRecommendDetailNetworkRecorder,
51
+ extractRecommendDetailCandidate,
52
+ isRecommendDetailOpenMissError,
53
+ isStaleRecommendNodeError,
54
+ openRecommendCardDetailWithFreshRetry,
55
+ waitForRecommendDetailNetworkEvents
56
+ } from "./detail.js";
56
57
  import {
57
58
  readRecommendCardCandidate,
58
59
  waitForRecommendCardNodeIds
@@ -416,12 +417,13 @@ export function countRecommendResultStatuses(results = [], {
416
417
  detail_open_failed: results.filter((item) => (
417
418
  item.error?.code === "DETAIL_STALE_NODE"
418
419
  || item.error?.code === "DETAIL_OPEN_FAILED"
419
- )).length,
420
- transient_recovered: results.filter((item) => (
421
- item.error?.code === "DETAIL_STALE_NODE"
422
- || item.error?.code === "IMAGE_CAPTURE_STALE_NODE"
423
- || item.error?.code === "IMAGE_CAPTURE_TIMEOUT"
424
- || item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT"
420
+ )).length,
421
+ transient_recovered: results.filter((item) => (
422
+ item.error?.code === "DETAIL_STALE_NODE"
423
+ || item.error?.code === "DETAIL_OPEN_FAILED"
424
+ || item.error?.code === "IMAGE_CAPTURE_STALE_NODE"
425
+ || item.error?.code === "IMAGE_CAPTURE_TIMEOUT"
426
+ || item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT"
425
427
  )).length
426
428
  };
427
429
  }
@@ -449,15 +451,46 @@ function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
449
451
  if (error.close_result) {
450
452
  result.close_result = compactCloseResult(error.close_result);
451
453
  }
454
+ if (error.refresh_attempt) {
455
+ result.refresh_attempt = error.refresh_attempt;
456
+ }
457
+ if (error.list_end_reason) {
458
+ result.list_end_reason = error.list_end_reason;
459
+ }
460
+ if (error.target_count != null) {
461
+ result.target_count = error.target_count;
462
+ }
463
+ if (error.passed_count != null) {
464
+ result.passed_count = error.passed_count;
465
+ }
466
+ if (Array.isArray(error.recommend_detail_open_attempts)) {
467
+ result.recommend_detail_open_attempts = error.recommend_detail_open_attempts;
468
+ }
452
469
  return result;
453
470
  }
454
471
 
455
- function createRecommendCloseFailureError(closeResult) {
456
- const error = new Error(closeResult?.reason || "Recommend detail did not close before recovery");
457
- error.code = "DETAIL_CLOSE_FAILED";
458
- error.close_result = closeResult || null;
459
- return error;
460
- }
472
+ function createRecommendCloseFailureError(closeResult) {
473
+ const error = new Error(closeResult?.reason || "Recommend detail did not close before recovery");
474
+ error.code = "DETAIL_CLOSE_FAILED";
475
+ error.close_result = closeResult || null;
476
+ return error;
477
+ }
478
+
479
+ function createRecommendRefreshFailureError(refreshAttempt, {
480
+ listEndReason = "",
481
+ targetCount = 0,
482
+ passedCount = 0
483
+ } = {}) {
484
+ const reason = refreshAttempt?.reason || "refresh_failed";
485
+ const detail = refreshAttempt?.error ? `: ${refreshAttempt.error}` : "";
486
+ const error = new Error(`Recommend refresh failed before target was reached (${reason}${detail})`);
487
+ error.code = "RECOMMEND_END_REFRESH_FAILED";
488
+ error.refresh_attempt = refreshAttempt || null;
489
+ error.list_end_reason = listEndReason || null;
490
+ error.target_count = targetCount;
491
+ error.passed_count = passedCount;
492
+ return error;
493
+ }
461
494
 
462
495
  export function isRecoverableImageCaptureError(error) {
463
496
  const code = String(error?.code || "");
@@ -520,9 +553,9 @@ function createImageCaptureFailureScreening(candidate, error) {
520
553
  };
521
554
  }
522
555
 
523
- export function isRecoverableRecommendDetailError(error) {
524
- return isStaleRecommendNodeError(error);
525
- }
556
+ export function isRecoverableRecommendDetailError(error) {
557
+ return isStaleRecommendNodeError(error) || isRecommendDetailOpenMissError(error);
558
+ }
526
559
 
527
560
  function compactRecoverableDetailError(error) {
528
561
  return compactError(error, isStaleRecommendNodeError(error) ? "DETAIL_STALE_NODE" : "DETAIL_OPEN_FAILED");
@@ -532,10 +565,12 @@ function createRecoverableDetailFailureScreening(candidate, error) {
532
565
  return {
533
566
  status: "fail",
534
567
  passed: false,
535
- score: 0,
536
- reasons: isStaleRecommendNodeError(error)
537
- ? ["detail_open_failed", "stale_node"]
538
- : ["detail_open_failed"],
568
+ score: 0,
569
+ reasons: isStaleRecommendNodeError(error)
570
+ ? ["detail_open_failed", "stale_node"]
571
+ : isRecommendDetailOpenMissError(error)
572
+ ? ["detail_open_failed", "detail_open_miss"]
573
+ : ["detail_open_failed"],
539
574
  error: compactRecoverableDetailError(error),
540
575
  candidate
541
576
  };
@@ -960,9 +995,9 @@ export async function runRecommendWorkflow({
960
995
  refresh_forced_recent_not_view: true,
961
996
  list_end_reason: listEndReason
962
997
  });
963
- if (refreshResult.ok) {
964
- rootState = refreshResult.root_state || await getRecommendRoots(client);
965
- rootState = await ensureRecommendViewport(rootState, "refresh_after");
998
+ if (refreshResult.ok) {
999
+ rootState = refreshResult.root_state || await getRecommendRoots(client);
1000
+ rootState = await ensureRecommendViewport(rootState, "refresh_after");
966
1001
  cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
967
1002
  timeoutMs: cardTimeoutMs,
968
1003
  intervalMs: 300
@@ -976,12 +1011,17 @@ export async function runRecommendWorkflow({
976
1011
  forced_recent_not_view: true
977
1012
  }
978
1013
  });
979
- listEndReason = "";
980
- continue;
981
- }
982
- }
983
- break;
984
- }
1014
+ listEndReason = "";
1015
+ continue;
1016
+ }
1017
+ throw createRecommendRefreshFailureError(compactRefresh, {
1018
+ listEndReason,
1019
+ targetCount: targetPassCount,
1020
+ passedCount: countPassedResults(results)
1021
+ });
1022
+ }
1023
+ break;
1024
+ }
985
1025
 
986
1026
  const index = results.length;
987
1027
  let cardNodeId = nextCandidateResult.item.node_id;