@reconcrap/boss-recommend-mcp 2.1.23 → 2.1.24

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 (66) hide show
  1. package/README.md +5 -0
  2. package/bin/boss-recommend-mcp.js +4 -4
  3. package/package.json +6 -1
  4. package/scripts/install-macos.sh +280 -280
  5. package/scripts/postinstall.cjs +44 -44
  6. package/skills/boss-chat/README.md +43 -42
  7. package/skills/boss-chat/SKILL.md +107 -106
  8. package/skills/boss-recommend-pipeline/README.md +13 -13
  9. package/skills/boss-recommend-pipeline/SKILL.md +47 -47
  10. package/skills/boss-recruit-pipeline/README.md +19 -19
  11. package/skills/boss-recruit-pipeline/SKILL.md +89 -89
  12. package/src/chat-mcp.js +301 -127
  13. package/src/core/boss-cards/index.js +199 -199
  14. package/src/core/browser/index.js +286 -136
  15. package/src/core/capture/index.js +2930 -1201
  16. package/src/core/cv-acquisition/index.js +512 -238
  17. package/src/core/cv-capture-target/index.js +513 -299
  18. package/src/core/greet-quota/index.js +71 -71
  19. package/src/core/infinite-list/index.js +31 -31
  20. package/src/core/reporting/legacy-csv.js +12 -12
  21. package/src/core/run/detached-launcher.js +305 -0
  22. package/src/core/run/index.js +105 -52
  23. package/src/core/run/timing.js +33 -33
  24. package/src/core/run/windows-detached-worker.ps1 +106 -0
  25. package/src/core/screening/index.js +2135 -2135
  26. package/src/core/self-heal/index.js +989 -973
  27. package/src/core/self-heal/viewport.js +1505 -564
  28. package/src/detached-worker.js +99 -99
  29. package/src/domains/chat/action-journal.js +443 -0
  30. package/src/domains/chat/cards.js +137 -137
  31. package/src/domains/chat/constants.js +9 -9
  32. package/src/domains/chat/detail.js +1684 -401
  33. package/src/domains/chat/index.js +8 -7
  34. package/src/domains/chat/jobs.js +620 -620
  35. package/src/domains/chat/page-guard.js +157 -122
  36. package/src/domains/chat/roots.js +56 -56
  37. package/src/domains/chat/run-service.js +1801 -760
  38. package/src/domains/common/account-rights-panel.js +314 -314
  39. package/src/domains/common/recovery-settle.js +159 -159
  40. package/src/domains/recommend/actions.js +515 -472
  41. package/src/domains/recommend/cards.js +243 -243
  42. package/src/domains/recommend/colleague-contact.js +333 -333
  43. package/src/domains/recommend/constants.js +92 -92
  44. package/src/domains/recommend/detail.js +12 -12
  45. package/src/domains/recommend/filters.js +611 -611
  46. package/src/domains/recommend/index.js +9 -9
  47. package/src/domains/recommend/jobs.js +542 -542
  48. package/src/domains/recommend/location.js +736 -736
  49. package/src/domains/recommend/refresh.js +422 -422
  50. package/src/domains/recommend/roots.js +80 -80
  51. package/src/domains/recommend/run-service.js +1791 -1205
  52. package/src/domains/recommend/scopes.js +246 -246
  53. package/src/domains/recruit/actions.js +277 -277
  54. package/src/domains/recruit/cards.js +74 -74
  55. package/src/domains/recruit/constants.js +236 -236
  56. package/src/domains/recruit/detail.js +588 -588
  57. package/src/domains/recruit/index.js +9 -9
  58. package/src/domains/recruit/instruction-parser.js +866 -866
  59. package/src/domains/recruit/refresh.js +45 -45
  60. package/src/domains/recruit/roots.js +68 -68
  61. package/src/domains/recruit/run-service.js +1817 -1620
  62. package/src/domains/recruit/search.js +3229 -3229
  63. package/src/index.js +16 -1
  64. package/src/parser.js +1296 -1296
  65. package/src/recommend-mcp.js +551 -362
  66. package/src/recommend-scheduler.js +75 -75
