@reconcrap/boss-recommend-mcp 2.1.22 → 2.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -8
- package/src/core/browser/index.js +56 -29
- package/src/core/infinite-list/index.js +31 -22
- package/src/core/run/index.js +22 -5
- package/src/domains/recommend/detail.js +13 -4
- package/src/domains/recommend/refresh.js +95 -14
- package/src/domains/recommend/run-service.js +712 -107
- package/src/index.js +108 -4
- package/src/recommend-mcp.js +392 -146
- package/src/recommend-scheduler.js +75 -9
package/src/recommend-mcp.js
CHANGED
|
@@ -1,18 +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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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";
|
|
16
17
|
import {
|
|
17
18
|
RUN_STATUS_CANCELING,
|
|
18
19
|
RUN_STATUS_CANCELED,
|
|
@@ -277,13 +278,22 @@ function normalizeScreeningModeArg(args = {}) {
|
|
|
277
278
|
: "llm";
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
function collectRecommendDebugTestOptions(args = {}, normalized = {}) {
|
|
281
|
-
const reasons = [];
|
|
281
|
+
function collectRecommendDebugTestOptions(args = {}, normalized = {}) {
|
|
282
|
+
const reasons = [];
|
|
282
283
|
if (normalizeScreeningModeArg(args) === "deterministic") reasons.push("deterministic_screening");
|
|
283
284
|
if (args.allow_card_only_screening === true) reasons.push("allow_card_only_screening");
|
|
284
285
|
if (parseNonNegativeInteger(args.detail_limit, null) === 0) reasons.push("detail_limit=0");
|
|
285
286
|
if (args.no_filter === true) reasons.push("no_filter");
|
|
286
|
-
if (args.filter_enabled === false) reasons.push("filter_enabled=false");
|
|
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
297
|
if (args.dry_run_post_action === true) reasons.push("dry_run_post_action");
|
|
288
298
|
if (args.execute_post_action === false && normalized.postAction && normalized.postAction !== "none") {
|
|
289
299
|
reasons.push("execute_post_action=false");
|
|
@@ -303,21 +313,39 @@ function resolveRecommendDetailLimit(args = {}, normalized = {}) {
|
|
|
303
313
|
return requested;
|
|
304
314
|
}
|
|
305
315
|
|
|
306
|
-
function methodSummary(methodLog = []) {
|
|
307
|
-
const summary = {};
|
|
308
|
-
for (const entry of methodLog || []) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
+
function compactMethodLogForStatus(methodLog = []) {
|
|
340
|
+
if (!Array.isArray(methodLog)) return [];
|
|
341
|
+
return methodLog
|
|
342
|
+
.map((entry) => ({
|
|
343
|
+
method: compactCdpMethodName(entry?.method || entry || ""),
|
|
344
|
+
at: compactCdpMethodTimestamp(entry?.at || "")
|
|
345
|
+
}))
|
|
346
|
+
.filter((entry) => Boolean(entry.method))
|
|
347
|
+
.slice(-STATUS_METHOD_LOG_TAIL_LIMIT);
|
|
348
|
+
}
|
|
321
349
|
|
|
322
350
|
function clonePlain(value, fallback = null) {
|
|
323
351
|
try {
|
|
@@ -331,10 +359,130 @@ function plainRecord(value) {
|
|
|
331
359
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
332
360
|
}
|
|
333
361
|
|
|
334
|
-
function nonEmptyRecord(value) {
|
|
335
|
-
const record = plainRecord(value);
|
|
336
|
-
return Object.keys(record).length ? record : null;
|
|
337
|
-
}
|
|
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
|
+
}
|
|
338
486
|
|
|
339
487
|
function normalizeRunId(runId) {
|
|
340
488
|
const normalized = normalizeText(runId);
|
|
@@ -506,17 +654,28 @@ function normalizeLegacyProgress(progress = {}, summary = null) {
|
|
|
506
654
|
: Number.isInteger(summary?.passed)
|
|
507
655
|
? summary.passed
|
|
508
656
|
: 0;
|
|
509
|
-
|
|
510
|
-
...progress,
|
|
657
|
+
const normalized = {
|
|
658
|
+
...progress,
|
|
511
659
|
processed,
|
|
512
660
|
inspected: processed,
|
|
513
661
|
screened,
|
|
514
662
|
passed,
|
|
515
663
|
skipped: Number.isInteger(progress.skipped) ? progress.skipped : Math.max(processed - passed, 0),
|
|
516
|
-
greet_count: Number.isInteger(progress.greet_count) ? progress.greet_count : 0,
|
|
517
|
-
post_action_clicked: Number.isInteger(progress.post_action_clicked) ? progress.post_action_clicked : 0
|
|
518
|
-
};
|
|
519
|
-
|
|
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
|
+
if (Object.prototype.hasOwnProperty.call(normalized, "list_read_stale_diagnostics")) {
|
|
673
|
+
normalized.list_read_stale_diagnostics = compactListReadStaleDiagnosticsForStatus(
|
|
674
|
+
normalized.list_read_stale_diagnostics
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
return normalized;
|
|
678
|
+
}
|
|
520
679
|
|
|
521
680
|
const STATUS_COUNT_KEYS = [
|
|
522
681
|
"processed",
|
|
@@ -544,11 +703,73 @@ function compactPositiveInteger(value, fallback = null) {
|
|
|
544
703
|
return Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
545
704
|
}
|
|
546
705
|
|
|
547
|
-
function compactSmallRecord(value, fallback = null) {
|
|
706
|
+
function compactSmallRecord(value, fallback = null) {
|
|
548
707
|
const record = plainRecord(value);
|
|
549
708
|
if (!Object.keys(record).length) return fallback;
|
|
550
|
-
return clonePlain(record, fallback);
|
|
551
|
-
}
|
|
709
|
+
return clonePlain(record, fallback);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function compactListReadStaleDiagnosticForStatus(value) {
|
|
713
|
+
const source = plainRecord(value);
|
|
714
|
+
if (!Object.keys(source).length) return null;
|
|
715
|
+
const compact = {};
|
|
716
|
+
for (const key of ["code", "message", "phase", "cdp_method", "cdp_at", "recovery_mode"]) {
|
|
717
|
+
const text = normalizeText(source[key] || "");
|
|
718
|
+
if (text) compact[key] = text.slice(0, key === "message" ? 500 : 200);
|
|
719
|
+
}
|
|
720
|
+
for (const key of ["cdp_node_id", "cdp_backend_node_id", "attempt"]) {
|
|
721
|
+
if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
|
|
722
|
+
}
|
|
723
|
+
for (const key of ["exhausted", "recovery_applied", "recovered"]) {
|
|
724
|
+
if (typeof source[key] === "boolean") compact[key] = source[key];
|
|
725
|
+
}
|
|
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
|
+
}
|
|
730
|
+
if (Array.isArray(source.cdp_param_keys)) {
|
|
731
|
+
compact.cdp_param_keys = source.cdp_param_keys
|
|
732
|
+
.map((key) => normalizeText(key))
|
|
733
|
+
.filter((key) => /^[A-Za-z][A-Za-z0-9_]*$/.test(key))
|
|
734
|
+
.slice(0, 20);
|
|
735
|
+
}
|
|
736
|
+
return Object.keys(compact).length ? compact : null;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function compactListReadStaleDiagnosticsForStatus(value) {
|
|
740
|
+
if (!Array.isArray(value)) return [];
|
|
741
|
+
return value
|
|
742
|
+
.map((item) => compactListReadStaleDiagnosticForStatus(item))
|
|
743
|
+
.filter(Boolean)
|
|
744
|
+
.slice(-12);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function compactListReadStaleCheckpointEvent(value) {
|
|
748
|
+
const source = plainRecord(value);
|
|
749
|
+
if (!Object.keys(source).length) return null;
|
|
750
|
+
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"]) {
|
|
756
|
+
if (Number.isInteger(source[key]) && source[key] >= 0) compact[key] = source[key];
|
|
757
|
+
}
|
|
758
|
+
if (source.recovery_error) {
|
|
759
|
+
const recoveryError = compactListReadStaleDiagnosticForStatus(source.recovery_error);
|
|
760
|
+
if (recoveryError) compact.recovery_error = recoveryError;
|
|
761
|
+
}
|
|
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
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return Object.keys(compact).length ? compact : null;
|
|
772
|
+
}
|
|
552
773
|
|
|
553
774
|
function compactRecommendSummaryForStatus(summary) {
|
|
554
775
|
if (!summary || typeof summary !== "object" || Array.isArray(summary)) return null;
|
|
@@ -596,7 +817,7 @@ function compactRecommendSummaryForStatus(summary) {
|
|
|
596
817
|
restLevel: normalizeText(summary.human_behavior.restLevel || summary.human_behavior.rest_level || "")
|
|
597
818
|
};
|
|
598
819
|
}
|
|
599
|
-
if (summary.human_rest) {
|
|
820
|
+
if (summary.human_rest) {
|
|
600
821
|
const humanRest = plainRecord(summary.human_rest);
|
|
601
822
|
compact.human_rest = {
|
|
602
823
|
enabled: humanRest.enabled === true,
|
|
@@ -604,9 +825,25 @@ function compactRecommendSummaryForStatus(summary) {
|
|
|
604
825
|
rest_count: compactPositiveInteger(humanRest.rest_count ?? humanRest.count, null),
|
|
605
826
|
total_pause_ms: compactPositiveInteger(humanRest.total_pause_ms ?? humanRest.pause_ms, null)
|
|
606
827
|
};
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
|
|
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;
|
|
843
|
+
const staleDiagnostics = compactListReadStaleDiagnosticsForStatus(summary.list_read_stale_diagnostics);
|
|
844
|
+
if (staleDiagnostics.length) compact.list_read_stale_diagnostics = staleDiagnostics;
|
|
845
|
+
return compact;
|
|
846
|
+
}
|
|
610
847
|
|
|
611
848
|
function compactRecommendCheckpointForStatus(checkpoint) {
|
|
612
849
|
if (!checkpoint || typeof checkpoint !== "object" || Array.isArray(checkpoint)) return {};
|
|
@@ -619,11 +856,21 @@ function compactRecommendCheckpointForStatus(checkpoint) {
|
|
|
619
856
|
} else if (Number.isInteger(checkpoint.results_count)) {
|
|
620
857
|
compact.results_count = checkpoint.results_count;
|
|
621
858
|
}
|
|
622
|
-
for (const key of STATUS_COUNT_KEYS) {
|
|
623
|
-
if (Number.isInteger(checkpoint[key])) compact[key] = checkpoint[key];
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
|
|
859
|
+
for (const key of STATUS_COUNT_KEYS) {
|
|
860
|
+
if (Number.isInteger(checkpoint[key])) compact[key] = checkpoint[key];
|
|
861
|
+
}
|
|
862
|
+
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
|
+
]) {
|
|
869
|
+
const event = compactListReadStaleCheckpointEvent(checkpoint[key]);
|
|
870
|
+
if (event) compact[key] = event;
|
|
871
|
+
}
|
|
872
|
+
return compact;
|
|
873
|
+
}
|
|
627
874
|
|
|
628
875
|
function compactRecommendResultForStatus(result) {
|
|
629
876
|
if (!result || typeof result !== "object" || Array.isArray(result)) return result || null;
|
|
@@ -1021,12 +1268,16 @@ function persistRecommendRunSnapshot(snapshot, {
|
|
|
1021
1268
|
const artifacts = getRecommendRunArtifacts(normalized.run_id);
|
|
1022
1269
|
if (!artifacts) return normalized;
|
|
1023
1270
|
const existing = readJsonFile(artifacts.run_state_path);
|
|
1024
|
-
normalized.control = mergePersistedControlRequest(normalized, existing);
|
|
1025
|
-
normalized = coerceCanceledTerminalSnapshot(normalized, existing);
|
|
1026
|
-
if (persistActiveCheckpoint) {
|
|
1027
|
-
persistRecommendCheckpointSnapshot(snapshot);
|
|
1028
|
-
}
|
|
1029
|
-
const
|
|
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 = {
|
|
1030
1281
|
run_id: normalized.run_id,
|
|
1031
1282
|
mode: normalized.mode,
|
|
1032
1283
|
state: normalized.state,
|
|
@@ -1042,12 +1293,14 @@ function persistRecommendRunSnapshot(snapshot, {
|
|
|
1042
1293
|
context: normalized.context,
|
|
1043
1294
|
control: normalized.control,
|
|
1044
1295
|
resume: normalized.resume,
|
|
1045
|
-
error: normalized.error,
|
|
1046
|
-
recovery: normalized.recovery,
|
|
1047
|
-
result: normalized.result,
|
|
1048
|
-
summary: normalized.summary,
|
|
1049
|
-
|
|
1050
|
-
|
|
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
|
+
};
|
|
1051
1304
|
writeJsonAtomic(artifacts.run_state_path, payload);
|
|
1052
1305
|
return normalized;
|
|
1053
1306
|
}
|
|
@@ -1120,19 +1373,18 @@ function persistRecommendLifecycleSnapshot(snapshot, event = {}) {
|
|
|
1120
1373
|
});
|
|
1121
1374
|
}
|
|
1122
1375
|
|
|
1123
|
-
function attachMethodEvidence(payload, runId) {
|
|
1124
|
-
const meta = getRecommendRunMeta(runId);
|
|
1125
|
-
const methodLog = meta.methodLog || [];
|
|
1126
|
-
assertNoForbiddenCdpCalls(methodLog);
|
|
1127
|
-
return {
|
|
1128
|
-
...payload,
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
}
|
|
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
|
+
}
|
|
1136
1388
|
|
|
1137
1389
|
function compactRecommendJobListOption(option, index) {
|
|
1138
1390
|
const label = normalizeText(option?.label);
|
|
@@ -1819,10 +2071,23 @@ function getRunOptions(args, parsed, normalized, session, configResolution = nul
|
|
|
1819
2071
|
) || "low",
|
|
1820
2072
|
imageOutputDir: resolveBossConfiguredOutputDir("", getRunsDir()),
|
|
1821
2073
|
humanRestEnabled: humanBehavior.restEnabled,
|
|
1822
|
-
humanBehavior,
|
|
1823
|
-
skipRecentColleagueContacted: normalized.skipRecentColleagueContacted,
|
|
1824
|
-
colleagueContactWindowDays: normalized.colleagueContactWindowDays,
|
|
1825
|
-
|
|
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",
|
|
1826
2091
|
parsed
|
|
1827
2092
|
};
|
|
1828
2093
|
}
|
|
@@ -2114,22 +2379,19 @@ export function getRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2114
2379
|
run: compactRecommendRunForStatus(normalizedRun)
|
|
2115
2380
|
}, runId);
|
|
2116
2381
|
} catch {
|
|
2117
|
-
const persisted = readRecommendRunState(runId);
|
|
2118
|
-
if (persisted) {
|
|
2119
|
-
const reconciled = compactRecommendRunForStatus(reconcilePersistedRecommendRunIfNeeded(persisted));
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
method_log: [],
|
|
2131
|
-
chrome: null
|
|
2132
|
-
};
|
|
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);
|
|
2133
2395
|
}
|
|
2134
2396
|
return {
|
|
2135
2397
|
status: "FAILED",
|
|
@@ -2170,17 +2432,13 @@ export function pauseRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2170
2432
|
message: "暂停请求已接收,将在当前候选人处理完成后进入 paused。"
|
|
2171
2433
|
}, runId);
|
|
2172
2434
|
} catch {
|
|
2173
|
-
const persisted = readRecommendRunState(runId);
|
|
2174
|
-
if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
|
|
2175
|
-
return {
|
|
2176
|
-
status: "PAUSE_IGNORED",
|
|
2177
|
-
run: compactRecommendRunForStatus(persisted),
|
|
2178
|
-
message: "目标任务已结束,无需暂停。"
|
|
2179
|
-
|
|
2180
|
-
method_summary: {},
|
|
2181
|
-
method_log: [],
|
|
2182
|
-
chrome: null
|
|
2183
|
-
};
|
|
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);
|
|
2184
2442
|
}
|
|
2185
2443
|
return getRecommendPipelineRunTool({ args });
|
|
2186
2444
|
}
|
|
@@ -2228,11 +2486,11 @@ export function resumeRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2228
2486
|
message: "已恢复 Recommend run,请使用 get_recommend_pipeline_run 按需轮询。"
|
|
2229
2487
|
}, runId);
|
|
2230
2488
|
} catch {
|
|
2231
|
-
const persisted = readRecommendRunState(runId);
|
|
2232
|
-
if (persisted) {
|
|
2233
|
-
return {
|
|
2234
|
-
status: "FAILED",
|
|
2235
|
-
error: {
|
|
2489
|
+
const persisted = readRecommendRunState(runId);
|
|
2490
|
+
if (persisted) {
|
|
2491
|
+
return attachPersistedCdpEvidence({
|
|
2492
|
+
status: "FAILED",
|
|
2493
|
+
error: {
|
|
2236
2494
|
code: TERMINAL_STATUSES.has(persisted.state) ? "RUN_ALREADY_TERMINATED" : "RUN_NOT_ACTIVE",
|
|
2237
2495
|
message: TERMINAL_STATUSES.has(persisted.state)
|
|
2238
2496
|
? "目标任务已结束,无法继续。"
|
|
@@ -2240,15 +2498,11 @@ export function resumeRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2240
2498
|
retryable: !TERMINAL_STATUSES.has(persisted.state)
|
|
2241
2499
|
},
|
|
2242
2500
|
run: compactRecommendRunForStatus(persisted),
|
|
2243
|
-
persistence: {
|
|
2244
|
-
source: "disk",
|
|
2245
|
-
active_control_available: false
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
method_summary: {},
|
|
2249
|
-
method_log: [],
|
|
2250
|
-
chrome: null
|
|
2251
|
-
};
|
|
2501
|
+
persistence: {
|
|
2502
|
+
source: "disk",
|
|
2503
|
+
active_control_available: false
|
|
2504
|
+
}
|
|
2505
|
+
}, persisted);
|
|
2252
2506
|
}
|
|
2253
2507
|
return getRecommendPipelineRunTool({ args });
|
|
2254
2508
|
}
|
|
@@ -2274,17 +2528,13 @@ export function cancelRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2274
2528
|
message: "已收到取消请求,将在当前候选人处理完成后安全停止。"
|
|
2275
2529
|
}, runId);
|
|
2276
2530
|
} catch {
|
|
2277
|
-
const persisted = readRecommendRunState(runId);
|
|
2278
|
-
if (persisted && TERMINAL_STATUSES.has(persisted.state)) {
|
|
2279
|
-
return {
|
|
2280
|
-
status: "CANCEL_IGNORED",
|
|
2281
|
-
run: compactRecommendRunForStatus(persisted),
|
|
2282
|
-
message: "目标任务已结束,无需取消。"
|
|
2283
|
-
|
|
2284
|
-
method_summary: {},
|
|
2285
|
-
method_log: [],
|
|
2286
|
-
chrome: null
|
|
2287
|
-
};
|
|
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);
|
|
2288
2538
|
}
|
|
2289
2539
|
const cancelMessage = "已收到取消请求,将由 detached worker 在下一个安全边界停止。";
|
|
2290
2540
|
const patched = patchPersistedRecommendRunControl(runId, {
|
|
@@ -2294,22 +2544,18 @@ export function cancelRecommendPipelineRunTool({ args = {} } = {}) {
|
|
|
2294
2544
|
cancel_requested: true
|
|
2295
2545
|
}, {
|
|
2296
2546
|
message: cancelMessage
|
|
2297
|
-
});
|
|
2298
|
-
if (patched) {
|
|
2299
|
-
return {
|
|
2300
|
-
status: "CANCEL_REQUESTED",
|
|
2301
|
-
run: compactRecommendRunForStatus(patched),
|
|
2547
|
+
});
|
|
2548
|
+
if (patched) {
|
|
2549
|
+
return attachPersistedCdpEvidence({
|
|
2550
|
+
status: "CANCEL_REQUESTED",
|
|
2551
|
+
run: compactRecommendRunForStatus(patched),
|
|
2302
2552
|
message: cancelMessage,
|
|
2303
2553
|
persistence: {
|
|
2304
2554
|
source: "disk",
|
|
2305
|
-
active_control_available: false,
|
|
2306
|
-
detached_control_requested: true
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
method_summary: {},
|
|
2310
|
-
method_log: [],
|
|
2311
|
-
chrome: null
|
|
2312
|
-
};
|
|
2555
|
+
active_control_available: false,
|
|
2556
|
+
detached_control_requested: true
|
|
2557
|
+
}
|
|
2558
|
+
}, patched);
|
|
2313
2559
|
}
|
|
2314
2560
|
return getRecommendPipelineRunTool({ args });
|
|
2315
2561
|
}
|