@reconcrap/boss-recommend-mcp 2.0.29 → 2.0.30
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 +1 -1
- package/src/domains/recommend/run-service.js +171 -129
- package/src/recommend-mcp.js +65 -0
package/package.json
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
closeRecommendDetail,
|
|
42
42
|
createRecommendDetailNetworkRecorder,
|
|
43
43
|
extractRecommendDetailCandidate,
|
|
44
|
+
isStaleRecommendNodeError,
|
|
44
45
|
openRecommendCardDetailWithFreshRetry,
|
|
45
46
|
waitForRecommendDetailNetworkEvents
|
|
46
47
|
} from "./detail.js";
|
|
@@ -381,6 +382,7 @@ function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
|
381
382
|
export function isRecoverableImageCaptureError(error) {
|
|
382
383
|
const code = String(error?.code || "");
|
|
383
384
|
if (code === "IMAGE_CAPTURE_TIMEOUT" || code === "IMAGE_CAPTURE_TOTAL_TIMEOUT") return true;
|
|
385
|
+
if (isStaleRecommendNodeError(error)) return true;
|
|
384
386
|
return /Image fallback capture timed out/i.test(String(error?.message || error || ""));
|
|
385
387
|
}
|
|
386
388
|
|
|
@@ -420,7 +422,7 @@ export function createRecoverableImageCaptureEvidence(error, {
|
|
|
420
422
|
llm_total_byte_length: 0,
|
|
421
423
|
llm_original_total_byte_length: 0,
|
|
422
424
|
llm_composition_error: null,
|
|
423
|
-
error_code: error?.code || "IMAGE_CAPTURE_FAILED",
|
|
425
|
+
error_code: error?.code || (isStaleRecommendNodeError(error) ? "IMAGE_CAPTURE_STALE_NODE" : "IMAGE_CAPTURE_FAILED"),
|
|
424
426
|
error: error?.message || String(error || "Image capture failed"),
|
|
425
427
|
file_paths: filePaths,
|
|
426
428
|
llm_file_paths: []
|
|
@@ -438,6 +440,27 @@ function createImageCaptureFailureScreening(candidate, error) {
|
|
|
438
440
|
};
|
|
439
441
|
}
|
|
440
442
|
|
|
443
|
+
export function isRecoverableRecommendDetailError(error) {
|
|
444
|
+
return isStaleRecommendNodeError(error);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function compactRecoverableDetailError(error) {
|
|
448
|
+
return compactError(error, isStaleRecommendNodeError(error) ? "DETAIL_STALE_NODE" : "DETAIL_OPEN_FAILED");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function createRecoverableDetailFailureScreening(candidate, error) {
|
|
452
|
+
return {
|
|
453
|
+
status: "fail",
|
|
454
|
+
passed: false,
|
|
455
|
+
score: 0,
|
|
456
|
+
reasons: isStaleRecommendNodeError(error)
|
|
457
|
+
? ["detail_open_failed", "stale_node"]
|
|
458
|
+
: ["detail_open_failed"],
|
|
459
|
+
error: compactRecoverableDetailError(error),
|
|
460
|
+
candidate
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
441
464
|
export async function runRecommendWorkflow({
|
|
442
465
|
client,
|
|
443
466
|
targetUrl = "",
|
|
@@ -746,139 +769,149 @@ export async function runRecommendWorkflow({
|
|
|
746
769
|
|
|
747
770
|
let screeningCandidate = cardCandidate;
|
|
748
771
|
let detailResult = null;
|
|
772
|
+
let recoverableDetailError = null;
|
|
749
773
|
if (index < effectiveDetailLimit) {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
addTiming(timings, "detail_open_ms", openedDetail.timings?.detail_open_ms);
|
|
765
|
-
cardNodeId = openedDetail.card_node_id || cardNodeId;
|
|
766
|
-
cardCandidate = openedDetail.card_candidate || cardCandidate;
|
|
767
|
-
screeningCandidate = cardCandidate;
|
|
768
|
-
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
769
|
-
const networkWait = await measureTiming(timings, "network_cv_wait_ms", () => waitForCvNetworkEvents(
|
|
770
|
-
waitForRecommendDetailNetworkEvents,
|
|
771
|
-
networkRecorder,
|
|
772
|
-
{
|
|
773
|
-
waitPlan,
|
|
774
|
-
minCount: 1,
|
|
775
|
-
requireLoaded: true,
|
|
776
|
-
intervalMs: 120
|
|
777
|
-
}
|
|
778
|
-
));
|
|
779
|
-
if (networkWait?.elapsed_ms != null) {
|
|
780
|
-
timings.network_cv_wait_ms = Math.round(Number(networkWait.elapsed_ms) || 0);
|
|
781
|
-
}
|
|
782
|
-
detailResult = await extractRecommendDetailCandidate(client, {
|
|
783
|
-
cardCandidate,
|
|
784
|
-
cardNodeId,
|
|
785
|
-
detailState: openedDetail.detail_state,
|
|
786
|
-
networkEvents: networkRecorder.events,
|
|
787
|
-
targetUrl,
|
|
788
|
-
closeDetail: false,
|
|
789
|
-
networkParseRetryMs: waitPlan.mode_before === "image" ? 500 : 2200,
|
|
790
|
-
networkParseIntervalMs: 250
|
|
791
|
-
});
|
|
792
|
-
addTiming(timings, "late_network_retry_ms", detailResult.network_parse_retry_elapsed_ms);
|
|
793
|
-
|
|
794
|
-
const parsedNetworkProfileCount = countParsedNetworkProfiles(detailResult);
|
|
795
|
-
let source = "network";
|
|
796
|
-
let imageEvidence = null;
|
|
797
|
-
if (parsedNetworkProfileCount > 0) {
|
|
798
|
-
recordCvNetworkHit(cvAcquisitionState, {
|
|
799
|
-
parsedNetworkProfileCount,
|
|
800
|
-
waitResult: networkWait
|
|
774
|
+
try {
|
|
775
|
+
await runControl.waitIfPaused();
|
|
776
|
+
runControl.throwIfCanceled();
|
|
777
|
+
runControl.setPhase("recommend:detail");
|
|
778
|
+
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
779
|
+
networkRecorder.clear();
|
|
780
|
+
const openedDetail = await openRecommendCardDetailWithFreshRetry(client, {
|
|
781
|
+
cardNodeId,
|
|
782
|
+
candidateKey,
|
|
783
|
+
cardCandidate,
|
|
784
|
+
rootState,
|
|
785
|
+
targetUrl,
|
|
786
|
+
retryTimeoutMs: 8000,
|
|
787
|
+
maxAttempts: 3
|
|
801
788
|
});
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
filePath: imageEvidencePath,
|
|
817
|
-
format: "jpeg",
|
|
818
|
-
quality: 72,
|
|
819
|
-
optimize: true,
|
|
820
|
-
resizeMaxWidth: 1100,
|
|
821
|
-
captureViewport: true,
|
|
822
|
-
padding: 4,
|
|
823
|
-
maxScreenshots: maxImagePages,
|
|
824
|
-
wheelDeltaY: imageWheelDeltaY,
|
|
825
|
-
settleMs: 350,
|
|
826
|
-
scrollMethod: "dom-anchor-fallback-input",
|
|
827
|
-
stepTimeoutMs: 45000,
|
|
828
|
-
totalTimeoutMs: 90000,
|
|
829
|
-
duplicateStopCount: 1,
|
|
830
|
-
skipDuplicateScreenshots: true,
|
|
831
|
-
composeForLlm: true,
|
|
832
|
-
llmPagesPerImage: 3,
|
|
833
|
-
llmResizeMaxWidth: 1100,
|
|
834
|
-
llmQuality: 72,
|
|
835
|
-
metadata: {
|
|
836
|
-
domain: "recommend",
|
|
837
|
-
capture_mode: "scroll_sequence",
|
|
838
|
-
acquisition_reason: "network_miss_image_fallback",
|
|
839
|
-
run_candidate_index: index,
|
|
840
|
-
candidate_key: candidateKey
|
|
841
|
-
}
|
|
842
|
-
}));
|
|
843
|
-
source = "image";
|
|
844
|
-
} catch (error) {
|
|
845
|
-
if (!isRecoverableImageCaptureError(error)) throw error;
|
|
846
|
-
imageEvidence = createRecoverableImageCaptureEvidence(error, {
|
|
847
|
-
elapsedMs: timings.screenshot_capture_ms,
|
|
848
|
-
filePath: imageEvidencePath,
|
|
849
|
-
extension: "jpg",
|
|
850
|
-
maxScreenshots: maxImagePages
|
|
851
|
-
});
|
|
852
|
-
source = "image_capture_failed";
|
|
789
|
+
addTiming(timings, "candidate_click_ms", openedDetail.timings?.candidate_click_ms);
|
|
790
|
+
addTiming(timings, "detail_open_ms", openedDetail.timings?.detail_open_ms);
|
|
791
|
+
cardNodeId = openedDetail.card_node_id || cardNodeId;
|
|
792
|
+
cardCandidate = openedDetail.card_candidate || cardCandidate;
|
|
793
|
+
screeningCandidate = cardCandidate;
|
|
794
|
+
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
795
|
+
const networkWait = await measureTiming(timings, "network_cv_wait_ms", () => waitForCvNetworkEvents(
|
|
796
|
+
waitForRecommendDetailNetworkEvents,
|
|
797
|
+
networkRecorder,
|
|
798
|
+
{
|
|
799
|
+
waitPlan,
|
|
800
|
+
minCount: 1,
|
|
801
|
+
requireLoaded: true,
|
|
802
|
+
intervalMs: 120
|
|
853
803
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
804
|
+
));
|
|
805
|
+
if (networkWait?.elapsed_ms != null) {
|
|
806
|
+
timings.network_cv_wait_ms = Math.round(Number(networkWait.elapsed_ms) || 0);
|
|
807
|
+
}
|
|
808
|
+
detailResult = await extractRecommendDetailCandidate(client, {
|
|
809
|
+
cardCandidate,
|
|
810
|
+
cardNodeId,
|
|
811
|
+
detailState: openedDetail.detail_state,
|
|
812
|
+
networkEvents: networkRecorder.events,
|
|
813
|
+
targetUrl,
|
|
814
|
+
closeDetail: false,
|
|
815
|
+
networkParseRetryMs: waitPlan.mode_before === "image" ? 500 : 2200,
|
|
816
|
+
networkParseIntervalMs: 250
|
|
817
|
+
});
|
|
818
|
+
addTiming(timings, "late_network_retry_ms", detailResult.network_parse_retry_elapsed_ms);
|
|
819
|
+
|
|
820
|
+
const parsedNetworkProfileCount = countParsedNetworkProfiles(detailResult);
|
|
821
|
+
let source = "network";
|
|
822
|
+
let imageEvidence = null;
|
|
823
|
+
if (parsedNetworkProfileCount > 0) {
|
|
824
|
+
recordCvNetworkHit(cvAcquisitionState, {
|
|
866
825
|
parsedNetworkProfileCount,
|
|
867
826
|
waitResult: networkWait
|
|
868
827
|
});
|
|
828
|
+
} else {
|
|
829
|
+
const captureNodeId = openedDetail.detail_state?.popup?.node_id
|
|
830
|
+
|| openedDetail.detail_state?.resumeIframe?.node_id
|
|
831
|
+
|| null;
|
|
832
|
+
if (captureNodeId) {
|
|
833
|
+
const imageEvidencePath = imageEvidenceFilePath({
|
|
834
|
+
imageOutputDir,
|
|
835
|
+
domain: "recommend",
|
|
836
|
+
runId: runControl?.runId,
|
|
837
|
+
index,
|
|
838
|
+
extension: "jpg"
|
|
839
|
+
});
|
|
840
|
+
try {
|
|
841
|
+
imageEvidence = await measureTiming(timings, "screenshot_capture_ms", () => captureScrolledNodeScreenshots(client, captureNodeId, {
|
|
842
|
+
filePath: imageEvidencePath,
|
|
843
|
+
format: "jpeg",
|
|
844
|
+
quality: 72,
|
|
845
|
+
optimize: true,
|
|
846
|
+
resizeMaxWidth: 1100,
|
|
847
|
+
captureViewport: true,
|
|
848
|
+
padding: 4,
|
|
849
|
+
maxScreenshots: maxImagePages,
|
|
850
|
+
wheelDeltaY: imageWheelDeltaY,
|
|
851
|
+
settleMs: 350,
|
|
852
|
+
scrollMethod: "dom-anchor-fallback-input",
|
|
853
|
+
stepTimeoutMs: 45000,
|
|
854
|
+
totalTimeoutMs: 90000,
|
|
855
|
+
duplicateStopCount: 1,
|
|
856
|
+
skipDuplicateScreenshots: true,
|
|
857
|
+
composeForLlm: true,
|
|
858
|
+
llmPagesPerImage: 3,
|
|
859
|
+
llmResizeMaxWidth: 1100,
|
|
860
|
+
llmQuality: 72,
|
|
861
|
+
metadata: {
|
|
862
|
+
domain: "recommend",
|
|
863
|
+
capture_mode: "scroll_sequence",
|
|
864
|
+
acquisition_reason: "network_miss_image_fallback",
|
|
865
|
+
run_candidate_index: index,
|
|
866
|
+
candidate_key: candidateKey
|
|
867
|
+
}
|
|
868
|
+
}));
|
|
869
|
+
source = "image";
|
|
870
|
+
} catch (error) {
|
|
871
|
+
if (!isRecoverableImageCaptureError(error)) throw error;
|
|
872
|
+
imageEvidence = createRecoverableImageCaptureEvidence(error, {
|
|
873
|
+
elapsedMs: timings.screenshot_capture_ms,
|
|
874
|
+
filePath: imageEvidencePath,
|
|
875
|
+
extension: "jpg",
|
|
876
|
+
maxScreenshots: maxImagePages
|
|
877
|
+
});
|
|
878
|
+
source = "image_capture_failed";
|
|
879
|
+
}
|
|
880
|
+
recordCvImageFallback(cvAcquisitionState, {
|
|
881
|
+
reason: source === "image_capture_failed"
|
|
882
|
+
? "network_miss_image_capture_failed"
|
|
883
|
+
: "network_miss_image_fallback",
|
|
884
|
+
parsedNetworkProfileCount,
|
|
885
|
+
waitResult: networkWait,
|
|
886
|
+
imageEvidence
|
|
887
|
+
});
|
|
888
|
+
} else {
|
|
889
|
+
source = "missing_capture_node";
|
|
890
|
+
recordCvNetworkMiss(cvAcquisitionState, {
|
|
891
|
+
reason: "network_miss_no_capture_node",
|
|
892
|
+
parsedNetworkProfileCount,
|
|
893
|
+
waitResult: networkWait
|
|
894
|
+
});
|
|
895
|
+
}
|
|
869
896
|
}
|
|
870
|
-
}
|
|
871
897
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
898
|
+
detailResult.image_evidence = imageEvidence;
|
|
899
|
+
detailResult.cv_acquisition = {
|
|
900
|
+
source,
|
|
901
|
+
mode_after: compactCvAcquisitionState(cvAcquisitionState).mode,
|
|
902
|
+
wait_plan: waitPlan,
|
|
903
|
+
network_wait: networkWait,
|
|
904
|
+
parsed_network_profile_count: parsedNetworkProfileCount,
|
|
905
|
+
image_evidence: summarizeImageEvidence(imageEvidence)
|
|
906
|
+
};
|
|
907
|
+
screeningCandidate = detailResult.candidate;
|
|
908
|
+
} catch (error) {
|
|
909
|
+
if (!isRecoverableRecommendDetailError(error)) throw error;
|
|
910
|
+
recoverableDetailError = error;
|
|
911
|
+
detailResult = null;
|
|
912
|
+
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
913
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
914
|
+
}
|
|
882
915
|
}
|
|
883
916
|
|
|
884
917
|
await runControl.waitIfPaused();
|
|
@@ -886,7 +919,7 @@ export async function runRecommendWorkflow({
|
|
|
886
919
|
runControl.setPhase("recommend:screening");
|
|
887
920
|
let llmResult = null;
|
|
888
921
|
if (useLlmScreening) {
|
|
889
|
-
if (detailResult?.image_evidence?.ok === false) {
|
|
922
|
+
if (recoverableDetailError || detailResult?.image_evidence?.ok === false) {
|
|
890
923
|
llmResult = null;
|
|
891
924
|
} else if (!llmConfig) {
|
|
892
925
|
llmResult = createMissingLlmConfigResult();
|
|
@@ -910,7 +943,9 @@ export async function runRecommendWorkflow({
|
|
|
910
943
|
}
|
|
911
944
|
if (detailResult) detailResult.llm_result = llmResult;
|
|
912
945
|
}
|
|
913
|
-
const screening =
|
|
946
|
+
const screening = recoverableDetailError
|
|
947
|
+
? createRecoverableDetailFailureScreening(screeningCandidate, recoverableDetailError)
|
|
948
|
+
: detailResult?.image_evidence?.ok === false
|
|
914
949
|
? createImageCaptureFailureScreening(screeningCandidate, {
|
|
915
950
|
code: detailResult.image_evidence.error_code,
|
|
916
951
|
message: detailResult.image_evidence.error
|
|
@@ -959,7 +994,9 @@ export async function runRecommendWorkflow({
|
|
|
959
994
|
screening: compactScreening(screening),
|
|
960
995
|
action_discovery: compactActionDiscovery(actionDiscovery),
|
|
961
996
|
post_action: postActionResult,
|
|
962
|
-
error:
|
|
997
|
+
error: recoverableDetailError
|
|
998
|
+
? compactRecoverableDetailError(recoverableDetailError)
|
|
999
|
+
: detailResult?.image_evidence?.ok === false
|
|
963
1000
|
? compactError({
|
|
964
1001
|
code: detailResult.image_evidence.error_code,
|
|
965
1002
|
message: detailResult.image_evidence.error
|
|
@@ -987,6 +1024,8 @@ export async function runRecommendWorkflow({
|
|
|
987
1024
|
greet_count: greetCount,
|
|
988
1025
|
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
989
1026
|
image_capture_failed: results.filter((item) => item.detail?.image_evidence?.ok === false).length,
|
|
1027
|
+
detail_open_failed: results.filter((item) => item.error?.code === "DETAIL_STALE_NODE" || item.error?.code === "DETAIL_OPEN_FAILED").length,
|
|
1028
|
+
transient_recovered: results.filter((item) => item.error?.code === "DETAIL_STALE_NODE" || item.error?.code === "IMAGE_CAPTURE_STALE_NODE" || item.error?.code === "IMAGE_CAPTURE_TIMEOUT" || item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT").length,
|
|
990
1029
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
991
1030
|
scroll_count: compactInfiniteListState(listState).scroll_count,
|
|
992
1031
|
refresh_rounds: refreshRounds,
|
|
@@ -1050,6 +1089,9 @@ export async function runRecommendWorkflow({
|
|
|
1050
1089
|
screened: results.length,
|
|
1051
1090
|
detail_opened: results.filter((item) => item.detail).length,
|
|
1052
1091
|
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
1092
|
+
detail_open_failed: results.filter((item) => item.error?.code === "DETAIL_STALE_NODE" || item.error?.code === "DETAIL_OPEN_FAILED").length,
|
|
1093
|
+
image_capture_failed: results.filter((item) => item.detail?.image_evidence?.ok === false).length,
|
|
1094
|
+
transient_recovered: results.filter((item) => item.error?.code === "DETAIL_STALE_NODE" || item.error?.code === "IMAGE_CAPTURE_STALE_NODE" || item.error?.code === "IMAGE_CAPTURE_TIMEOUT" || item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT").length,
|
|
1053
1095
|
passed: results.filter((item) => item.screening.passed).length,
|
|
1054
1096
|
greet_count: greetCount,
|
|
1055
1097
|
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
package/src/recommend-mcp.js
CHANGED
|
@@ -288,6 +288,66 @@ function completionReason(status) {
|
|
|
288
288
|
return null;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
function normalizeErrorText(error = {}) {
|
|
292
|
+
return normalizeText([
|
|
293
|
+
error?.code || "",
|
|
294
|
+
error?.message || error || ""
|
|
295
|
+
].join(" "));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function classifyRecommendRecovery(error = {}) {
|
|
299
|
+
const text = normalizeErrorText(error);
|
|
300
|
+
if (!text) return null;
|
|
301
|
+
if (/BOSS_LOGIN_REQUIRED/i.test(text)) return "login_required";
|
|
302
|
+
if (/Could not find node with given id|No node with given id|Node is detached|Cannot find node|DETAIL_STALE_NODE|IMAGE_CAPTURE_STALE_NODE/i.test(text)) {
|
|
303
|
+
return "transient_stale_dom";
|
|
304
|
+
}
|
|
305
|
+
if (/IMAGE_CAPTURE_TIMEOUT|IMAGE_CAPTURE_TOTAL_TIMEOUT|Image fallback capture timed out/i.test(text)) {
|
|
306
|
+
return "transient_image_capture";
|
|
307
|
+
}
|
|
308
|
+
if (/(?:aborted|abort|timeout|timed out|fetch failed|socket|network|ECONNRESET|ETIMEDOUT|EAI_AGAIN)/i.test(text)) {
|
|
309
|
+
return "transient_network_or_llm";
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function buildConstrainedAgentRecovery(snapshot = {}, meta = {}, artifacts = null) {
|
|
315
|
+
const error = snapshot?.error || snapshot?.result?.error || null;
|
|
316
|
+
const classification = classifyRecommendRecovery(error);
|
|
317
|
+
if (!classification) return null;
|
|
318
|
+
const canRestartSameRequest = classification !== "login_required";
|
|
319
|
+
return {
|
|
320
|
+
policy_version: 1,
|
|
321
|
+
classification,
|
|
322
|
+
safe_for_outer_ai_agent: true,
|
|
323
|
+
recommended_action: canRestartSameRequest
|
|
324
|
+
? "restart_same_recommend_request_only"
|
|
325
|
+
: "ask_user_to_login_then_retry_same_recommend_request",
|
|
326
|
+
package_requirement: "@reconcrap/boss-recommend-mcp@>=2.0.30",
|
|
327
|
+
run_id: snapshot?.runId || snapshot?.run_id || null,
|
|
328
|
+
retryable: true,
|
|
329
|
+
same_request_sources: {
|
|
330
|
+
instruction: "run.context.instruction",
|
|
331
|
+
confirmation: "run.context.confirmation",
|
|
332
|
+
overrides: "run.context.overrides",
|
|
333
|
+
follow_up: "run.context.follow_up"
|
|
334
|
+
},
|
|
335
|
+
constraints: [
|
|
336
|
+
"Do not change instruction, criteria, filters, job, page_scope, target_count, post_action, or max_greet_count.",
|
|
337
|
+
"Do not switch to search/recruit/chat and do not add follow_up.chat.",
|
|
338
|
+
"Do not summarize, translate, or rewrite criteria.",
|
|
339
|
+
"Do not ask the user to reconfirm business choices unless Boss login is required or the stored context is missing.",
|
|
340
|
+
"Use the same Chrome debug port and recommend page route."
|
|
341
|
+
],
|
|
342
|
+
artifacts: artifacts ? {
|
|
343
|
+
run_state_path: artifacts.run_state_path || null,
|
|
344
|
+
checkpoint_path: artifacts.checkpoint_path || null,
|
|
345
|
+
report_json: artifacts.report_json || null,
|
|
346
|
+
output_csv: artifacts.output_csv || null
|
|
347
|
+
} : null
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
291
351
|
function ensureRecommendRunArtifacts(snapshot) {
|
|
292
352
|
const artifacts = getRecommendRunArtifacts(snapshot?.runId || snapshot?.run_id);
|
|
293
353
|
if (!artifacts) return null;
|
|
@@ -319,6 +379,7 @@ function ensureRecommendRunArtifacts(snapshot) {
|
|
|
319
379
|
checkpoint,
|
|
320
380
|
error: snapshot.error || null,
|
|
321
381
|
last_message: snapshot.error?.message || snapshot.phase || snapshot.stage || null,
|
|
382
|
+
recovery: buildConstrainedAgentRecovery(snapshot, meta, artifacts),
|
|
322
383
|
summary: artifactSummary,
|
|
323
384
|
generated_at: new Date().toISOString()
|
|
324
385
|
});
|
|
@@ -397,6 +458,7 @@ function buildLegacyRecommendResult(snapshot) {
|
|
|
397
458
|
screen_params: clonePlain(meta.parsed?.screenParams || {}, {}),
|
|
398
459
|
target_count_semantics: TARGET_COUNT_SEMANTICS,
|
|
399
460
|
error: snapshot.error || null,
|
|
461
|
+
recovery: buildConstrainedAgentRecovery(snapshot, meta, artifacts),
|
|
400
462
|
results: resultRows
|
|
401
463
|
};
|
|
402
464
|
}
|
|
@@ -411,6 +473,7 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
411
473
|
TERMINAL_STATUSES.has(snapshot.status)
|
|
412
474
|
|| snapshot.status === RUN_STATUS_PAUSED
|
|
413
475
|
) ? buildLegacyRecommendResult({ ...snapshot, progress }) : null;
|
|
476
|
+
const recovery = buildConstrainedAgentRecovery(snapshot, meta, artifacts);
|
|
414
477
|
const oldContext = {
|
|
415
478
|
workspace_root: meta.workspaceRoot || null,
|
|
416
479
|
instruction: meta.args?.instruction || "",
|
|
@@ -451,6 +514,7 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
451
514
|
last_resumed_at: meta.lastResumedAt || null,
|
|
452
515
|
last_paused_at: snapshot.status === RUN_STATUS_PAUSED ? snapshot.updatedAt : null
|
|
453
516
|
},
|
|
517
|
+
recovery,
|
|
454
518
|
result: legacyResult,
|
|
455
519
|
artifacts
|
|
456
520
|
};
|
|
@@ -483,6 +547,7 @@ function persistRecommendRunSnapshot(snapshot, {
|
|
|
483
547
|
control: normalized.control,
|
|
484
548
|
resume: normalized.resume,
|
|
485
549
|
error: normalized.error,
|
|
550
|
+
recovery: normalized.recovery,
|
|
486
551
|
result: normalized.result,
|
|
487
552
|
summary: normalized.summary,
|
|
488
553
|
artifacts: normalized.artifacts
|