@@ -1,19 +1,19 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
- import {
5
- assertNoForbiddenCdpCalls,
6
- bringPageToFront,
7
- connectToChromeTargetOrOpen,
8
- createBossLoginRequiredError,
9
- detectBossLoginState,
10
- enableDomains,
11
- FORBIDDEN_CDP_METHODS,
12
- getMainFrameUrl,
13
- isBossLoginUrl,
14
- waitForMainFrameUrl,
15
- sleep
16
- } from "./core/browser/index.js";
4
+ import {
5
+ assertNoForbiddenCdpCalls,
6
+ bringPageToFront,
7
+ connectToChromeTargetOrOpen,
8
+ createBossLoginRequiredError,
9
+ detectBossLoginState,
10
+ enableDomains,
11
+ FORBIDDEN_CDP_METHODS,
12
+ getMainFrameUrl,
13
+ isBossLoginUrl,
14
+ waitForMainFrameUrl,
15
+ sleep
16
+ } from "./core/browser/index.js";
17
17
  import {
18
18
  RUN_STATUS_CANCELING,
19
19
  RUN_STATUS_CANCELED,
@@ -278,22 +278,22 @@ function normalizeScreeningModeArg(args = {}) {
278
278
  : "llm";
279
279
  }
280
280
 
281
- function collectRecommendDebugTestOptions(args = {}, normalized = {}) {
282
- const reasons = [];
281
+ function collectRecommendDebugTestOptions(args = {}, normalized = {}) {
282
+ const reasons = [];
283
283
  if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
284
284
  if (args.allow_card_only_screening === true) reasons.push("allow_card_only_screening");
285
285
  if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
286
286
  if (args.no_filter === true) reasons.push("no_filter");
287
- if (args.filter_enabled === false) reasons.push("filter_enabled=false");
288
- if (args.debug_force_list_end_after_processed != null) {
289
- reasons.push("debug_force_list_end_after_processed");
290
- }
291
- if (args.debug_force_context_recovery_after_processed != null) {
292
- reasons.push("debug_force_context_recovery_after_processed");
293
- }
294
- if (args.debug_force_cdp_reconnect_after_processed != null) {
295
- reasons.push("debug_force_cdp_reconnect_after_processed");
296
- }
287
+ if (args.filter_enabled === false) reasons.push("filter_enabled=false");
288
+ if (args.debug_force_list_end_after_processed != null) {
289
+ reasons.push("debug_force_list_end_after_processed");
290
+ }
291
+ if (args.debug_force_context_recovery_after_processed != null) {
292
+ reasons.push("debug_force_context_recovery_after_processed");
293
+ }
294
+ if (args.debug_force_cdp_reconnect_after_processed != null) {
295
+ reasons.push("debug_force_cdp_reconnect_after_processed");
296
+ }
297
297
  if (args.dry_run_post_action === true) reasons.push("dry_run_post_action");
298
298
  if (args.execute_post_action === false && normalized.postAction && normalized.postAction !== "none") {
299
299
  reasons.push("execute_post_action=false");
@@ -313,36 +313,44 @@ function resolveRecommendDetailLimit(args = {}, normalized = {}) {
313
313
  return requested;
314
314
  }
315
315
 
316
- function methodSummary(methodLog = []) {
317
- const summary = {};
318
- for (const entry of methodLog || []) {
319
- const method = compactCdpMethodName(entry?.method || entry || "");
320
- if (!method) continue;
321
- summary[method] = (summary[method] || 0) + 1;
322
- }
323
- return summary;
324
- }
325
-
326
- function compactCdpMethodName(value) {
327
- const method = normalizeText(value);
328
- return /^[A-Za-z][A-Za-z0-9_]*\.[A-Za-z][A-Za-z0-9_]*(?::retry_after_reconnect)?$/.test(method)
329
- ? method
330
- : "";
331
- }
332
-
333
- function compactCdpMethodTimestamp(value) {
334
- const timestamp = normalizeText(value);
335
- const parsed = Date.parse(timestamp);
336
- return Number.isFinite(parsed) ? new Date(parsed).toISOString() : "";
337
- }
338
-
316
+ function methodSummary(methodLog = []) {
317
+ const summary = {};
318
+ for (const entry of methodLog || []) {
319
+ const method = compactCdpMethodName(entry?.method || entry || "");
320
+ if (!method) continue;
321
+ summary[method] = (summary[method] || 0) + 1;
322
+ }
323
+ return summary;
324
+ }
325
+
326
+ function compactCdpMethodName(value) {
327
+ const method = normalizeText(value);
328
+ return /^[A-Za-z][A-Za-z0-9_]*\.[A-Za-z][A-Za-z0-9_]*(?::retry_after_reconnect)?$/.test(method)
329
+ ? method
330
+ : "";
331
+ }
332
+
333
+ function compactCdpMethodTimestamp(value) {
334
+ const timestamp = normalizeText(value);
335
+ const parsed = Date.parse(timestamp);
336
+ return Number.isFinite(parsed) ? new Date(parsed).toISOString() : "";
337
+ }
338
+
339
339
  function compactMethodLogForStatus(methodLog = []) {
340
340
  if (!Array.isArray(methodLog)) return [];
341
341
  return methodLog
342
- .map((entry) => ({
343
- method: compactCdpMethodName(entry?.method || entry || ""),
344
- at: compactCdpMethodTimestamp(entry?.at || "")
345
- }))
342
+ .map((entry) => {
343
+ const compact = {
344
+ method: compactCdpMethodName(entry?.method || entry || ""),
345
+ at: compactCdpMethodTimestamp(entry?.at || "")
346
+ };
347
+ for (const key of ["connection_epoch", "replay_of_connection_epoch"]) {
348
+ if (Number.isInteger(entry?.[key]) && entry[key] >= 0) compact[key] = entry[key];
349
+ }
350
+ const replayPolicy = normalizeText(entry?.replay_policy || "");
351
+ if (replayPolicy) compact.replay_policy = replayPolicy.slice(0, 100);
352
+ return compact;
353
+ })
346
354
  .filter((entry) => Boolean(entry.method))
347
355
  .slice(-STATUS_METHOD_LOG_TAIL_LIMIT);
348
356
  }
@@ -359,130 +367,130 @@ function plainRecord(value) {
359
367
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
360
368
  }
361
369
 
362
- function nonEmptyRecord(value) {
363
- const record = plainRecord(value);
364
- return Object.keys(record).length ? record : null;
365
- }
366
-
367
- function compactMethodSummaryForStatus(value = {}) {
368
- const compact = {};
369
- for (const [rawMethod, rawCount] of Object.entries(plainRecord(value))) {
370
- const method = compactCdpMethodName(rawMethod);
371
- const count = Number(rawCount);
372
- if (!method || !Number.isInteger(count) || count < 1) continue;
373
- compact[method] = count;
374
- }
375
- return compact;
376
- }
377
-
378
- function compactChromeEvidenceUrl(value) {
379
- const text = normalizeText(value);
380
- if (!text) return null;
381
- try {
382
- const parsed = new URL(text);
383
- parsed.username = "";
384
- parsed.password = "";
385
- parsed.search = "";
386
- parsed.hash = "";
387
- return parsed.toString();
388
- } catch {
389
- return text.split(/[?#]/, 1)[0] || null;
390
- }
391
- }
392
-
393
- function compactChromeAutoLaunchEvidence(value = {}) {
394
- const source = plainRecord(value);
395
- if (!Object.keys(source).length) return null;
396
- const compact = {};
397
- const booleanKeys = [
398
- "launched",
399
- "reused",
400
- "guard_checked",
401
- "required_flags_ok",
402
- "replaced",
403
- "target_created",
404
- "fallback_target",
405
- "fallback_any_page"
406
- ];
407
- for (const key of booleanKeys) {
408
- if (typeof source[key] === "boolean") compact[key] = source[key];
409
- }
410
- for (const key of ["port", "target_count", "command_line_args_count"]) {
411
- if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
412
- }
413
- for (const key of ["close_method", "command_line_source"]) {
414
- const text = normalizeText(source[key]);
415
- if (text) compact[key] = text;
416
- }
417
- return Object.keys(compact).length ? compact : null;
418
- }
419
-
420
- function compactChromeEvidence(value = {}) {
421
- const source = plainRecord(value);
422
- if (!Object.keys(source).length) return null;
423
- const compact = {};
424
- const host = normalizeText(source.host);
425
- const targetId = normalizeText(source.target_id);
426
- const targetUrl = compactChromeEvidenceUrl(source.target_url);
427
- if (host) compact.host = host;
428
- if (Number.isInteger(source.port) && source.port > 0) compact.port = source.port;
429
- if (targetUrl) compact.target_url = targetUrl;
430
- if (targetId) compact.target_id = targetId;
431
- const autoLaunch = compactChromeAutoLaunchEvidence(source.auto_launch);
432
- if (autoLaunch) compact.auto_launch = autoLaunch;
433
- return Object.keys(compact).length ? compact : null;
434
- }
435
-
436
- function methodEvidenceFlags(methodSummaryValue = {}) {
437
- const methods = Object.keys(methodSummaryValue);
438
- return {
439
- runtime_evaluate_used: methods.some((method) => method.split(".", 1)[0] === "Runtime"),
440
- script_injection_used: methods.some((method) => (
441
- FORBIDDEN_CDP_METHODS.has(method.replace(/:retry_after_reconnect$/, ""))
442
- ))
443
- };
444
- }
445
-
446
- function buildPersistedCdpEvidence({ meta = {}, persisted = {} } = {}) {
447
- const hasLiveMethodLog = Array.isArray(meta.methodLog);
448
- const persistedMethodLog = compactMethodLogForStatus(persisted.method_log);
449
- const methodLog = hasLiveMethodLog
450
- ? compactMethodLogForStatus(meta.methodLog)
451
- : persistedMethodLog;
452
- const persistedSummary = compactMethodSummaryForStatus(persisted.method_summary);
453
- const summary = hasLiveMethodLog
454
- ? methodSummary(meta.methodLog)
455
- : Object.keys(persistedSummary).length
456
- ? persistedSummary
457
- : methodSummary(methodLog);
458
- const liveTotal = hasLiveMethodLog ? meta.methodLog.length : null;
459
- const persistedTotal = Number(persisted.method_log_total);
460
- const methodLogTotal = Number.isInteger(liveTotal)
461
- ? liveTotal
462
- : Number.isInteger(persistedTotal) && persistedTotal >= methodLog.length
463
- ? persistedTotal
464
- : methodLog.length;
465
- const inferredFlags = methodEvidenceFlags(summary);
466
- return {
467
- runtime_evaluate_used: hasLiveMethodLog
468
- ? inferredFlags.runtime_evaluate_used
469
- : persisted.runtime_evaluate_used === true || inferredFlags.runtime_evaluate_used,
470
- script_injection_used: hasLiveMethodLog
471
- ? inferredFlags.script_injection_used
472
- : persisted.script_injection_used === true || inferredFlags.script_injection_used,
473
- method_summary: summary,
474
- method_log: methodLog,
475
- method_log_total: methodLogTotal,
476
- chrome: compactChromeEvidence(meta.chrome || persisted.chrome)
477
- };
478
- }
479
-
480
- function attachPersistedCdpEvidence(payload, persisted = {}) {
481
- return {
482
- ...payload,
483
- ...buildPersistedCdpEvidence({ persisted })
484
- };
485
- }
370
+ function nonEmptyRecord(value) {
371
+ const record = plainRecord(value);
372
+ return Object.keys(record).length ? record : null;
373
+ }
374
+
375
+ function compactMethodSummaryForStatus(value = {}) {
376
+ const compact = {};
377
+ for (const [rawMethod, rawCount] of Object.entries(plainRecord(value))) {
378
+ const method = compactCdpMethodName(rawMethod);
379
+ const count = Number(rawCount);
380
+ if (!method || !Number.isInteger(count) || count < 1) continue;
381
+ compact[method] = count;
382
+ }
383
+ return compact;
384
+ }
385
+
386
+ function compactChromeEvidenceUrl(value) {
387
+ const text = normalizeText(value);
388
+ if (!text) return null;
389
+ try {
390
+ const parsed = new URL(text);
391
+ parsed.username = "";
392
+ parsed.password = "";
393
+ parsed.search = "";
394
+ parsed.hash = "";
395
+ return parsed.toString();
396
+ } catch {
397
+ return text.split(/[?#]/, 1)[0] || null;
398
+ }
399
+ }
400
+
401
+ function compactChromeAutoLaunchEvidence(value = {}) {
402
+ const source = plainRecord(value);
403
+ if (!Object.keys(source).length) return null;
404
+ const compact = {};
405
+ const booleanKeys = [
406
+ "launched",
407
+ "reused",
408
+ "guard_checked",
409
+ "required_flags_ok",
410
+ "replaced",
411
+ "target_created",
412
+ "fallback_target",
413
+ "fallback_any_page"
414
+ ];
415
+ for (const key of booleanKeys) {
416
+ if (typeof source[key] === "boolean") compact[key] = source[key];
417
+ }
418
+ for (const key of ["port", "target_count", "command_line_args_count"]) {
419
+ if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
420
+ }
421
+ for (const key of ["close_method", "command_line_source"]) {
422
+ const text = normalizeText(source[key]);
423
+ if (text) compact[key] = text;
424
+ }
425
+ return Object.keys(compact).length ? compact : null;
426
+ }
427
+
428
+ function compactChromeEvidence(value = {}) {
429
+ const source = plainRecord(value);
430
+ if (!Object.keys(source).length) return null;
431
+ const compact = {};
432
+ const host = normalizeText(source.host);
433
+ const targetId = normalizeText(source.target_id);
434
+ const targetUrl = compactChromeEvidenceUrl(source.target_url);
435
+ if (host) compact.host = host;
436
+ if (Number.isInteger(source.port) && source.port > 0) compact.port = source.port;
437
+ if (targetUrl) compact.target_url = targetUrl;
438
+ if (targetId) compact.target_id = targetId;
439
+ const autoLaunch = compactChromeAutoLaunchEvidence(source.auto_launch);
440
+ if (autoLaunch) compact.auto_launch = autoLaunch;
441
+ return Object.keys(compact).length ? compact : null;
442
+ }
443
+
444
+ function methodEvidenceFlags(methodSummaryValue = {}) {
445
+ const methods = Object.keys(methodSummaryValue);
446
+ return {
447
+ runtime_evaluate_used: methods.some((method) => method.split(".", 1)[0] === "Runtime"),
448
+ script_injection_used: methods.some((method) => (
449
+ FORBIDDEN_CDP_METHODS.has(method.replace(/:retry_after_reconnect$/, ""))
450
+ ))
451
+ };
452
+ }
453
+
454
+ function buildPersistedCdpEvidence({ meta = {}, persisted = {} } = {}) {
455
+ const hasLiveMethodLog = Array.isArray(meta.methodLog);
456
+ const persistedMethodLog = compactMethodLogForStatus(persisted.method_log);
457
+ const methodLog = hasLiveMethodLog
458
+ ? compactMethodLogForStatus(meta.methodLog)
459
+ : persistedMethodLog;
460
+ const persistedSummary = compactMethodSummaryForStatus(persisted.method_summary);
461
+ const summary = hasLiveMethodLog
462
+ ? methodSummary(meta.methodLog)
463
+ : Object.keys(persistedSummary).length
464
+ ? persistedSummary
465
+ : methodSummary(methodLog);
466
+ const liveTotal = hasLiveMethodLog ? meta.methodLog.length : null;
467
+ const persistedTotal = Number(persisted.method_log_total);
468
+ const methodLogTotal = Number.isInteger(liveTotal)
469
+ ? liveTotal
470
+ : Number.isInteger(persistedTotal) && persistedTotal >= methodLog.length
471
+ ? persistedTotal
472
+ : methodLog.length;
473
+ const inferredFlags = methodEvidenceFlags(summary);
474
+ return {
475
+ runtime_evaluate_used: hasLiveMethodLog
476
+ ? inferredFlags.runtime_evaluate_used
477
+ : persisted.runtime_evaluate_used === true || inferredFlags.runtime_evaluate_used,
478
+ script_injection_used: hasLiveMethodLog
479
+ ? inferredFlags.script_injection_used
480
+ : persisted.script_injection_used === true || inferredFlags.script_injection_used,
481
+ method_summary: summary,
482
+ method_log: methodLog,
483
+ method_log_total: methodLogTotal,
484
+ chrome: compactChromeEvidence(meta.chrome || persisted.chrome)
485
+ };
486
+ }
487
+
488
+ function attachPersistedCdpEvidence(payload, persisted = {}) {
489
+ return {
490
+ ...payload,
491
+ ...buildPersistedCdpEvidence({ persisted })
492
+ };
493
+ }
486
494
 
487
495
  function normalizeRunId(runId) {
488
496
  const normalized = normalizeText(runId);
@@ -530,12 +538,12 @@ function readJsonFile(filePath) {
530
538
 
531
539
  function recommendSearchParamsForCsv(searchParams = {}) {
532
540
  return {
533
- school_tag: Object.prototype.hasOwnProperty.call(searchParams, "school_tag") ? searchParams.school_tag : "不限",
534
- degree: Object.prototype.hasOwnProperty.call(searchParams, "degree") ? searchParams.degree : "不限",
535
- gender: Object.prototype.hasOwnProperty.call(searchParams, "gender") ? searchParams.gender : "不限",
536
- recent_not_view: Object.prototype.hasOwnProperty.call(searchParams, "recent_not_view") ? searchParams.recent_not_view : "不限",
537
- current_city_only: Object.prototype.hasOwnProperty.call(searchParams, "current_city_only") ? searchParams.current_city_only : false,
538
- activity_level: Object.prototype.hasOwnProperty.call(searchParams, "activity_level") ? searchParams.activity_level : "不限"
541
+ school_tag: Object.prototype.hasOwnProperty.call(searchParams, "school_tag") ? searchParams.school_tag : "不限",
542
+ degree: Object.prototype.hasOwnProperty.call(searchParams, "degree") ? searchParams.degree : "不限",
543
+ gender: Object.prototype.hasOwnProperty.call(searchParams, "gender") ? searchParams.gender : "不限",
544
+ recent_not_view: Object.prototype.hasOwnProperty.call(searchParams, "recent_not_view") ? searchParams.recent_not_view : "不限",
545
+ current_city_only: Object.prototype.hasOwnProperty.call(searchParams, "current_city_only") ? searchParams.current_city_only : false,
546
+ activity_level: Object.prototype.hasOwnProperty.call(searchParams, "activity_level") ? searchParams.activity_level : "不限"
539
547
  };
540
548
  }
541
549
 
@@ -574,12 +582,12 @@ function selectedRecommendJobForCsv(meta = {}, snapshot = {}) {
574
582
  function buildRecommendCsvInputRows(snapshot = {}, meta = {}) {
575
583
  const { context, confirmation, overrides, followUp, shared } = getSnapshotRequestContext(snapshot);
576
584
  const searchParams = recommendSearchParamsForCsv(meta.parsed?.searchParams || {
577
- school_tag: overrides.school_tag ?? confirmation.school_tag_value,
578
- degree: overrides.degree ?? confirmation.degree_value,
579
- gender: overrides.gender ?? confirmation.gender_value,
580
- recent_not_view: overrides.recent_not_view ?? confirmation.recent_not_view_value,
581
- current_city_only: overrides.current_city_only ?? false,
582
- activity_level: overrides.activity_level ?? "不限"
585
+ school_tag: overrides.school_tag ?? confirmation.school_tag_value,
586
+ degree: overrides.degree ?? confirmation.degree_value,
587
+ gender: overrides.gender ?? confirmation.gender_value,
588
+ recent_not_view: overrides.recent_not_view ?? confirmation.recent_not_view_value,
589
+ current_city_only: overrides.current_city_only ?? false,
590
+ activity_level: overrides.activity_level ?? "不限"
583
591
  });
584
592
  const parsedScreenParams = meta.parsed?.screenParams || {};
585
593
  const screenParams = {
@@ -654,26 +662,36 @@ function normalizeLegacyProgress(progress = {}, summary = null) {
654
662
  : Number.isInteger(summary?.passed)
655
663
  ? summary.passed
656
664
  : 0;
657
- const normalized = {
658
- ...progress,
665
+ const normalized = {
666
+ ...progress,
659
667
  processed,
660
668
  inspected: processed,
661
669
  screened,
662
670
  passed,
663
671
  skipped: Number.isInteger(progress.skipped) ? progress.skipped : Math.max(processed - passed, 0),
664
- greet_count: Number.isInteger(progress.greet_count) ? progress.greet_count : 0,
665
- post_action_clicked: Number.isInteger(progress.post_action_clicked) ? progress.post_action_clicked : 0
666
- };
667
- if (Object.prototype.hasOwnProperty.call(normalized, "last_list_read_stale_diagnostic")) {
668
- normalized.last_list_read_stale_diagnostic = compactListReadStaleDiagnosticForStatus(
669
- normalized.last_list_read_stale_diagnostic
670
- );
671
- }
672
+ greet_count: Number.isInteger(progress.greet_count) ? progress.greet_count : 0,
673
+ post_action_clicked: Number.isInteger(progress.post_action_clicked) ? progress.post_action_clicked : 0
674
+ };
675
+ if (Object.prototype.hasOwnProperty.call(normalized, "last_list_read_stale_diagnostic")) {
676
+ normalized.last_list_read_stale_diagnostic = compactListReadStaleDiagnosticForStatus(
677
+ normalized.last_list_read_stale_diagnostic
678
+ );
679
+ }
672
680
  if (Object.prototype.hasOwnProperty.call(normalized, "list_read_stale_diagnostics")) {
673
681
  normalized.list_read_stale_diagnostics = compactListReadStaleDiagnosticsForStatus(
674
682
  normalized.list_read_stale_diagnostics
675
683
  );
676
684
  }
685
+ if (Object.prototype.hasOwnProperty.call(normalized, "dom_stale_forensic")) {
686
+ normalized.dom_stale_forensic = compactDomStaleForensicForStatus(
687
+ normalized.dom_stale_forensic
688
+ );
689
+ }
690
+ if (Object.prototype.hasOwnProperty.call(normalized, "dom_stale_forensics")) {
691
+ normalized.dom_stale_forensics = compactDomStaleForensicsForStatus(
692
+ normalized.dom_stale_forensics
693
+ );
694
+ }
677
695
  return normalized;
678
696
  }
679
697
 
@@ -703,73 +721,236 @@ function compactPositiveInteger(value, fallback = null) {
703
721
  return Number.isInteger(value) && value >= 0 ? value : fallback;
704
722
  }
705
723
 
706
- function compactSmallRecord(value, fallback = null) {
724
+ function compactSmallRecord(value, fallback = null) {
707
725
  const record = plainRecord(value);
708
726
  if (!Object.keys(record).length) return fallback;
709
- return clonePlain(record, fallback);
710
- }
711
-
727
+ return clonePlain(record, fallback);
728
+ }
729
+
712
730
  function compactListReadStaleDiagnosticForStatus(value) {
713
731
  const source = plainRecord(value);
714
732
  if (!Object.keys(source).length) return null;
715
733
  const compact = {};
716
- for (const key of ["code", "message", "phase", "cdp_method", "cdp_at", "recovery_mode"]) {
734
+ for (const key of [
735
+ "name",
736
+ "code",
737
+ "message",
738
+ "phase",
739
+ "cdp_method",
740
+ "cdp_at",
741
+ "cdp_search_id",
742
+ "cdp_replay_policy",
743
+ "recovery_mode"
744
+ ]) {
717
745
  const text = normalizeText(source[key] || "");
718
746
  if (text) compact[key] = text.slice(0, key === "message" ? 500 : 200);
719
747
  }
720
- for (const key of ["cdp_node_id", "cdp_backend_node_id", "attempt"]) {
748
+ for (const key of [
749
+ "cdp_node_id",
750
+ "cdp_backend_node_id",
751
+ "cdp_connection_epoch",
752
+ "cdp_reconnected_epoch",
753
+ "attempt"
754
+ ]) {
721
755
  if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
722
756
  }
723
- for (const key of ["exhausted", "recovery_applied", "recovered"]) {
757
+ for (const key of [
758
+ "exhausted",
759
+ "recovery_applied",
760
+ "recovered",
761
+ "cdp_replay_suppressed",
762
+ "cdp_outcome_unknown",
763
+ "cdp_reconnected",
764
+ "cdp_replayed_after_reconnect"
765
+ ]) {
724
766
  if (typeof source[key] === "boolean") compact[key] = source[key];
725
767
  }
726
- for (const key of ["at", "recovery_applied_at", "recovered_at"]) {
727
- const text = normalizeText(source[key] || "");
728
- if (text && Number.isFinite(Date.parse(text))) compact[key] = new Date(text).toISOString();
729
- }
768
+ for (const key of ["at", "recovery_applied_at", "recovered_at"]) {
769
+ const text = normalizeText(source[key] || "");
770
+ if (text && Number.isFinite(Date.parse(text))) compact[key] = new Date(text).toISOString();
771
+ }
730
772
  if (Array.isArray(source.cdp_param_keys)) {
731
- compact.cdp_param_keys = source.cdp_param_keys
732
- .map((key) => normalizeText(key))
773
+ compact.cdp_param_keys = source.cdp_param_keys
774
+ .map((key) => normalizeText(key))
733
775
  .filter((key) => /^[A-Za-z][A-Za-z0-9_]*$/.test(key))
734
776
  .slice(0, 20);
735
777
  }
778
+ if (source.cdp_reconnect_error !== undefined && source.cdp_reconnect_error !== null) {
779
+ const reconnectError = plainRecord(source.cdp_reconnect_error);
780
+ if (Object.keys(reconnectError).length) {
781
+ const safeReconnectError = {};
782
+ for (const key of ["name", "code", "message"]) {
783
+ const text = normalizeText(reconnectError[key] || "");
784
+ if (text) safeReconnectError[key] = text.slice(0, key === "message" ? 500 : 200);
785
+ }
786
+ if (Object.keys(safeReconnectError).length) compact.cdp_reconnect_error = safeReconnectError;
787
+ } else {
788
+ const reconnectErrorText = normalizeText(source.cdp_reconnect_error);
789
+ if (reconnectErrorText) compact.cdp_reconnect_error = reconnectErrorText.slice(0, 500);
790
+ }
791
+ }
736
792
  return Object.keys(compact).length ? compact : null;
737
793
  }
738
794
 
739
- function compactListReadStaleDiagnosticsForStatus(value) {
795
+ function compactDomStaleRootIdentityForStatus(value) {
796
+ const source = plainRecord(value);
797
+ if (!Object.keys(source).length) return null;
798
+ const compact = {};
799
+ for (const key of [
800
+ "connection_epoch",
801
+ "top_document_node_id",
802
+ "iframe_owner_node_id",
803
+ "iframe_document_node_id"
804
+ ]) {
805
+ if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
806
+ }
807
+ const iframeSelector = normalizeText(source.iframe_selector || "");
808
+ if (iframeSelector) compact.iframe_selector = iframeSelector.slice(0, 300);
809
+ return Object.keys(compact).length ? compact : null;
810
+ }
811
+
812
+ function compactDomStaleNumericRecordForStatus(value) {
813
+ const source = plainRecord(value);
814
+ if (!Object.keys(source).length) return null;
815
+ const compact = {};
816
+ for (const [key, rawValue] of Object.entries(source)) {
817
+ if (!/^[a-z][a-z0-9_]*$/i.test(key)) continue;
818
+ if (!Number.isInteger(rawValue) || rawValue < 0) continue;
819
+ compact[key] = rawValue;
820
+ if (Object.keys(compact).length >= 30) break;
821
+ }
822
+ return Object.keys(compact).length ? compact : null;
823
+ }
824
+
825
+ function compactDomStaleLifecycleTimelineForStatus(value) {
740
826
  if (!Array.isArray(value)) return [];
741
827
  return value
742
- .map((item) => compactListReadStaleDiagnosticForStatus(item))
828
+ .map((rawEntry) => {
829
+ const entry = plainRecord(rawEntry);
830
+ const compact = {};
831
+ const at = compactCdpMethodTimestamp(entry.at || "");
832
+ if (at) compact.at = at;
833
+ for (const key of ["type", "operation", "frame_id", "parent_frame_id", "loader_id"]) {
834
+ const text = normalizeText(entry[key] || "");
835
+ if (text) compact[key] = text.slice(0, 300);
836
+ }
837
+ if (Number.isInteger(entry.connection_epoch) && entry.connection_epoch >= 0) {
838
+ compact.connection_epoch = entry.connection_epoch;
839
+ }
840
+ const url = compactChromeEvidenceUrl(entry.url);
841
+ if (url) compact.url = url;
842
+ return Object.keys(compact).length ? compact : null;
843
+ })
743
844
  .filter(Boolean)
744
- .slice(-12);
845
+ .slice(-20);
745
846
  }
746
847
 
747
- function compactListReadStaleCheckpointEvent(value) {
848
+ function compactDomStaleCandidateForStatus(value) {
748
849
  const source = plainRecord(value);
749
850
  if (!Object.keys(source).length) return null;
750
851
  const compact = {};
751
- const diagnostic = compactListReadStaleDiagnosticForStatus(source.diagnostic || source.trigger);
752
- if (diagnostic) compact.diagnostic = diagnostic;
753
- const recentDiagnostics = compactListReadStaleDiagnosticsForStatus(source.recent_diagnostics);
754
- if (recentDiagnostics.length) compact.recent_diagnostics = recentDiagnostics;
755
- for (const key of ["recovery_count", "recovery_applied_count"]) {
852
+ for (const key of ["index", "card_node_id", "visible_index", "failing_list_node_id"]) {
756
853
  if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
757
854
  }
758
- if (source.recovery_error) {
759
- const recoveryError = compactListReadStaleDiagnosticForStatus(source.recovery_error);
760
- if (recoveryError) compact.recovery_error = recoveryError;
855
+ const candidateKey = normalizeText(source.key || "");
856
+ if (candidateKey) compact.key = candidateKey.slice(0, 300);
857
+ return Object.keys(compact).length ? compact : null;
858
+ }
859
+
860
+ function compactDomStaleRecoveryForStatus(value) {
861
+ const source = plainRecord(value);
862
+ if (!Object.keys(source).length) return null;
863
+ const compact = {};
864
+ for (const key of ["status", "mode", "escalated_from", "method"]) {
865
+ const text = normalizeText(source[key] || "");
866
+ if (text) compact[key] = text.slice(0, 200);
761
867
  }
762
- if (source.candidate_list) {
763
- const candidateList = plainRecord(source.candidate_list);
764
- compact.candidate_list = {};
765
- for (const key of ["seen_count", "queued_count", "processed_count", "read_error_count", "scroll_count"]) {
766
- if (Number.isInteger(candidateList[key]) && candidateList[key] >= 0) {
767
- compact.candidate_list[key] = candidateList[key];
768
- }
769
- }
868
+ const at = compactCdpMethodTimestamp(source.at || "");
869
+ if (at) compact.at = at;
870
+ if (typeof source.ok === "boolean") compact.ok = source.ok;
871
+ if (Number.isInteger(source.card_count) && source.card_count >= 0) {
872
+ compact.card_count = source.card_count;
873
+ }
874
+ const error = compactListReadStaleDiagnosticForStatus(source.error);
875
+ if (error) compact.error = error;
876
+ return Object.keys(compact).length ? compact : null;
877
+ }
878
+
879
+ function compactDomStaleForensicForStatus(value) {
880
+ const source = plainRecord(value);
881
+ if (!Object.keys(source).length) return null;
882
+ const compact = {};
883
+ if (Number.isInteger(source.schema_version) && source.schema_version >= 0) {
884
+ compact.schema_version = source.schema_version;
885
+ }
886
+ for (const key of ["event_id", "event_type", "phase", "operation", "detail_step"]) {
887
+ const text = normalizeText(source[key] || "");
888
+ if (text) compact[key] = text.slice(0, key === "event_id" ? 300 : 200);
770
889
  }
890
+ const at = compactCdpMethodTimestamp(source.at || "");
891
+ if (at) compact.at = at;
892
+
893
+ const candidate = compactDomStaleCandidateForStatus(source.candidate);
894
+ if (candidate) compact.candidate = candidate;
895
+ const error = compactListReadStaleDiagnosticForStatus(source.error);
896
+ if (error) compact.error = error;
897
+ const preRecoveryRoots = compactDomStaleRootIdentityForStatus(source.pre_recovery_roots);
898
+ if (preRecoveryRoots) compact.pre_recovery_roots = preRecoveryRoots;
899
+ const postRecoveryRoots = compactDomStaleRootIdentityForStatus(source.post_recovery_roots);
900
+ if (postRecoveryRoots) compact.post_recovery_roots = postRecoveryRoots;
901
+ const candidateList = compactDomStaleNumericRecordForStatus(source.candidate_list);
902
+ if (candidateList) compact.candidate_list = candidateList;
903
+ const counters = compactDomStaleNumericRecordForStatus(source.counters);
904
+ if (counters) compact.counters = counters;
905
+ const lifecycleTimeline = compactDomStaleLifecycleTimelineForStatus(source.lifecycle_timeline);
906
+ if (lifecycleTimeline.length) compact.lifecycle_timeline = lifecycleTimeline;
907
+ const recovery = compactDomStaleRecoveryForStatus(source.recovery);
908
+ if (recovery) compact.recovery = recovery;
771
909
  return Object.keys(compact).length ? compact : null;
772
910
  }
911
+
912
+ function compactDomStaleForensicsForStatus(value) {
913
+ if (!Array.isArray(value)) return [];
914
+ return value
915
+ .map((item) => compactDomStaleForensicForStatus(item))
916
+ .filter(Boolean)
917
+ .slice(-12);
918
+ }
919
+
920
+ function compactListReadStaleDiagnosticsForStatus(value) {
921
+ if (!Array.isArray(value)) return [];
922
+ return value
923
+ .map((item) => compactListReadStaleDiagnosticForStatus(item))
924
+ .filter(Boolean)
925
+ .slice(-12);
926
+ }
927
+
928
+ function compactListReadStaleCheckpointEvent(value) {
929
+ const source = plainRecord(value);
930
+ if (!Object.keys(source).length) return null;
931
+ const compact = {};
932
+ const diagnostic = compactListReadStaleDiagnosticForStatus(source.diagnostic || source.trigger);
933
+ if (diagnostic) compact.diagnostic = diagnostic;
934
+ const recentDiagnostics = compactListReadStaleDiagnosticsForStatus(source.recent_diagnostics);
935
+ if (recentDiagnostics.length) compact.recent_diagnostics = recentDiagnostics;
936
+ for (const key of ["recovery_count", "recovery_applied_count"]) {
937
+ if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
938
+ }
939
+ if (source.recovery_error) {
940
+ const recoveryError = compactListReadStaleDiagnosticForStatus(source.recovery_error);
941
+ if (recoveryError) compact.recovery_error = recoveryError;
942
+ }
943
+ if (source.candidate_list) {
944
+ const candidateList = plainRecord(source.candidate_list);
945
+ compact.candidate_list = {};
946
+ for (const key of ["seen_count", "queued_count", "processed_count", "read_error_count", "scroll_count"]) {
947
+ if (Number.isInteger(candidateList[key]) && candidateList[key] >= 0) {
948
+ compact.candidate_list[key] = candidateList[key];
949
+ }
950
+ }
951
+ }
952
+ return Object.keys(compact).length ? compact : null;
953
+ }
773
954
 
774
955
  function compactRecommendSummaryForStatus(summary) {
775
956
  if (!summary || typeof summary !== "object" || Array.isArray(summary)) return null;
@@ -817,7 +998,7 @@ function compactRecommendSummaryForStatus(summary) {
817
998
  restLevel: normalizeText(summary.human_behavior.restLevel || summary.human_behavior.rest_level || "")
818
999
  };
819
1000
  }
820
- if (summary.human_rest) {
1001
+ if (summary.human_rest) {
821
1002
  const humanRest = plainRecord(summary.human_rest);
822
1003
  compact.human_rest = {
823
1004
  enabled: humanRest.enabled === true,
@@ -825,23 +1006,27 @@ function compactRecommendSummaryForStatus(summary) {
825
1006
  rest_count: compactPositiveInteger(humanRest.rest_count ?? humanRest.count, null),
826
1007
  total_pause_ms: compactPositiveInteger(humanRest.total_pause_ms ?? humanRest.pause_ms, null)
827
1008
  };
828
- }
829
- for (const key of [
830
- "list_read_stale_recovery_attempts",
831
- "list_read_stale_recovery_applied",
832
- "list_read_stale_recoveries"
833
- ]) {
834
- if (Number.isInteger(summary[key]) && summary[key] >= 0) compact[key] = summary[key];
835
- }
836
- if (summary.last_list_read_recovery_mode) {
837
- compact.last_list_read_recovery_mode = normalizeText(summary.last_list_read_recovery_mode).slice(0, 100);
838
- }
839
- const lastStaleDiagnostic = compactListReadStaleDiagnosticForStatus(
840
- summary.last_list_read_stale_diagnostic
841
- );
842
- if (lastStaleDiagnostic) compact.last_list_read_stale_diagnostic = lastStaleDiagnostic;
1009
+ }
1010
+ for (const key of [
1011
+ "list_read_stale_recovery_attempts",
1012
+ "list_read_stale_recovery_applied",
1013
+ "list_read_stale_recoveries"
1014
+ ]) {
1015
+ if (Number.isInteger(summary[key]) && summary[key] >= 0) compact[key] = summary[key];
1016
+ }
1017
+ if (summary.last_list_read_recovery_mode) {
1018
+ compact.last_list_read_recovery_mode = normalizeText(summary.last_list_read_recovery_mode).slice(0, 100);
1019
+ }
1020
+ const lastStaleDiagnostic = compactListReadStaleDiagnosticForStatus(
1021
+ summary.last_list_read_stale_diagnostic
1022
+ );
1023
+ if (lastStaleDiagnostic) compact.last_list_read_stale_diagnostic = lastStaleDiagnostic;
843
1024
  const staleDiagnostics = compactListReadStaleDiagnosticsForStatus(summary.list_read_stale_diagnostics);
844
1025
  if (staleDiagnostics.length) compact.list_read_stale_diagnostics = staleDiagnostics;
1026
+ const domStaleForensic = compactDomStaleForensicForStatus(summary.dom_stale_forensic);
1027
+ if (domStaleForensic) compact.dom_stale_forensic = domStaleForensic;
1028
+ const domStaleForensics = compactDomStaleForensicsForStatus(summary.dom_stale_forensics);
1029
+ if (domStaleForensics.length) compact.dom_stale_forensics = domStaleForensics;
845
1030
  return compact;
846
1031
  }
847
1032
 
@@ -856,19 +1041,23 @@ function compactRecommendCheckpointForStatus(checkpoint) {
856
1041
  } else if (Number.isInteger(checkpoint.results_count)) {
857
1042
  compact.results_count = checkpoint.results_count;
858
1043
  }
859
- for (const key of STATUS_COUNT_KEYS) {
860
- if (Number.isInteger(checkpoint[key])) compact[key] = checkpoint[key];
861
- }
1044
+ for (const key of STATUS_COUNT_KEYS) {
1045
+ if (Number.isInteger(checkpoint[key])) compact[key] = checkpoint[key];
1046
+ }
862
1047
  for (const key of [
863
- "list_read_stale_recovery",
864
- "list_read_stale_recovery_applied",
865
- "list_read_stale_recovered",
866
- "list_read_stale_recovery_exhausted",
867
- "list_read_stale_recovery_failed"
868
- ]) {
1048
+ "list_read_stale_recovery",
1049
+ "list_read_stale_recovery_applied",
1050
+ "list_read_stale_recovered",
1051
+ "list_read_stale_recovery_exhausted",
1052
+ "list_read_stale_recovery_failed"
1053
+ ]) {
869
1054
  const event = compactListReadStaleCheckpointEvent(checkpoint[key]);
870
1055
  if (event) compact[key] = event;
871
1056
  }
1057
+ const domStaleForensic = compactDomStaleForensicForStatus(checkpoint.dom_stale_forensic);
1058
+ if (domStaleForensic) compact.dom_stale_forensic = domStaleForensic;
1059
+ const domStaleForensics = compactDomStaleForensicsForStatus(checkpoint.dom_stale_forensics);
1060
+ if (domStaleForensics.length) compact.dom_stale_forensics = domStaleForensics;
872
1061
  return compact;
873
1062
  }
874
1063
 
@@ -1268,16 +1457,16 @@ function persistRecommendRunSnapshot(snapshot, {
1268
1457
  const artifacts = getRecommendRunArtifacts(normalized.run_id);
1269
1458
  if (!artifacts) return normalized;
1270
1459
  const existing = readJsonFile(artifacts.run_state_path);
1271
- normalized.control = mergePersistedControlRequest(normalized, existing);
1272
- normalized = coerceCanceledTerminalSnapshot(normalized, existing);
1273
- if (persistActiveCheckpoint) {
1274
- persistRecommendCheckpointSnapshot(snapshot);
1275
- }
1276
- const cdpEvidence = buildPersistedCdpEvidence({
1277
- meta: getRecommendRunMeta(normalized.run_id),
1278
- persisted: existing || {}
1279
- });
1280
- const payload = {
1460
+ normalized.control = mergePersistedControlRequest(normalized, existing);
1461
+ normalized = coerceCanceledTerminalSnapshot(normalized, existing);
1462
+ if (persistActiveCheckpoint) {
1463
+ persistRecommendCheckpointSnapshot(snapshot);
1464
+ }
1465
+ const cdpEvidence = buildPersistedCdpEvidence({
1466
+ meta: getRecommendRunMeta(normalized.run_id),
1467
+ persisted: existing || {}
1468
+ });
1469
+ const payload = {
1281
1470
  run_id: normalized.run_id,
1282
1471
  mode: normalized.mode,
1283
1472
  state: normalized.state,
@@ -1293,14 +1482,14 @@ function persistRecommendRunSnapshot(snapshot, {
1293
1482
  context: normalized.context,
1294
1483
  control: normalized.control,
1295
1484
  resume: normalized.resume,
1296
- error: normalized.error,
1297
- recovery: normalized.recovery,
1298
- result: normalized.result,
1299
- summary: normalized.summary,
1300
- checkpoint: normalized.checkpoint,
1301
- artifacts: normalized.artifacts,
1302
- ...cdpEvidence
1303
- };
1485
+ error: normalized.error,
1486
+ recovery: normalized.recovery,
1487
+ result: normalized.result,
1488
+ summary: normalized.summary,
1489
+ checkpoint: normalized.checkpoint,
1490
+ artifacts: normalized.artifacts,
1491
+ ...cdpEvidence
1492
+ };
1304
1493
  writeJsonAtomic(artifacts.run_state_path, payload);
1305
1494
  return normalized;
1306
1495
  }
@@ -1373,18 +1562,18 @@ function persistRecommendLifecycleSnapshot(snapshot, event = {}) {
1373
1562
  });
1374
1563
  }
1375
1564
 
1376
- function attachMethodEvidence(payload, runId) {
1377
- const meta = getRecommendRunMeta(runId);
1378
- const methodLog = meta.methodLog || [];
1379
- assertNoForbiddenCdpCalls(methodLog);
1380
- return {
1381
- ...payload,
1382
- ...buildPersistedCdpEvidence({
1383
- meta,
1384
- persisted: readRecommendRunState(runId) || {}
1385
- })
1386
- };
1387
- }
1565
+ function attachMethodEvidence(payload, runId) {
1566
+ const meta = getRecommendRunMeta(runId);
1567
+ const methodLog = meta.methodLog || [];
1568
+ assertNoForbiddenCdpCalls(methodLog);
1569
+ return {
1570
+ ...payload,
1571
+ ...buildPersistedCdpEvidence({
1572
+ meta,
1573
+ persisted: readRecommendRunState(runId) || {}
1574
+ })
1575
+ };
1576
+ }
1388
1577
 
1389
1578
  function compactRecommendJobListOption(option, index) {
1390
1579
  const label = normalizeText(option?.label);
@@ -1935,13 +2124,13 @@ function buildRecommendFilter(parsed, args = {}) {
1935
2124
  return { enabled: false };
1936
2125
  }
1937
2126
 
1938
- const groups = [{
1939
- group: "activity",
1940
- labels: [parsed.searchParams?.activity_level || "不限"],
1941
- selectAllLabels: false,
1942
- allowUnlimited: true,
1943
- verifySticky: true
1944
- }];
2127
+ const groups = [{
2128
+ group: "activity",
2129
+ labels: [parsed.searchParams?.activity_level || "不限"],
2130
+ selectAllLabels: false,
2131
+ allowUnlimited: true,
2132
+ verifySticky: true
2133
+ }];
1945
2134
  const recentNotView = withoutUnlimited(parsed.searchParams?.recent_not_view);
1946
2135
  if (recentNotView.length) {
1947
2136
  groups.push({
@@ -1978,12 +2167,12 @@ function buildRecommendFilter(parsed, args = {}) {
1978
2167
  });
1979
2168
  }
1980
2169
 
1981
- return {
1982
- enabled: true,
1983
- currentCityOnly: parsed.searchParams?.current_city_only === true,
1984
- filterGroups: groups
1985
- };
1986
- }
2170
+ return {
2171
+ enabled: true,
2172
+ currentCityOnly: parsed.searchParams?.current_city_only === true,
2173
+ filterGroups: groups
2174
+ };
2175
+ }
1987
2176
 
1988
2177
  function normalizeRecommendStartInput(args = {}, parsed, configResolution = null) {
1989
2178
  const confirmation = args.confirmation || {};
@@ -2071,23 +2260,23 @@ function getRunOptions(args, parsed, normalized, session, configResolution = nul
2071
2260
  ) || "low",
2072
2261
  imageOutputDir: resolveBossConfiguredOutputDir("", getRunsDir()),
2073
2262
  humanRestEnabled: humanBehavior.restEnabled,
2074
- humanBehavior,
2075
- skipRecentColleagueContacted: normalized.skipRecentColleagueContacted,
2076
- colleagueContactWindowDays: normalized.colleagueContactWindowDays,
2077
- debugTestMode: isDebugTestMode(args),
2078
- debugForceListEndAfterProcessed: parsePositiveInteger(
2079
- args.debug_force_list_end_after_processed,
2080
- null
2081
- ),
2082
- debugForceContextRecoveryAfterProcessed: parsePositiveInteger(
2083
- args.debug_force_context_recovery_after_processed,
2084
- null
2085
- ),
2086
- debugForceCdpReconnectAfterProcessed: parsePositiveInteger(
2087
- args.debug_force_cdp_reconnect_after_processed,
2088
- null
2089
- ),
2090
- name: "mcp-recommend-pipeline-run",
2263
+ humanBehavior,
2264
+ skipRecentColleagueContacted: normalized.skipRecentColleagueContacted,
2265
+ colleagueContactWindowDays: normalized.colleagueContactWindowDays,
2266
+ debugTestMode: isDebugTestMode(args),
2267
+ debugForceListEndAfterProcessed: parsePositiveInteger(
2268
+ args.debug_force_list_end_after_processed,
2269
+ null
2270
+ ),
2271
+ debugForceContextRecoveryAfterProcessed: parsePositiveInteger(
2272
+ args.debug_force_context_recovery_after_processed,
2273
+ null
2274
+ ),
2275
+ debugForceCdpReconnectAfterProcessed: parsePositiveInteger(
2276
+ args.debug_force_cdp_reconnect_after_processed,
2277
+ null
2278
+ ),
2279
+ name: "mcp-recommend-pipeline-run",
2091
2280
  parsed
2092
2281
  };
2093
2282
  }
@@ -2379,19 +2568,19 @@ export function getRecommendPipelineRunTool({ args = {} } = {}) {
2379
2568
  run: compactRecommendRunForStatus(normalizedRun)
2380
2569
  }, runId);
2381
2570
  } catch {
2382
- const persisted = readRecommendRunState(runId);
2383
- if (persisted) {
2384
- const reconciled = compactRecommendRunForStatus(reconcilePersistedRecommendRunIfNeeded(persisted));
2385
- const persistedEvidence = readRecommendRunState(runId) || persisted;
2386
- return attachPersistedCdpEvidence({
2387
- status: "RUN_STATUS",
2388
- run: reconciled,
2389
- persistence: {
2390
- source: "disk",
2391
- active_control_available: false,
2392
- stale_process_reconciled: reconciled?.state !== persisted.state
2393
- }
2394
- }, persistedEvidence);
2571
+ const persisted = readRecommendRunState(runId);
2572
+ if (persisted) {
2573
+ const reconciled = compactRecommendRunForStatus(reconcilePersistedRecommendRunIfNeeded(persisted));
2574
+ const persistedEvidence = readRecommendRunState(runId) || persisted;
2575
+ return attachPersistedCdpEvidence({
2576
+ status: "RUN_STATUS",
2577
+ run: reconciled,
2578
+ persistence: {
2579
+ source: "disk",
2580
+ active_control_available: false,
2581
+ stale_process_reconciled: reconciled?.state !== persisted.state
2582
+ }
2583
+ }, persistedEvidence);
2395
2584
  }
2396
2585
  return {
2397
2586
  status: "FAILED",
@@ -2432,13 +2621,13 @@ export function pauseRecommendPipelineRunTool({ args = {} } = {}) {
2432
2621
  message: "暂停请求已接收,将在当前候选人处理完成后进入 paused。"
2433
2622
  }, runId);
2434
2623
  } catch {
2435
- const persisted = readRecommendRunState(runId);
2436
- if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
2437
- return attachPersistedCdpEvidence({
2438
- status: "PAUSE_IGNORED",
2439
- run: compactRecommendRunForStatus(persisted),
2440
- message: "目标任务已结束,无需暂停。"
2441
- }, persisted);
2624
+ const persisted = readRecommendRunState(runId);
2625
+ if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
2626
+ return attachPersistedCdpEvidence({
2627
+ status: "PAUSE_IGNORED",
2628
+ run: compactRecommendRunForStatus(persisted),
2629
+ message: "目标任务已结束,无需暂停。"
2630
+ }, persisted);
2442
2631
  }
2443
2632
  return getRecommendPipelineRunTool({ args });
2444
2633
  }
@@ -2486,11 +2675,11 @@ export function resumeRecommendPipelineRunTool({ args = {} } = {}) {
2486
2675
  message: "已恢复 Recommend run,请使用 get_recommend_pipeline_run 按需轮询。"
2487
2676
  }, runId);
2488
2677
  } catch {
2489
- const persisted = readRecommendRunState(runId);
2490
- if (persisted) {
2491
- return attachPersistedCdpEvidence({
2492
- status: "FAILED",
2493
- error: {
2678
+ const persisted = readRecommendRunState(runId);
2679
+ if (persisted) {
2680
+ return attachPersistedCdpEvidence({
2681
+ status: "FAILED",
2682
+ error: {
2494
2683
  code: TERMINAL_STATUSES.has(persisted.state) ? "RUN_ALREADY_TERMINATED" : "RUN_NOT_ACTIVE",
2495
2684
  message: TERMINAL_STATUSES.has(persisted.state)
2496
2685
  ? "目标任务已结束,无法继续。"
@@ -2498,11 +2687,11 @@ export function resumeRecommendPipelineRunTool({ args = {} } = {}) {
2498
2687
  retryable: !TERMINAL_STATUSES.has(persisted.state)
2499
2688
  },
2500
2689
  run: compactRecommendRunForStatus(persisted),
2501
- persistence: {
2502
- source: "disk",
2503
- active_control_available: false
2504
- }
2505
- }, persisted);
2690
+ persistence: {
2691
+ source: "disk",
2692
+ active_control_available: false
2693
+ }
2694
+ }, persisted);
2506
2695
  }
2507
2696
  return getRecommendPipelineRunTool({ args });
2508
2697
  }
@@ -2528,13 +2717,13 @@ export function cancelRecommendPipelineRunTool({ args = {} } = {}) {
2528
2717
  message: "已收到取消请求,将在当前候选人处理完成后安全停止。"
2529
2718
  }, runId);
2530
2719
  } catch {
2531
- const persisted = readRecommendRunState(runId);
2532
- if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
2533
- return attachPersistedCdpEvidence({
2534
- status: "CANCEL_IGNORED",
2535
- run: compactRecommendRunForStatus(persisted),
2536
- message: "目标任务已结束,无需取消。"
2537
- }, persisted);
2720
+ const persisted = readRecommendRunState(runId);
2721
+ if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
2722
+ return attachPersistedCdpEvidence({
2723
+ status: "CANCEL_IGNORED",
2724
+ run: compactRecommendRunForStatus(persisted),
2725
+ message: "目标任务已结束,无需取消。"
2726
+ }, persisted);
2538
2727
  }
2539
2728
  const cancelMessage = "已收到取消请求,将由 detached worker 在下一个安全边界停止。";
2540
2729
  const patched = patchPersistedRecommendRunControl(runId, {
@@ -2544,18 +2733,18 @@ export function cancelRecommendPipelineRunTool({ args = {} } = {}) {
2544
2733
  cancel_requested: true
2545
2734
  }, {
2546
2735
  message: cancelMessage
2547
- });
2548
- if (patched) {
2549
- return attachPersistedCdpEvidence({
2550
- status: "CANCEL_REQUESTED",
2551
- run: compactRecommendRunForStatus(patched),
2736
+ });
2737
+ if (patched) {
2738
+ return attachPersistedCdpEvidence({
2739
+ status: "CANCEL_REQUESTED",
2740
+ run: compactRecommendRunForStatus(patched),
2552
2741
  message: cancelMessage,
2553
2742
  persistence: {
2554
2743
  source: "disk",
2555
- active_control_available: false,
2556
- detached_control_requested: true
2557
- }
2558
- }, patched);
2744
+ active_control_available: false,
2745
+ detached_control_requested: true
2746
+ }
2747
+ }, patched);
2559
2748
  }
2560
2749
  return getRecommendPipelineRunTool({ args });
2561
2750
  }