@reconcrap/boss-recommend-mcp 2.0.54 → 2.0.56
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/chat/constants.js +9 -24
- package/src/domains/chat/detail.js +34 -186
- package/src/domains/common/account-rights-panel.js +314 -0
- package/src/domains/recommend/constants.js +17 -0
- package/src/domains/recommend/detail.js +379 -72
- package/src/domains/recommend/run-service.js +137 -57
- package/src/domains/recruit/detail.js +15 -0
- package/src/domains/recruit/run-service.js +78 -1
|
@@ -45,7 +45,9 @@ import {
|
|
|
45
45
|
llmResultToScreening,
|
|
46
46
|
screenCandidate
|
|
47
47
|
} from "../../core/screening/index.js";
|
|
48
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
closeRecommendBlockingPanels,
|
|
50
|
+
closeRecommendAvatarPreview,
|
|
49
51
|
closeRecommendDetail,
|
|
50
52
|
createRecommendDetailNetworkRecorder,
|
|
51
53
|
extractRecommendDetailCandidate,
|
|
@@ -465,12 +467,17 @@ function countPassedResults(results = []) {
|
|
|
465
467
|
|
|
466
468
|
function compactCloseResult(closeResult) {
|
|
467
469
|
if (!closeResult) return null;
|
|
468
|
-
|
|
470
|
+
const result = {
|
|
469
471
|
closed: Boolean(closeResult.closed),
|
|
470
472
|
reason: closeResult.reason || null,
|
|
473
|
+
probe: closeResult.probe || null,
|
|
471
474
|
attempts: closeResult.attempts || [],
|
|
472
475
|
verification: closeResult.verification || null
|
|
473
476
|
};
|
|
477
|
+
if (closeResult.already_closed !== undefined) {
|
|
478
|
+
result.already_closed = Boolean(closeResult.already_closed);
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
474
481
|
}
|
|
475
482
|
|
|
476
483
|
function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
@@ -482,6 +489,9 @@ function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
|
482
489
|
if (error.close_result) {
|
|
483
490
|
result.close_result = compactCloseResult(error.close_result);
|
|
484
491
|
}
|
|
492
|
+
if (error.phase) {
|
|
493
|
+
result.phase = error.phase;
|
|
494
|
+
}
|
|
485
495
|
if (error.refresh_attempt) {
|
|
486
496
|
result.refresh_attempt = error.refresh_attempt;
|
|
487
497
|
}
|
|
@@ -497,6 +507,16 @@ function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
|
497
507
|
if (Array.isArray(error.recommend_detail_open_attempts)) {
|
|
498
508
|
result.recommend_detail_open_attempts = error.recommend_detail_open_attempts;
|
|
499
509
|
}
|
|
510
|
+
if (Array.isArray(error.click_attempts)) {
|
|
511
|
+
result.click_attempts = error.click_attempts;
|
|
512
|
+
}
|
|
513
|
+
if (error.avatar_preview) {
|
|
514
|
+
result.avatar_preview = {
|
|
515
|
+
open: Boolean(error.avatar_preview.open),
|
|
516
|
+
selector: error.avatar_preview.preview?.selector || null,
|
|
517
|
+
rect: error.avatar_preview.preview?.rect || null
|
|
518
|
+
};
|
|
519
|
+
}
|
|
500
520
|
return result;
|
|
501
521
|
}
|
|
502
522
|
|
|
@@ -507,6 +527,14 @@ function createRecommendCloseFailureError(closeResult) {
|
|
|
507
527
|
return error;
|
|
508
528
|
}
|
|
509
529
|
|
|
530
|
+
function createRecommendBlockingPanelCloseFailureError(closeResult, phase = "") {
|
|
531
|
+
const error = new Error(closeResult?.reason || "Boss account-rights panel did not close before recovery");
|
|
532
|
+
error.code = "ACCOUNT_RIGHTS_PANEL_CLOSE_FAILED";
|
|
533
|
+
error.close_result = closeResult || null;
|
|
534
|
+
error.phase = phase || null;
|
|
535
|
+
return error;
|
|
536
|
+
}
|
|
537
|
+
|
|
510
538
|
function createRecommendRefreshFailureError(refreshAttempt, {
|
|
511
539
|
listEndReason = "",
|
|
512
540
|
targetCount = 0,
|
|
@@ -699,10 +727,11 @@ export async function runRecommendWorkflow({
|
|
|
699
727
|
let refreshRounds = 0;
|
|
700
728
|
let contextRecoveryAttempts = 0;
|
|
701
729
|
let greetCount = 0;
|
|
702
|
-
const candidateRecoveryCounts = new Map();
|
|
703
|
-
let jobSelection = null;
|
|
730
|
+
const candidateRecoveryCounts = new Map();
|
|
731
|
+
let jobSelection = null;
|
|
704
732
|
let pageScopeSelection = null;
|
|
705
733
|
let filterResult = null;
|
|
734
|
+
let rootState = null;
|
|
706
735
|
let cardNodeIds = [];
|
|
707
736
|
let listEndReason = "";
|
|
708
737
|
let lastHumanEvent = null;
|
|
@@ -769,9 +798,9 @@ export async function runRecommendWorkflow({
|
|
|
769
798
|
});
|
|
770
799
|
}
|
|
771
800
|
|
|
772
|
-
function checkpointInProgressCandidate({
|
|
773
|
-
index = results.length,
|
|
774
|
-
candidateKey = "",
|
|
801
|
+
function checkpointInProgressCandidate({
|
|
802
|
+
index = results.length,
|
|
803
|
+
candidateKey = "",
|
|
775
804
|
cardNodeId = null,
|
|
776
805
|
detailStep = "",
|
|
777
806
|
error = null
|
|
@@ -785,11 +814,22 @@ export async function runRecommendWorkflow({
|
|
|
785
814
|
counters: countRecommendResultStatuses(results, { greetCount }),
|
|
786
815
|
error: compactError(error, "RECOMMEND_IN_PROGRESS_ERROR")
|
|
787
816
|
},
|
|
788
|
-
candidate_list: compactInfiniteListState(listState)
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
async function
|
|
817
|
+
candidate_list: compactInfiniteListState(listState)
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
async function closeRecommendBlockingPanelsForRun(phase = "cleanup") {
|
|
822
|
+
const result = await closeRecommendBlockingPanels(client, {
|
|
823
|
+
attemptsLimit: 2,
|
|
824
|
+
rootState
|
|
825
|
+
});
|
|
826
|
+
if (!result?.closed) {
|
|
827
|
+
throw createRecommendBlockingPanelCloseFailureError(result, phase);
|
|
828
|
+
}
|
|
829
|
+
return result;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async function recoverAndReapplyRecommendContext(reason = "context_recovery", error = null, {
|
|
793
833
|
forceRecentNotView = true
|
|
794
834
|
} = {}) {
|
|
795
835
|
await runControl.waitIfPaused();
|
|
@@ -797,9 +837,9 @@ export async function runRecommendWorkflow({
|
|
|
797
837
|
const started = Date.now();
|
|
798
838
|
runControl.setPhase("recommend:recover-context");
|
|
799
839
|
contextRecoveryAttempts += 1;
|
|
800
|
-
const refreshResult = await refreshRecommendListAtEnd(client, {
|
|
801
|
-
rootState,
|
|
802
|
-
jobLabel,
|
|
840
|
+
const refreshResult = await refreshRecommendListAtEnd(client, {
|
|
841
|
+
rootState,
|
|
842
|
+
jobLabel,
|
|
803
843
|
pageScope: pageScopeSelection?.effective_scope || requestedPageScope,
|
|
804
844
|
fallbackPageScope: normalizedFallbackPageScope,
|
|
805
845
|
filter: normalizedFilter,
|
|
@@ -808,16 +848,24 @@ export async function runRecommendWorkflow({
|
|
|
808
848
|
targetUrl: targetUrl || RECOMMEND_TARGET_URL,
|
|
809
849
|
forceRecentNotView,
|
|
810
850
|
cardTimeoutMs,
|
|
811
|
-
buttonSettleMs: refreshButtonSettleMs,
|
|
812
|
-
reloadSettleMs: refreshReloadSettleMs
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
851
|
+
buttonSettleMs: refreshButtonSettleMs,
|
|
852
|
+
reloadSettleMs: refreshReloadSettleMs
|
|
853
|
+
});
|
|
854
|
+
let blockingPanelClose = null;
|
|
855
|
+
if (refreshResult.ok) {
|
|
856
|
+
blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
857
|
+
attemptsLimit: 2,
|
|
858
|
+
rootState: refreshResult.root_state || rootState
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
const compactRefresh = {
|
|
862
|
+
...compactRefreshAttempt(refreshResult),
|
|
863
|
+
context_recovery: true,
|
|
864
|
+
recovery_reason: reason,
|
|
865
|
+
trigger_error: compactError(error, "RECOMMEND_CONTEXT_RECOVERY_TRIGGER"),
|
|
866
|
+
account_rights_panel_close: compactCloseResult(blockingPanelClose),
|
|
867
|
+
elapsed_ms: Date.now() - started
|
|
868
|
+
};
|
|
821
869
|
refreshAttempts.push(compactRefresh);
|
|
822
870
|
runControl.checkpoint({
|
|
823
871
|
context_recovery: {
|
|
@@ -834,10 +882,15 @@ export async function runRecommendWorkflow({
|
|
|
834
882
|
refresh_method: refreshResult.method || null,
|
|
835
883
|
refresh_forced_recent_not_view: forceRecentNotView,
|
|
836
884
|
recovery_reason: reason
|
|
837
|
-
});
|
|
838
|
-
throw new Error(`Recommend context recovery failed after ${reason}: ${refreshResult.reason || refreshResult.error || "refresh returned no cards"}`);
|
|
839
|
-
}
|
|
840
|
-
|
|
885
|
+
});
|
|
886
|
+
throw new Error(`Recommend context recovery failed after ${reason}: ${refreshResult.reason || refreshResult.error || "refresh returned no cards"}`);
|
|
887
|
+
}
|
|
888
|
+
if (!blockingPanelClose?.closed) {
|
|
889
|
+
const panelError = createRecommendBlockingPanelCloseFailureError(blockingPanelClose, `recover:${reason}`);
|
|
890
|
+
panelError.refresh_attempt = compactRefresh;
|
|
891
|
+
throw panelError;
|
|
892
|
+
}
|
|
893
|
+
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
841
894
|
rootState = await ensureRecommendViewport(rootState, "recover_after");
|
|
842
895
|
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
843
896
|
timeoutMs: cardTimeoutMs,
|
|
@@ -863,13 +916,15 @@ export async function runRecommendWorkflow({
|
|
|
863
916
|
return refreshResult;
|
|
864
917
|
}
|
|
865
918
|
|
|
866
|
-
runControl.setPhase("recommend:cleanup");
|
|
867
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 });
|
|
868
|
-
|
|
869
|
-
await
|
|
870
|
-
|
|
871
|
-
runControl.
|
|
872
|
-
|
|
919
|
+
runControl.setPhase("recommend:cleanup");
|
|
920
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 });
|
|
921
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 });
|
|
922
|
+
await closeRecommendBlockingPanelsForRun("cleanup");
|
|
923
|
+
|
|
924
|
+
await runControl.waitIfPaused();
|
|
925
|
+
runControl.throwIfCanceled();
|
|
926
|
+
runControl.setPhase("recommend:roots");
|
|
927
|
+
rootState = await getRecommendRoots(client);
|
|
873
928
|
rootState = await ensureRecommendViewport(rootState, "roots");
|
|
874
929
|
runControl.checkpoint({
|
|
875
930
|
iframe_selector: rootState.iframe.selector,
|
|
@@ -1067,9 +1122,28 @@ export async function runRecommendWorkflow({
|
|
|
1067
1122
|
try {
|
|
1068
1123
|
await runControl.waitIfPaused();
|
|
1069
1124
|
runControl.throwIfCanceled();
|
|
1070
|
-
runControl.setPhase("recommend:detail");
|
|
1071
|
-
detailStep = "ensure_viewport";
|
|
1072
|
-
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
1125
|
+
runControl.setPhase("recommend:detail");
|
|
1126
|
+
detailStep = "ensure_viewport";
|
|
1127
|
+
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
1128
|
+
const blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
1129
|
+
attemptsLimit: 2,
|
|
1130
|
+
rootState
|
|
1131
|
+
});
|
|
1132
|
+
if (!blockingPanelClose?.closed) {
|
|
1133
|
+
const panelError = createRecommendBlockingPanelCloseFailureError(
|
|
1134
|
+
blockingPanelClose,
|
|
1135
|
+
"before_detail_open"
|
|
1136
|
+
);
|
|
1137
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1138
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error: panelError });
|
|
1139
|
+
await recoverAndReapplyRecommendContext("account_rights_panel_before_detail", panelError, {
|
|
1140
|
+
forceRecentNotView: true
|
|
1141
|
+
});
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (blockingPanelClose.already_closed === false) {
|
|
1145
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1146
|
+
}
|
|
1073
1147
|
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep });
|
|
1074
1148
|
detailStep = "open_detail";
|
|
1075
1149
|
networkRecorder.clear();
|
|
@@ -1182,12 +1256,14 @@ export async function runRecommendWorkflow({
|
|
|
1182
1256
|
const recoveryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
1183
1257
|
if (recoveryCount < 1) {
|
|
1184
1258
|
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1185
|
-
timings.image_capture_recovery_trigger = compactError(error, "IMAGE_CAPTURE_FAILED");
|
|
1186
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1187
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1188
|
-
await
|
|
1189
|
-
|
|
1190
|
-
}
|
|
1259
|
+
timings.image_capture_recovery_trigger = compactError(error, "IMAGE_CAPTURE_FAILED");
|
|
1260
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1261
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1262
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1263
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1264
|
+
await recoverAndReapplyRecommendContext(`image_capture:${detailStep}`, error, {
|
|
1265
|
+
forceRecentNotView: true
|
|
1266
|
+
});
|
|
1191
1267
|
continue;
|
|
1192
1268
|
}
|
|
1193
1269
|
imageEvidence = createRecoverableImageCaptureEvidence(error, {
|
|
@@ -1232,20 +1308,24 @@ export async function runRecommendWorkflow({
|
|
|
1232
1308
|
if (!isRecoverableRecommendDetailError(error)) throw error;
|
|
1233
1309
|
const recoveryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
1234
1310
|
if (recoveryCount < 1) {
|
|
1235
|
-
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1236
|
-
timings.detail_recovery_trigger = compactRecoverableDetailError(error);
|
|
1237
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1238
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1239
|
-
await
|
|
1240
|
-
|
|
1241
|
-
}
|
|
1311
|
+
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1312
|
+
timings.detail_recovery_trigger = compactRecoverableDetailError(error);
|
|
1313
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1314
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1315
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1316
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1317
|
+
await recoverAndReapplyRecommendContext(`detail:${detailStep}`, error, {
|
|
1318
|
+
forceRecentNotView: true
|
|
1319
|
+
});
|
|
1242
1320
|
continue;
|
|
1243
1321
|
}
|
|
1244
|
-
recoverableDetailError = error;
|
|
1245
|
-
detailResult = null;
|
|
1246
|
-
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
1247
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1248
|
-
|
|
1322
|
+
recoverableDetailError = error;
|
|
1323
|
+
detailResult = null;
|
|
1324
|
+
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
1325
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1326
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1327
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1328
|
+
}
|
|
1249
1329
|
}
|
|
1250
1330
|
|
|
1251
1331
|
await runControl.waitIfPaused();
|
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
buildScreeningCandidateFromDetail,
|
|
14
14
|
htmlToText
|
|
15
15
|
} from "../../core/screening/index.js";
|
|
16
|
+
import {
|
|
17
|
+
closeBossAccountRightsBlockingPanel,
|
|
18
|
+
findBossAccountRightsBlockingPanel
|
|
19
|
+
} from "../common/account-rights-panel.js";
|
|
16
20
|
import {
|
|
17
21
|
RECRUIT_DETAIL_CLOSE_SELECTORS,
|
|
18
22
|
RECRUIT_DETAIL_NETWORK_PATTERNS,
|
|
@@ -64,6 +68,17 @@ export function createRecruitDetailNetworkRecorder(client) {
|
|
|
64
68
|
};
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
export async function findRecruitBlockingPanel(client, options = {}) {
|
|
72
|
+
return findBossAccountRightsBlockingPanel(client, options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function closeRecruitBlockingPanels(client, options = {}) {
|
|
76
|
+
return closeBossAccountRightsBlockingPanel(client, {
|
|
77
|
+
resolveRoots: getRecruitRoots,
|
|
78
|
+
...options
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
67
82
|
export async function waitForRecruitDetailNetworkEvents(recorder, {
|
|
68
83
|
minCount = 1,
|
|
69
84
|
requireLoaded = true,
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
screenCandidate
|
|
45
45
|
} from "../../core/screening/index.js";
|
|
46
46
|
import {
|
|
47
|
+
closeRecruitBlockingPanels,
|
|
47
48
|
closeRecruitDetail,
|
|
48
49
|
createRecruitDetailNetworkRecorder,
|
|
49
50
|
extractRecruitDetailCandidate,
|
|
@@ -159,6 +160,12 @@ function compactError(error, fallbackCode = "RECRUIT_RUN_ERROR") {
|
|
|
159
160
|
code: error.code || fallbackCode,
|
|
160
161
|
message: error.message || String(error)
|
|
161
162
|
};
|
|
163
|
+
if (error.close_result) {
|
|
164
|
+
result.close_result = compactCloseResult(error.close_result);
|
|
165
|
+
}
|
|
166
|
+
if (error.phase) {
|
|
167
|
+
result.phase = error.phase;
|
|
168
|
+
}
|
|
162
169
|
if (error.refresh_attempt) {
|
|
163
170
|
result.refresh_attempt = error.refresh_attempt;
|
|
164
171
|
}
|
|
@@ -174,6 +181,21 @@ function compactError(error, fallbackCode = "RECRUIT_RUN_ERROR") {
|
|
|
174
181
|
return result;
|
|
175
182
|
}
|
|
176
183
|
|
|
184
|
+
function compactCloseResult(closeResult) {
|
|
185
|
+
if (!closeResult) return null;
|
|
186
|
+
const result = {
|
|
187
|
+
closed: Boolean(closeResult.closed),
|
|
188
|
+
reason: closeResult.reason || null,
|
|
189
|
+
probe: closeResult.probe || null,
|
|
190
|
+
attempts: closeResult.attempts || [],
|
|
191
|
+
verification: closeResult.verification || null
|
|
192
|
+
};
|
|
193
|
+
if (closeResult.already_closed !== undefined) {
|
|
194
|
+
result.already_closed = Boolean(closeResult.already_closed);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
|
|
177
199
|
function createRecruitCloseFailureError(closeResult) {
|
|
178
200
|
const error = new Error(closeResult?.reason || "Recruit detail did not close before recovery");
|
|
179
201
|
error.code = "DETAIL_CLOSE_FAILED";
|
|
@@ -181,6 +203,14 @@ function createRecruitCloseFailureError(closeResult) {
|
|
|
181
203
|
return error;
|
|
182
204
|
}
|
|
183
205
|
|
|
206
|
+
function createRecruitBlockingPanelCloseFailureError(closeResult, phase = "") {
|
|
207
|
+
const error = new Error(closeResult?.reason || "Boss account-rights panel did not close before recovery");
|
|
208
|
+
error.code = "ACCOUNT_RIGHTS_PANEL_CLOSE_FAILED";
|
|
209
|
+
error.close_result = closeResult || null;
|
|
210
|
+
error.phase = phase || null;
|
|
211
|
+
return error;
|
|
212
|
+
}
|
|
213
|
+
|
|
184
214
|
function createRecruitRefreshFailureError(refreshAttempt, {
|
|
185
215
|
listEndReason = "",
|
|
186
216
|
targetCount = 0,
|
|
@@ -397,6 +427,7 @@ export async function runRecruitWorkflow({
|
|
|
397
427
|
let refreshRounds = 0;
|
|
398
428
|
let contextRecoveryAttempts = 0;
|
|
399
429
|
const candidateRecoveryCounts = new Map();
|
|
430
|
+
let rootState = null;
|
|
400
431
|
let cardNodeIds = [];
|
|
401
432
|
let listEndReason = "";
|
|
402
433
|
let lastHumanEvent = null;
|
|
@@ -482,6 +513,17 @@ export async function runRecruitWorkflow({
|
|
|
482
513
|
});
|
|
483
514
|
}
|
|
484
515
|
|
|
516
|
+
async function closeRecruitBlockingPanelsForRun(phase = "cleanup") {
|
|
517
|
+
const result = await closeRecruitBlockingPanels(client, {
|
|
518
|
+
attemptsLimit: 2,
|
|
519
|
+
rootState
|
|
520
|
+
});
|
|
521
|
+
if (!result?.closed) {
|
|
522
|
+
throw createRecruitBlockingPanelCloseFailureError(result, phase);
|
|
523
|
+
}
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
|
|
485
527
|
async function recoverAndReapplyRecruitContext(reason = "context_recovery", error = null, {
|
|
486
528
|
forceRecentViewed = true
|
|
487
529
|
} = {}) {
|
|
@@ -499,11 +541,18 @@ export async function runRecruitWorkflow({
|
|
|
499
541
|
cityOptionTimeoutMs,
|
|
500
542
|
forceRecentViewed
|
|
501
543
|
});
|
|
544
|
+
let blockingPanelClose = null;
|
|
545
|
+
if (refreshResult.ok) {
|
|
546
|
+
blockingPanelClose = await closeRecruitBlockingPanels(client, {
|
|
547
|
+
attemptsLimit: 2
|
|
548
|
+
});
|
|
549
|
+
}
|
|
502
550
|
const compactRefresh = {
|
|
503
551
|
...compactRefreshAttempt(refreshResult),
|
|
504
552
|
context_recovery: true,
|
|
505
553
|
recovery_reason: reason,
|
|
506
554
|
trigger_error: compactError(error, "RECRUIT_CONTEXT_RECOVERY_TRIGGER"),
|
|
555
|
+
account_rights_panel_close: compactCloseResult(blockingPanelClose),
|
|
507
556
|
elapsed_ms: Date.now() - started
|
|
508
557
|
};
|
|
509
558
|
refreshAttempts.push(compactRefresh);
|
|
@@ -525,6 +574,11 @@ export async function runRecruitWorkflow({
|
|
|
525
574
|
});
|
|
526
575
|
throw new Error(`Recruit context recovery failed after ${reason}: ${refreshResult.application?.reason || "refresh returned no cards"}`);
|
|
527
576
|
}
|
|
577
|
+
if (!blockingPanelClose?.closed) {
|
|
578
|
+
const panelError = createRecruitBlockingPanelCloseFailureError(blockingPanelClose, `recover:${reason}`);
|
|
579
|
+
panelError.refresh_attempt = compactRefresh;
|
|
580
|
+
throw panelError;
|
|
581
|
+
}
|
|
528
582
|
rootState = await getRecruitRoots(client);
|
|
529
583
|
rootState = await ensureRecruitViewport(rootState, "recover_after");
|
|
530
584
|
cardNodeIds = await waitForRecruitCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
@@ -553,11 +607,12 @@ export async function runRecruitWorkflow({
|
|
|
553
607
|
|
|
554
608
|
runControl.setPhase("recruit:cleanup");
|
|
555
609
|
await closeRecruitDetail(client, { attemptsLimit: 2 });
|
|
610
|
+
await closeRecruitBlockingPanelsForRun("cleanup");
|
|
556
611
|
|
|
557
612
|
await runControl.waitIfPaused();
|
|
558
613
|
runControl.throwIfCanceled();
|
|
559
614
|
runControl.setPhase("recruit:roots");
|
|
560
|
-
|
|
615
|
+
rootState = await getRecruitRoots(client);
|
|
561
616
|
rootState = await ensureRecruitViewport(rootState, "roots");
|
|
562
617
|
runControl.checkpoint({
|
|
563
618
|
iframe_selector: rootState.iframe.selector,
|
|
@@ -732,6 +787,25 @@ export async function runRecruitWorkflow({
|
|
|
732
787
|
runControl.setPhase("recruit:detail");
|
|
733
788
|
detailStep = "ensure_viewport";
|
|
734
789
|
rootState = await ensureRecruitViewport(rootState, "detail");
|
|
790
|
+
const blockingPanelClose = await closeRecruitBlockingPanels(client, {
|
|
791
|
+
attemptsLimit: 2,
|
|
792
|
+
rootState
|
|
793
|
+
});
|
|
794
|
+
if (!blockingPanelClose?.closed) {
|
|
795
|
+
const panelError = createRecruitBlockingPanelCloseFailureError(
|
|
796
|
+
blockingPanelClose,
|
|
797
|
+
"before_detail_open"
|
|
798
|
+
);
|
|
799
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
800
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error: panelError });
|
|
801
|
+
await recoverAndReapplyRecruitContext("account_rights_panel_before_detail", panelError, {
|
|
802
|
+
forceRecentViewed: true
|
|
803
|
+
});
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (blockingPanelClose.already_closed === false) {
|
|
807
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
808
|
+
}
|
|
735
809
|
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep });
|
|
736
810
|
detailStep = "open_detail";
|
|
737
811
|
networkRecorder.clear();
|
|
@@ -835,6 +909,7 @@ export async function runRecruitWorkflow({
|
|
|
835
909
|
timings.image_capture_recovery_trigger = compactError(error, "IMAGE_CAPTURE_FAILED");
|
|
836
910
|
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
837
911
|
await closeRecruitDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
912
|
+
await closeRecruitBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
838
913
|
await recoverAndReapplyRecruitContext(`image_capture:${detailStep}`, error, {
|
|
839
914
|
forceRecentViewed: true
|
|
840
915
|
});
|
|
@@ -907,6 +982,7 @@ export async function runRecruitWorkflow({
|
|
|
907
982
|
timings.detail_recovery_trigger = compactRecoverableDetailError(error);
|
|
908
983
|
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
909
984
|
await closeRecruitDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
985
|
+
await closeRecruitBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
910
986
|
await recoverAndReapplyRecruitContext(`detail:${detailStep}`, error, {
|
|
911
987
|
forceRecentViewed: true
|
|
912
988
|
});
|
|
@@ -916,6 +992,7 @@ export async function runRecruitWorkflow({
|
|
|
916
992
|
detailResult = null;
|
|
917
993
|
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
918
994
|
await closeRecruitDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
995
|
+
await closeRecruitBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
919
996
|
}
|
|
920
997
|
}
|
|
921
998
|
|