@reconcrap/boss-recommend-mcp 2.1.21 → 2.1.22
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/README.md +5 -2
- package/bin/boss-recommend-mcp.js +4 -4
- package/config/screening-config.example.json +33 -33
- package/package.json +8 -8
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +42 -42
- package/skills/boss-chat/SKILL.md +106 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +219 -214
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +127 -127
- package/src/chat-runtime-config.js +775 -775
- package/src/cli.js +573 -573
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +2419 -2415
- package/src/core/capture/index.js +1201 -1201
- package/src/core/cv-acquisition/index.js +238 -238
- package/src/core/cv-capture-target/index.js +299 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +1326 -1326
- package/src/core/reporting/legacy-csv.js +334 -332
- package/src/core/run/index.js +32 -32
- package/src/core/run/timing.js +33 -33
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +973 -973
- package/src/core/self-heal/viewport.js +564 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +113 -113
- package/src/domains/chat/index.js +7 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +122 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +571 -571
- package/src/domains/common/account-rights-panel.js +314 -314
- package/src/domains/common/recovery-settle.js +159 -159
- package/src/domains/recommend/actions.js +472 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +228 -159
- package/src/domains/recommend/detail.js +650 -650
- package/src/domains/recommend/filters.js +748 -377
- package/src/domains/recommend/index.js +4 -3
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -0
- package/src/domains/recommend/refresh.js +504 -361
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +987 -854
- package/src/domains/recommend/scopes.js +246 -246
- package/src/domains/recruit/actions.js +277 -277
- package/src/domains/recruit/cards.js +74 -74
- package/src/domains/recruit/constants.js +236 -236
- package/src/domains/recruit/detail.js +588 -588
- package/src/domains/recruit/index.js +9 -9
- package/src/domains/recruit/instruction-parser.js +866 -866
- package/src/domains/recruit/refresh.js +45 -45
- package/src/domains/recruit/roots.js +68 -68
- package/src/domains/recruit/run-service.js +1620 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +13 -0
- package/src/parser.js +376 -8
- package/src/recommend-mcp.js +929 -915
- package/src/recommend-scheduler.js +496 -496
- package/src/recruit-mcp.js +2121 -2121
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
} from "../../core/run/timing.js";
|
|
9
9
|
import { captureScrolledNodeScreenshots } from "../../core/capture/index.js";
|
|
10
10
|
import { waitForCvCaptureTarget } from "../../core/cv-capture-target/index.js";
|
|
11
|
-
import {
|
|
12
|
-
configureHumanInteraction,
|
|
13
|
-
createHumanRestController,
|
|
14
|
-
humanDelay,
|
|
15
|
-
normalizeHumanBehaviorOptions,
|
|
16
|
-
sleep
|
|
17
|
-
} from "../../core/browser/index.js";
|
|
11
|
+
import {
|
|
12
|
+
configureHumanInteraction,
|
|
13
|
+
createHumanRestController,
|
|
14
|
+
humanDelay,
|
|
15
|
+
normalizeHumanBehaviorOptions,
|
|
16
|
+
sleep
|
|
17
|
+
} from "../../core/browser/index.js";
|
|
18
18
|
import { GREET_CREDITS_EXHAUSTED_CODE } from "../../core/greet-quota/index.js";
|
|
19
19
|
import {
|
|
20
20
|
compactCvAcquisitionState,
|
|
@@ -38,41 +38,43 @@ import {
|
|
|
38
38
|
resolveInfiniteListFallbackPoint
|
|
39
39
|
} from "../../core/infinite-list/index.js";
|
|
40
40
|
import { createViewportRunGuard } from "../../core/self-heal/index.js";
|
|
41
|
-
import {
|
|
42
|
-
callScreeningLlm,
|
|
43
|
-
compactScreeningLlmResult,
|
|
44
|
-
createFatalLlmRunError,
|
|
45
|
-
createFailedLlmScreeningResult,
|
|
46
|
-
isFatalLlmProviderError,
|
|
47
|
-
llmResultToScreening,
|
|
48
|
-
screenCandidate
|
|
49
|
-
} from "../../core/screening/index.js";
|
|
50
|
-
import {
|
|
51
|
-
closeRecommendBlockingPanels,
|
|
52
|
-
closeRecommendAvatarPreview,
|
|
53
|
-
closeRecommendDetail,
|
|
54
|
-
createRecommendDetailNetworkRecorder,
|
|
55
|
-
extractRecommendDetailCandidate,
|
|
56
|
-
isRecommendDetailOpenMissError,
|
|
57
|
-
isStaleRecommendNodeError,
|
|
58
|
-
openRecommendCardDetailWithFreshRetry,
|
|
59
|
-
waitForRecommendDetailNetworkEvents
|
|
60
|
-
} from "./detail.js";
|
|
61
41
|
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
42
|
+
callScreeningLlm,
|
|
43
|
+
compactScreeningLlmResult,
|
|
44
|
+
createFatalLlmRunError,
|
|
45
|
+
createFailedLlmScreeningResult,
|
|
46
|
+
isFatalLlmProviderError,
|
|
47
|
+
llmResultToScreening,
|
|
48
|
+
screenCandidate
|
|
49
|
+
} from "../../core/screening/index.js";
|
|
66
50
|
import {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
closeRecommendBlockingPanels,
|
|
52
|
+
closeRecommendAvatarPreview,
|
|
53
|
+
closeRecommendDetail,
|
|
54
|
+
createRecommendDetailNetworkRecorder,
|
|
55
|
+
extractRecommendDetailCandidate,
|
|
56
|
+
isRecommendDetailOpenMissError,
|
|
57
|
+
isStaleRecommendNodeError,
|
|
58
|
+
openRecommendCardDetailWithFreshRetry,
|
|
59
|
+
waitForRecommendDetailNetworkEvents
|
|
60
|
+
} from "./detail.js";
|
|
71
61
|
import {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} from "./
|
|
75
|
-
import {
|
|
62
|
+
readRecommendCardCandidate,
|
|
63
|
+
waitForRecommendCardNodeIds
|
|
64
|
+
} from "./cards.js";
|
|
65
|
+
import { selectAndConfirmFirstSafeFilter } from "./filters.js";
|
|
66
|
+
import { ensureRecommendCurrentCityOnly } from "./location.js";
|
|
67
|
+
import {
|
|
68
|
+
applyRecommendFilterEnvelopeStages,
|
|
69
|
+
buildRecommendFilterSelectionOptions,
|
|
70
|
+
refreshRecommendListAtEnd
|
|
71
|
+
} from "./refresh.js";
|
|
72
|
+
import { selectRecommendJob } from "./jobs.js";
|
|
73
|
+
import {
|
|
74
|
+
normalizeRecommendPageScope,
|
|
75
|
+
selectRecommendPageScope
|
|
76
|
+
} from "./scopes.js";
|
|
77
|
+
import { inspectRecentColleagueContact } from "./colleague-contact.js";
|
|
76
78
|
import {
|
|
77
79
|
RECOMMEND_BOTTOM_MARKER_SELECTORS,
|
|
78
80
|
RECOMMEND_CARD_SELECTOR,
|
|
@@ -104,26 +106,46 @@ function isRefreshableListStall(reason = "") {
|
|
|
104
106
|
function normalizeFilter(filter = {}) {
|
|
105
107
|
const filterGroups = Array.isArray(filter.filterGroups)
|
|
106
108
|
? filter.filterGroups
|
|
107
|
-
: Array.isArray(filter.groups)
|
|
108
|
-
? filter.groups
|
|
109
|
-
: [];
|
|
110
|
-
return {
|
|
111
|
-
enabled: filter.enabled !== false,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
109
|
+
: Array.isArray(filter.groups)
|
|
110
|
+
? filter.groups
|
|
111
|
+
: [];
|
|
112
|
+
return {
|
|
113
|
+
enabled: filter.enabled !== false,
|
|
114
|
+
currentCityOnly: filter.currentCityOnly === true || filter.current_city_only === true,
|
|
115
|
+
group: String(filter.group || ""),
|
|
116
|
+
labels: normalizeLabels(filter.labels || filter.filterLabels || []),
|
|
117
|
+
selectAllLabels: Boolean(filter.selectAllLabels),
|
|
118
|
+
allowUnlimited: filter.allowUnlimited === true,
|
|
119
|
+
verifySticky: filter.verifySticky === true,
|
|
120
|
+
filterGroups: filterGroups.map((group) => ({
|
|
121
|
+
group: String(group?.group || ""),
|
|
122
|
+
labels: normalizeLabels(group?.labels || group?.filterLabels || []),
|
|
123
|
+
selectAllLabels: group?.selectAllLabels !== false,
|
|
124
|
+
allowUnlimited: group?.allowUnlimited === true,
|
|
125
|
+
verifySticky: group?.verifySticky === true
|
|
126
|
+
})).filter((group) => group.group || group.labels.length)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function compactFilterResult(filterResult) {
|
|
131
|
+
if (!filterResult) return null;
|
|
132
|
+
return {
|
|
133
|
+
opened_panel: Boolean(filterResult.opened_panel),
|
|
134
|
+
requested_groups: (filterResult.requested_groups || []).map((group) => ({
|
|
135
|
+
group: group.group,
|
|
136
|
+
labels: group.labels || [],
|
|
137
|
+
select_all_labels: group.select_all_labels !== false,
|
|
138
|
+
allow_unlimited: Boolean(group.allow_unlimited),
|
|
139
|
+
verify_sticky: Boolean(group.verify_sticky)
|
|
140
|
+
})),
|
|
141
|
+
effective_groups: (filterResult.sticky_verification?.groups || []).map((group) => ({
|
|
142
|
+
group: group.group,
|
|
143
|
+
requested_labels: group.requested_labels || [],
|
|
144
|
+
active_labels: group.active_labels || [],
|
|
145
|
+
verified: Boolean(group.verified),
|
|
146
|
+
unavailable: Boolean(group.unavailable),
|
|
147
|
+
reason: group.reason || null
|
|
148
|
+
})),
|
|
127
149
|
selected_option: filterResult.selected_option
|
|
128
150
|
? {
|
|
129
151
|
group: filterResult.selected_option.group,
|
|
@@ -133,49 +155,98 @@ function compactFilterResult(filterResult) {
|
|
|
133
155
|
}
|
|
134
156
|
: null,
|
|
135
157
|
selected_options: (filterResult.selected_options || []).map((option) => ({
|
|
136
|
-
group: option.group,
|
|
137
|
-
label: option.label,
|
|
138
|
-
was_active: Boolean(option.was_active),
|
|
139
|
-
clicked: option.clicked !== false
|
|
140
|
-
})),
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
group: option.group,
|
|
159
|
+
label: option.label,
|
|
160
|
+
was_active: Boolean(option.was_active),
|
|
161
|
+
clicked: option.clicked !== false
|
|
162
|
+
})),
|
|
163
|
+
unavailable: Boolean(filterResult.unavailable),
|
|
164
|
+
unavailable_groups: filterResult.unavailable_groups || [],
|
|
165
|
+
confirmed: Boolean(filterResult.confirmed),
|
|
166
|
+
sticky_verification: filterResult.sticky_verification || null,
|
|
167
|
+
attempts: {
|
|
168
|
+
initial_close: filterResult.initial_close_attempts || [],
|
|
169
|
+
open: (filterResult.open_attempts || []).map((attempt) => ({
|
|
170
|
+
selector: attempt.selector || null,
|
|
171
|
+
node_id: attempt.node_id || null,
|
|
172
|
+
click_target: attempt.click_target || null
|
|
173
|
+
})),
|
|
174
|
+
confirmation: (filterResult.confirm_attempts || []).map((attempt) => ({
|
|
175
|
+
node_id: attempt.node_id || null,
|
|
176
|
+
label: attempt.label || null,
|
|
177
|
+
clicked: Boolean(attempt.clicked),
|
|
178
|
+
errors: (attempt.errors || []).map((error) => ({
|
|
179
|
+
node_id: error.node_id || null,
|
|
180
|
+
message: error.message || String(error)
|
|
181
|
+
}))
|
|
182
|
+
}))
|
|
183
|
+
},
|
|
184
|
+
before_counts: filterResult.before_counts,
|
|
185
|
+
after_confirm_counts: filterResult.after_confirm_counts
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function compactCurrentCityOnlyResult(result) {
|
|
190
|
+
if (!result) return null;
|
|
191
|
+
return {
|
|
192
|
+
requested: Boolean(result.requested),
|
|
193
|
+
effective: typeof result.effective === "boolean" ? result.effective : null,
|
|
194
|
+
available: result.available !== false,
|
|
195
|
+
unavailable: Boolean(result.unavailable),
|
|
196
|
+
reason: result.reason || null,
|
|
197
|
+
clicked: Boolean(result.clicked),
|
|
198
|
+
current_city_label: result.current_city_label || null,
|
|
199
|
+
before: result.before || null,
|
|
200
|
+
after_toggle: result.after_toggle || null,
|
|
201
|
+
confirmation: result.confirmation || null,
|
|
202
|
+
sticky_verification: result.sticky_verification
|
|
163
203
|
? {
|
|
164
|
-
verified: Boolean(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ok: Boolean(jobSelection.sticky_verification.menu_close.ok),
|
|
172
|
-
closed: Boolean(jobSelection.sticky_verification.menu_close.closed),
|
|
173
|
-
reason: jobSelection.sticky_verification.menu_close.reason || ""
|
|
174
|
-
}
|
|
175
|
-
: null
|
|
204
|
+
verified: Boolean(result.sticky_verification.verified),
|
|
205
|
+
expected: Boolean(result.sticky_verification.expected),
|
|
206
|
+
actual: typeof result.sticky_verification.actual === "boolean"
|
|
207
|
+
? result.sticky_verification.actual
|
|
208
|
+
: null,
|
|
209
|
+
state_source: result.sticky_verification.state_source || null,
|
|
210
|
+
close_confirmation: result.sticky_verification.close_confirmation || null
|
|
176
211
|
}
|
|
177
212
|
: null,
|
|
178
|
-
|
|
213
|
+
attempts: Array.isArray(result.attempts) ? result.attempts : [],
|
|
214
|
+
evidence: result.evidence || null
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function compactJobSelection(jobSelection) {
|
|
219
|
+
if (!jobSelection) return null;
|
|
220
|
+
return {
|
|
221
|
+
requested: jobSelection.requested || "",
|
|
222
|
+
selected: Boolean(jobSelection.selected),
|
|
223
|
+
already_current: Boolean(jobSelection.already_current),
|
|
224
|
+
reason: jobSelection.reason || null,
|
|
225
|
+
selected_option: jobSelection.selected_option || null,
|
|
226
|
+
menu_close: jobSelection.menu_close
|
|
227
|
+
? {
|
|
228
|
+
ok: Boolean(jobSelection.menu_close.ok),
|
|
229
|
+
closed: Boolean(jobSelection.menu_close.closed),
|
|
230
|
+
reason: jobSelection.menu_close.reason || ""
|
|
231
|
+
}
|
|
232
|
+
: null,
|
|
233
|
+
sticky_verification: jobSelection.sticky_verification
|
|
234
|
+
? {
|
|
235
|
+
verified: Boolean(jobSelection.sticky_verification.verified),
|
|
236
|
+
current_label: jobSelection.sticky_verification.current_label_without_salary
|
|
237
|
+
|| jobSelection.sticky_verification.current_label
|
|
238
|
+
|| "",
|
|
239
|
+
visible_option_count: jobSelection.sticky_verification.visible_option_count || 0,
|
|
240
|
+
menu_close: jobSelection.sticky_verification.menu_close
|
|
241
|
+
? {
|
|
242
|
+
ok: Boolean(jobSelection.sticky_verification.menu_close.ok),
|
|
243
|
+
closed: Boolean(jobSelection.sticky_verification.menu_close.closed),
|
|
244
|
+
reason: jobSelection.sticky_verification.menu_close.reason || ""
|
|
245
|
+
}
|
|
246
|
+
: null
|
|
247
|
+
}
|
|
248
|
+
: null,
|
|
249
|
+
options: (jobSelection.options || []).map((option) => ({
|
|
179
250
|
label: option.label,
|
|
180
251
|
label_without_salary: option.label_without_salary,
|
|
181
252
|
current: Boolean(option.current),
|
|
@@ -225,20 +296,20 @@ function compactCandidate(candidate) {
|
|
|
225
296
|
};
|
|
226
297
|
}
|
|
227
298
|
|
|
228
|
-
function compactDetail(detailResult) {
|
|
229
|
-
if (!detailResult) return null;
|
|
230
|
-
return {
|
|
231
|
-
popup_text_length: detailResult.detail?.popup_text?.length || 0,
|
|
232
|
-
resume_text_length: detailResult.detail?.resume_text?.length || 0,
|
|
299
|
+
function compactDetail(detailResult) {
|
|
300
|
+
if (!detailResult) return null;
|
|
301
|
+
return {
|
|
302
|
+
popup_text_length: detailResult.detail?.popup_text?.length || 0,
|
|
303
|
+
resume_text_length: detailResult.detail?.resume_text?.length || 0,
|
|
233
304
|
network_body_count: detailResult.network_bodies?.filter((item) => item.body).length || 0,
|
|
234
305
|
parsed_network_profile_count: detailResult.parsed_network_profiles?.filter((item) => item.ok).length || 0,
|
|
235
|
-
cv_acquisition: detailResult.cv_acquisition || null,
|
|
236
|
-
colleague_contact: detailResult.colleague_contact || null,
|
|
237
|
-
image_evidence: summarizeImageEvidence(detailResult.image_evidence),
|
|
238
|
-
llm_screening: compactScreeningLlmResult(detailResult.llm_result),
|
|
239
|
-
close_result: detailResult.close_result
|
|
240
|
-
};
|
|
241
|
-
}
|
|
306
|
+
cv_acquisition: detailResult.cv_acquisition || null,
|
|
307
|
+
colleague_contact: detailResult.colleague_contact || null,
|
|
308
|
+
image_evidence: summarizeImageEvidence(detailResult.image_evidence),
|
|
309
|
+
llm_screening: compactScreeningLlmResult(detailResult.llm_result),
|
|
310
|
+
close_result: detailResult.close_result
|
|
311
|
+
};
|
|
312
|
+
}
|
|
242
313
|
|
|
243
314
|
function normalizeScreeningMode(value) {
|
|
244
315
|
const normalized = String(value || "llm").trim().toLowerCase();
|
|
@@ -287,22 +358,22 @@ async function runRecommendPostAction({
|
|
|
287
358
|
reason: ""
|
|
288
359
|
};
|
|
289
360
|
|
|
290
|
-
if (!screening?.passed) {
|
|
291
|
-
result.reason = "screening_not_passed";
|
|
292
|
-
return result;
|
|
293
|
-
}
|
|
294
|
-
if (plan.effective === "none") {
|
|
295
|
-
result.reason = plan.reason === "greet_limit_reached" ? "greet_limit_reached" : "post_action_none";
|
|
296
|
-
return result;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const summary = actionDiscovery?.summary || {};
|
|
300
|
-
const control = summary.greet;
|
|
301
|
-
if (!control?.found) {
|
|
302
|
-
result.reason = `${plan.effective}_control_not_found`;
|
|
303
|
-
return result;
|
|
304
|
-
}
|
|
305
|
-
result.control = control;
|
|
361
|
+
if (!screening?.passed) {
|
|
362
|
+
result.reason = "screening_not_passed";
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
if (plan.effective === "none") {
|
|
366
|
+
result.reason = plan.reason === "greet_limit_reached" ? "greet_limit_reached" : "post_action_none";
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const summary = actionDiscovery?.summary || {};
|
|
371
|
+
const control = summary.greet;
|
|
372
|
+
if (!control?.found) {
|
|
373
|
+
result.reason = `${plan.effective}_control_not_found`;
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
result.control = control;
|
|
306
377
|
|
|
307
378
|
if (plan.effective === "greet" && control.continue_chat) {
|
|
308
379
|
result.reason = "already_connected_continue_chat";
|
|
@@ -319,10 +390,10 @@ async function runRecommendPostAction({
|
|
|
319
390
|
result.reason = "greet_control_not_available";
|
|
320
391
|
return result;
|
|
321
392
|
}
|
|
322
|
-
if (control.disabled) {
|
|
323
|
-
result.reason = `${plan.effective}_control_disabled`;
|
|
324
|
-
return result;
|
|
325
|
-
}
|
|
393
|
+
if (control.disabled) {
|
|
394
|
+
result.reason = `${plan.effective}_control_disabled`;
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
326
397
|
if (!executePostAction) {
|
|
327
398
|
result.reason = "dry_run_post_action";
|
|
328
399
|
result.would_click = true;
|
|
@@ -357,18 +428,18 @@ async function runRecommendPostAction({
|
|
|
357
428
|
timeoutMs: 2500,
|
|
358
429
|
intervalMs: 300,
|
|
359
430
|
requireAny: false
|
|
360
|
-
});
|
|
361
|
-
const afterSummary = afterDiscovery?.summary || {};
|
|
362
|
-
const afterControl = afterSummary.greet;
|
|
363
|
-
result.action_discovery_after = compactActionDiscovery(afterDiscovery);
|
|
364
|
-
result.control_after = afterControl || null;
|
|
365
|
-
if (plan.effective === "greet") {
|
|
366
|
-
result.verified_after_click = Boolean(
|
|
367
|
-
afterControl?.continue_chat
|
|
368
|
-
|| String(afterControl?.label || "").includes("继续沟通")
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
} catch (error) {
|
|
431
|
+
});
|
|
432
|
+
const afterSummary = afterDiscovery?.summary || {};
|
|
433
|
+
const afterControl = afterSummary.greet;
|
|
434
|
+
result.action_discovery_after = compactActionDiscovery(afterDiscovery);
|
|
435
|
+
result.control_after = afterControl || null;
|
|
436
|
+
if (plan.effective === "greet") {
|
|
437
|
+
result.verified_after_click = Boolean(
|
|
438
|
+
afterControl?.continue_chat
|
|
439
|
+
|| String(afterControl?.label || "").includes("继续沟通")
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
372
443
|
result.verify_error = {
|
|
373
444
|
message: error?.message || String(error)
|
|
374
445
|
};
|
|
@@ -383,176 +454,195 @@ function compactRefreshAttempt(refreshAttempt) {
|
|
|
383
454
|
method: refreshAttempt.method || "",
|
|
384
455
|
reason: refreshAttempt.reason || null,
|
|
385
456
|
error: refreshAttempt.error || null,
|
|
386
|
-
forced_recent_not_view: Boolean(refreshAttempt.forced_recent_not_view),
|
|
387
|
-
target_url: refreshAttempt.target_url || null,
|
|
388
|
-
card_count: refreshAttempt.card_count || 0,
|
|
389
|
-
elapsed_ms: refreshAttempt.elapsed_ms || 0,
|
|
390
|
-
recovery_settle: refreshAttempt.recovery_settle
|
|
391
|
-
? {
|
|
392
|
-
ok: Boolean(refreshAttempt.recovery_settle.ok),
|
|
393
|
-
status: refreshAttempt.recovery_settle.status || "",
|
|
394
|
-
reason: refreshAttempt.recovery_settle.reason || "",
|
|
395
|
-
elapsed_ms: refreshAttempt.recovery_settle.elapsed_ms || 0
|
|
396
|
-
}
|
|
397
|
-
: null,
|
|
398
|
-
attempts: (refreshAttempt.attempts || []).map((attempt) => ({
|
|
457
|
+
forced_recent_not_view: Boolean(refreshAttempt.forced_recent_not_view),
|
|
458
|
+
target_url: refreshAttempt.target_url || null,
|
|
459
|
+
card_count: refreshAttempt.card_count || 0,
|
|
460
|
+
elapsed_ms: refreshAttempt.elapsed_ms || 0,
|
|
461
|
+
recovery_settle: refreshAttempt.recovery_settle
|
|
462
|
+
? {
|
|
463
|
+
ok: Boolean(refreshAttempt.recovery_settle.ok),
|
|
464
|
+
status: refreshAttempt.recovery_settle.status || "",
|
|
465
|
+
reason: refreshAttempt.recovery_settle.reason || "",
|
|
466
|
+
elapsed_ms: refreshAttempt.recovery_settle.elapsed_ms || 0
|
|
467
|
+
}
|
|
468
|
+
: null,
|
|
469
|
+
attempts: (refreshAttempt.attempts || []).map((attempt) => ({
|
|
399
470
|
ok: Boolean(attempt.ok),
|
|
400
471
|
method: attempt.method || "",
|
|
401
472
|
reason: attempt.reason || null,
|
|
402
|
-
error: attempt.error || null,
|
|
403
|
-
label: attempt.label || null,
|
|
473
|
+
error: attempt.error || null,
|
|
474
|
+
label: attempt.label || null,
|
|
404
475
|
before_card_count: attempt.before_card_count || 0,
|
|
405
476
|
after_card_count: attempt.after_card_count || 0,
|
|
406
477
|
card_count: attempt.card_count || 0,
|
|
407
|
-
elapsed_ms: attempt.elapsed_ms || 0
|
|
478
|
+
elapsed_ms: attempt.elapsed_ms || 0,
|
|
479
|
+
current_city_only: compactCurrentCityOnlyResult(attempt.current_city_only),
|
|
480
|
+
current_city_only_attempts: (attempt.current_city_only_attempts || []).map((cityAttempt) => ({
|
|
481
|
+
ok: Boolean(cityAttempt.ok),
|
|
482
|
+
method: cityAttempt.method || "current_city_only_reapply",
|
|
483
|
+
reason: cityAttempt.reason || null,
|
|
484
|
+
error: cityAttempt.error || null,
|
|
485
|
+
attempt: cityAttempt.attempt || 0,
|
|
486
|
+
result: compactCurrentCityOnlyResult(cityAttempt.result)
|
|
487
|
+
})),
|
|
488
|
+
filter: compactFilterResult(attempt.filter)
|
|
408
489
|
})),
|
|
409
|
-
|
|
490
|
+
current_city_only_attempts: (refreshAttempt.current_city_only_attempts || []).map((attempt) => ({
|
|
410
491
|
ok: Boolean(attempt.ok),
|
|
411
|
-
method: attempt.method || "
|
|
492
|
+
method: attempt.method || "current_city_only_reapply",
|
|
412
493
|
reason: attempt.reason || null,
|
|
413
494
|
error: attempt.error || null,
|
|
414
|
-
attempt: attempt.attempt || 0
|
|
495
|
+
attempt: attempt.attempt || 0,
|
|
496
|
+
result: compactCurrentCityOnlyResult(attempt.result)
|
|
415
497
|
})),
|
|
416
|
-
|
|
498
|
+
filter_reapply_attempts: (refreshAttempt.filter_reapply_attempts || []).map((attempt) => ({
|
|
417
499
|
ok: Boolean(attempt.ok),
|
|
418
|
-
method: attempt.method || "
|
|
500
|
+
method: attempt.method || "filter_reapply",
|
|
419
501
|
reason: attempt.reason || null,
|
|
420
|
-
error: attempt.error || null,
|
|
421
|
-
attempt: attempt.attempt || 0
|
|
422
|
-
|
|
502
|
+
error: attempt.error || null,
|
|
503
|
+
attempt: attempt.attempt || 0
|
|
504
|
+
})),
|
|
505
|
+
job_selection_attempts: (refreshAttempt.job_selection_attempts || []).map((attempt) => ({
|
|
506
|
+
ok: Boolean(attempt.ok),
|
|
507
|
+
method: attempt.method || "job_select",
|
|
508
|
+
reason: attempt.reason || null,
|
|
509
|
+
error: attempt.error || null,
|
|
510
|
+
attempt: attempt.attempt || 0,
|
|
511
|
+
iframe_document_node_id: attempt.iframe_document_node_id || 0,
|
|
423
512
|
selected: Boolean(attempt.selected),
|
|
424
513
|
selection_reason: attempt.selection_reason || null
|
|
425
514
|
})),
|
|
426
515
|
job_selection: compactJobSelection(refreshAttempt.job_selection),
|
|
427
516
|
page_scope: compactPageScopeSelection(refreshAttempt.page_scope),
|
|
517
|
+
current_city_only: compactCurrentCityOnlyResult(refreshAttempt.current_city_only),
|
|
428
518
|
filter: compactFilterResult(refreshAttempt.filter)
|
|
429
519
|
};
|
|
430
520
|
}
|
|
431
|
-
|
|
521
|
+
|
|
432
522
|
export function countRecommendResultStatuses(results = [], {
|
|
433
|
-
greetCount = 0
|
|
434
|
-
} = {}) {
|
|
435
|
-
return {
|
|
436
|
-
processed: results.length,
|
|
437
|
-
screened: results.length,
|
|
438
|
-
detail_opened: results.filter((item) => item.detail).length,
|
|
439
|
-
passed: results.filter((item) => item.screening?.passed).length,
|
|
440
|
-
skipped: results.filter((item) => item.screening?.passed === false).length,
|
|
441
|
-
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
442
|
-
greet_count: greetCount,
|
|
443
|
-
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
444
|
-
image_capture_failed: results.filter((item) => item.detail?.image_evidence?.ok === false).length,
|
|
523
|
+
greetCount = 0
|
|
524
|
+
} = {}) {
|
|
525
|
+
return {
|
|
526
|
+
processed: results.length,
|
|
527
|
+
screened: results.length,
|
|
528
|
+
detail_opened: results.filter((item) => item.detail).length,
|
|
529
|
+
passed: results.filter((item) => item.screening?.passed).length,
|
|
530
|
+
skipped: results.filter((item) => item.screening?.passed === false).length,
|
|
531
|
+
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
532
|
+
greet_count: greetCount,
|
|
533
|
+
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
534
|
+
image_capture_failed: results.filter((item) => item.detail?.image_evidence?.ok === false).length,
|
|
445
535
|
detail_open_failed: results.filter((item) => (
|
|
446
536
|
item.error?.code === "DETAIL_STALE_NODE"
|
|
447
537
|
|| item.error?.code === "DETAIL_OPEN_FAILED"
|
|
448
|
-
)).length,
|
|
449
|
-
transient_recovered: results.filter((item) => (
|
|
450
|
-
item.error?.code === "DETAIL_STALE_NODE"
|
|
451
|
-
|| item.error?.code === "DETAIL_OPEN_FAILED"
|
|
452
|
-
|| item.error?.code === "IMAGE_CAPTURE_STALE_NODE"
|
|
453
|
-
|| item.error?.code === "IMAGE_CAPTURE_TIMEOUT"
|
|
454
|
-
|| item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT"
|
|
455
|
-
)).length,
|
|
456
|
-
colleague_contact_checked: results.filter((item) => item.detail?.colleague_contact?.checked).length,
|
|
457
|
-
recent_colleague_contact_skipped: results.filter((item) => (
|
|
458
|
-
item.screening?.status === "skip"
|
|
459
|
-
&& item.screening?.reasons?.includes("skipped_recent_colleague_contact")
|
|
460
|
-
)).length,
|
|
461
|
-
colleague_contact_panel_missing: results.filter((item) => (
|
|
462
|
-
item.detail?.colleague_contact?.reason === "panel_missing"
|
|
463
|
-
)).length
|
|
464
|
-
};
|
|
465
|
-
}
|
|
538
|
+
)).length,
|
|
539
|
+
transient_recovered: results.filter((item) => (
|
|
540
|
+
item.error?.code === "DETAIL_STALE_NODE"
|
|
541
|
+
|| item.error?.code === "DETAIL_OPEN_FAILED"
|
|
542
|
+
|| item.error?.code === "IMAGE_CAPTURE_STALE_NODE"
|
|
543
|
+
|| item.error?.code === "IMAGE_CAPTURE_TIMEOUT"
|
|
544
|
+
|| item.error?.code === "IMAGE_CAPTURE_TOTAL_TIMEOUT"
|
|
545
|
+
)).length,
|
|
546
|
+
colleague_contact_checked: results.filter((item) => item.detail?.colleague_contact?.checked).length,
|
|
547
|
+
recent_colleague_contact_skipped: results.filter((item) => (
|
|
548
|
+
item.screening?.status === "skip"
|
|
549
|
+
&& item.screening?.reasons?.includes("skipped_recent_colleague_contact")
|
|
550
|
+
)).length,
|
|
551
|
+
colleague_contact_panel_missing: results.filter((item) => (
|
|
552
|
+
item.detail?.colleague_contact?.reason === "panel_missing"
|
|
553
|
+
)).length
|
|
554
|
+
};
|
|
555
|
+
}
|
|
466
556
|
|
|
467
|
-
function countPassedResults(results = []) {
|
|
468
|
-
return countRecommendResultStatuses(results).passed;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function compactCloseResult(closeResult) {
|
|
472
|
-
if (!closeResult) return null;
|
|
473
|
-
const result = {
|
|
474
|
-
closed: Boolean(closeResult.closed),
|
|
475
|
-
reason: closeResult.reason || null,
|
|
476
|
-
probe: closeResult.probe || null,
|
|
477
|
-
attempts: closeResult.attempts || [],
|
|
478
|
-
verification: closeResult.verification || null
|
|
479
|
-
};
|
|
480
|
-
if (closeResult.already_closed !== undefined) {
|
|
481
|
-
result.already_closed = Boolean(closeResult.already_closed);
|
|
482
|
-
}
|
|
483
|
-
return result;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
487
|
-
if (!error) return null;
|
|
488
|
-
const result = {
|
|
489
|
-
code: error.code || fallbackCode,
|
|
490
|
-
message: error.message || String(error)
|
|
491
|
-
};
|
|
492
|
-
if (error.close_result) {
|
|
493
|
-
result.close_result = compactCloseResult(error.close_result);
|
|
494
|
-
}
|
|
495
|
-
if (error.phase) {
|
|
496
|
-
result.phase = error.phase;
|
|
497
|
-
}
|
|
498
|
-
if (error.refresh_attempt) {
|
|
499
|
-
result.refresh_attempt = error.refresh_attempt;
|
|
500
|
-
}
|
|
501
|
-
if (error.list_end_reason) {
|
|
502
|
-
result.list_end_reason = error.list_end_reason;
|
|
503
|
-
}
|
|
504
|
-
if (error.target_count != null) {
|
|
505
|
-
result.target_count = error.target_count;
|
|
506
|
-
}
|
|
507
|
-
if (error.passed_count != null) {
|
|
508
|
-
result.passed_count = error.passed_count;
|
|
509
|
-
}
|
|
510
|
-
if (Array.isArray(error.recommend_detail_open_attempts)) {
|
|
511
|
-
result.recommend_detail_open_attempts = error.recommend_detail_open_attempts;
|
|
512
|
-
}
|
|
513
|
-
if (Array.isArray(error.click_attempts)) {
|
|
514
|
-
result.click_attempts = error.click_attempts;
|
|
515
|
-
}
|
|
516
|
-
if (error.avatar_preview) {
|
|
517
|
-
result.avatar_preview = {
|
|
518
|
-
open: Boolean(error.avatar_preview.open),
|
|
519
|
-
selector: error.avatar_preview.preview?.selector || null,
|
|
520
|
-
rect: error.avatar_preview.preview?.rect || null
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
return result;
|
|
524
|
-
}
|
|
557
|
+
function countPassedResults(results = []) {
|
|
558
|
+
return countRecommendResultStatuses(results).passed;
|
|
559
|
+
}
|
|
525
560
|
|
|
526
|
-
function
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
return
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
error.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
error.
|
|
554
|
-
|
|
555
|
-
}
|
|
561
|
+
function compactCloseResult(closeResult) {
|
|
562
|
+
if (!closeResult) return null;
|
|
563
|
+
const result = {
|
|
564
|
+
closed: Boolean(closeResult.closed),
|
|
565
|
+
reason: closeResult.reason || null,
|
|
566
|
+
probe: closeResult.probe || null,
|
|
567
|
+
attempts: closeResult.attempts || [],
|
|
568
|
+
verification: closeResult.verification || null
|
|
569
|
+
};
|
|
570
|
+
if (closeResult.already_closed !== undefined) {
|
|
571
|
+
result.already_closed = Boolean(closeResult.already_closed);
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function compactError(error, fallbackCode = "RECOMMEND_RUN_ERROR") {
|
|
577
|
+
if (!error) return null;
|
|
578
|
+
const result = {
|
|
579
|
+
code: error.code || fallbackCode,
|
|
580
|
+
message: error.message || String(error)
|
|
581
|
+
};
|
|
582
|
+
if (error.close_result) {
|
|
583
|
+
result.close_result = compactCloseResult(error.close_result);
|
|
584
|
+
}
|
|
585
|
+
if (error.phase) {
|
|
586
|
+
result.phase = error.phase;
|
|
587
|
+
}
|
|
588
|
+
if (error.refresh_attempt) {
|
|
589
|
+
result.refresh_attempt = error.refresh_attempt;
|
|
590
|
+
}
|
|
591
|
+
if (error.list_end_reason) {
|
|
592
|
+
result.list_end_reason = error.list_end_reason;
|
|
593
|
+
}
|
|
594
|
+
if (error.target_count != null) {
|
|
595
|
+
result.target_count = error.target_count;
|
|
596
|
+
}
|
|
597
|
+
if (error.passed_count != null) {
|
|
598
|
+
result.passed_count = error.passed_count;
|
|
599
|
+
}
|
|
600
|
+
if (Array.isArray(error.recommend_detail_open_attempts)) {
|
|
601
|
+
result.recommend_detail_open_attempts = error.recommend_detail_open_attempts;
|
|
602
|
+
}
|
|
603
|
+
if (Array.isArray(error.click_attempts)) {
|
|
604
|
+
result.click_attempts = error.click_attempts;
|
|
605
|
+
}
|
|
606
|
+
if (error.avatar_preview) {
|
|
607
|
+
result.avatar_preview = {
|
|
608
|
+
open: Boolean(error.avatar_preview.open),
|
|
609
|
+
selector: error.avatar_preview.preview?.selector || null,
|
|
610
|
+
rect: error.avatar_preview.preview?.rect || null
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function createRecommendCloseFailureError(closeResult) {
|
|
617
|
+
const error = new Error(closeResult?.reason || "Recommend detail did not close before recovery");
|
|
618
|
+
error.code = "DETAIL_CLOSE_FAILED";
|
|
619
|
+
error.close_result = closeResult || null;
|
|
620
|
+
return error;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function createRecommendBlockingPanelCloseFailureError(closeResult, phase = "") {
|
|
624
|
+
const error = new Error(closeResult?.reason || "Boss account-rights panel did not close before recovery");
|
|
625
|
+
error.code = "ACCOUNT_RIGHTS_PANEL_CLOSE_FAILED";
|
|
626
|
+
error.close_result = closeResult || null;
|
|
627
|
+
error.phase = phase || null;
|
|
628
|
+
return error;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function createRecommendRefreshFailureError(refreshAttempt, {
|
|
632
|
+
listEndReason = "",
|
|
633
|
+
targetCount = 0,
|
|
634
|
+
passedCount = 0
|
|
635
|
+
} = {}) {
|
|
636
|
+
const reason = refreshAttempt?.reason || "refresh_failed";
|
|
637
|
+
const detail = refreshAttempt?.error ? `: ${refreshAttempt.error}` : "";
|
|
638
|
+
const error = new Error(`Recommend refresh failed before target was reached (${reason}${detail})`);
|
|
639
|
+
error.code = "RECOMMEND_END_REFRESH_FAILED";
|
|
640
|
+
error.refresh_attempt = refreshAttempt || null;
|
|
641
|
+
error.list_end_reason = listEndReason || null;
|
|
642
|
+
error.target_count = targetCount;
|
|
643
|
+
error.passed_count = passedCount;
|
|
644
|
+
return error;
|
|
645
|
+
}
|
|
556
646
|
|
|
557
647
|
export function isRecoverableImageCaptureError(error) {
|
|
558
648
|
const code = String(error?.code || "");
|
|
@@ -615,41 +705,41 @@ function createImageCaptureFailureScreening(candidate, error) {
|
|
|
615
705
|
};
|
|
616
706
|
}
|
|
617
707
|
|
|
618
|
-
export function isRecoverableRecommendDetailError(error) {
|
|
619
|
-
return isStaleRecommendNodeError(error) || isRecommendDetailOpenMissError(error);
|
|
620
|
-
}
|
|
708
|
+
export function isRecoverableRecommendDetailError(error) {
|
|
709
|
+
return isStaleRecommendNodeError(error) || isRecommendDetailOpenMissError(error);
|
|
710
|
+
}
|
|
621
711
|
|
|
622
712
|
function compactRecoverableDetailError(error) {
|
|
623
713
|
return compactError(error, isStaleRecommendNodeError(error) ? "DETAIL_STALE_NODE" : "DETAIL_OPEN_FAILED");
|
|
624
714
|
}
|
|
625
715
|
|
|
626
|
-
function createRecoverableDetailFailureScreening(candidate, error) {
|
|
627
|
-
return {
|
|
628
|
-
status: "fail",
|
|
629
|
-
passed: false,
|
|
630
|
-
score: 0,
|
|
631
|
-
reasons: isStaleRecommendNodeError(error)
|
|
632
|
-
? ["detail_open_failed", "stale_node"]
|
|
633
|
-
: isRecommendDetailOpenMissError(error)
|
|
634
|
-
? ["detail_open_failed", "detail_open_miss"]
|
|
635
|
-
: ["detail_open_failed"],
|
|
716
|
+
function createRecoverableDetailFailureScreening(candidate, error) {
|
|
717
|
+
return {
|
|
718
|
+
status: "fail",
|
|
719
|
+
passed: false,
|
|
720
|
+
score: 0,
|
|
721
|
+
reasons: isStaleRecommendNodeError(error)
|
|
722
|
+
? ["detail_open_failed", "stale_node"]
|
|
723
|
+
: isRecommendDetailOpenMissError(error)
|
|
724
|
+
? ["detail_open_failed", "detail_open_miss"]
|
|
725
|
+
: ["detail_open_failed"],
|
|
636
726
|
error: compactRecoverableDetailError(error),
|
|
637
|
-
candidate
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
function createRecentColleagueContactSkipScreening(candidate, colleagueContact) {
|
|
642
|
-
const matched = colleagueContact?.matched_row || null;
|
|
643
|
-
return {
|
|
644
|
-
status: "skip",
|
|
645
|
-
passed: false,
|
|
646
|
-
score: 0,
|
|
647
|
-
reasons: ["skipped_recent_colleague_contact"],
|
|
648
|
-
reason: matched?.text || "Candidate has recent colleague contact history",
|
|
649
|
-
matched_colleague_contact: matched,
|
|
650
|
-
candidate
|
|
651
|
-
};
|
|
652
|
-
}
|
|
727
|
+
candidate
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function createRecentColleagueContactSkipScreening(candidate, colleagueContact) {
|
|
732
|
+
const matched = colleagueContact?.matched_row || null;
|
|
733
|
+
return {
|
|
734
|
+
status: "skip",
|
|
735
|
+
passed: false,
|
|
736
|
+
score: 0,
|
|
737
|
+
reasons: ["skipped_recent_colleague_contact"],
|
|
738
|
+
reason: matched?.text || "Candidate has recent colleague contact history",
|
|
739
|
+
matched_colleague_contact: matched,
|
|
740
|
+
candidate
|
|
741
|
+
};
|
|
742
|
+
}
|
|
653
743
|
|
|
654
744
|
export async function runRecommendWorkflow({
|
|
655
745
|
client,
|
|
@@ -681,47 +771,47 @@ export async function runRecommendWorkflow({
|
|
|
681
771
|
executePostAction = true,
|
|
682
772
|
actionTimeoutMs = 8000,
|
|
683
773
|
actionIntervalMs = 500,
|
|
684
|
-
actionAfterClickDelayMs = 900,
|
|
685
|
-
screeningMode = "llm",
|
|
686
|
-
llmConfig = null,
|
|
687
|
-
llmTimeoutMs = 120000,
|
|
688
|
-
llmImageLimit = 8,
|
|
689
|
-
llmImageDetail = "high",
|
|
690
|
-
imageOutputDir = "",
|
|
691
|
-
humanRestEnabled = false,
|
|
692
|
-
humanBehavior = null,
|
|
693
|
-
skipRecentColleagueContacted = true,
|
|
694
|
-
colleagueContactWindowDays = 14
|
|
695
|
-
} = {}, runControl) {
|
|
696
|
-
if (!client) throw new Error("runRecommendWorkflow requires a guarded CDP client");
|
|
697
|
-
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
698
|
-
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
699
|
-
});
|
|
700
|
-
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
701
|
-
configureHumanInteraction(client, {
|
|
702
|
-
enabled: effectiveHumanBehavior.enabled,
|
|
703
|
-
clickMovementEnabled: effectiveHumanBehavior.clickMovement,
|
|
704
|
-
textEntryEnabled: effectiveHumanBehavior.textEntry,
|
|
705
|
-
safeClickPointEnabled: effectiveHumanBehavior.clickMovement,
|
|
706
|
-
actionCooldownEnabled: effectiveHumanBehavior.actionCooldown
|
|
707
|
-
});
|
|
708
|
-
const humanRestController = createHumanRestController({
|
|
709
|
-
enabled: effectiveHumanRestEnabled,
|
|
710
|
-
shortRestEnabled: effectiveHumanBehavior.shortRest,
|
|
711
|
-
batchRestEnabled: effectiveHumanBehavior.batchRest,
|
|
712
|
-
restLevel: effectiveHumanBehavior.restLevel
|
|
713
|
-
});
|
|
714
|
-
const normalizedFilter = normalizeFilter(filter);
|
|
774
|
+
actionAfterClickDelayMs = 900,
|
|
775
|
+
screeningMode = "llm",
|
|
776
|
+
llmConfig = null,
|
|
777
|
+
llmTimeoutMs = 120000,
|
|
778
|
+
llmImageLimit = 8,
|
|
779
|
+
llmImageDetail = "high",
|
|
780
|
+
imageOutputDir = "",
|
|
781
|
+
humanRestEnabled = false,
|
|
782
|
+
humanBehavior = null,
|
|
783
|
+
skipRecentColleagueContacted = true,
|
|
784
|
+
colleagueContactWindowDays = 14
|
|
785
|
+
} = {}, runControl) {
|
|
786
|
+
if (!client) throw new Error("runRecommendWorkflow requires a guarded CDP client");
|
|
787
|
+
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
788
|
+
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
789
|
+
});
|
|
790
|
+
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
791
|
+
configureHumanInteraction(client, {
|
|
792
|
+
enabled: effectiveHumanBehavior.enabled,
|
|
793
|
+
clickMovementEnabled: effectiveHumanBehavior.clickMovement,
|
|
794
|
+
textEntryEnabled: effectiveHumanBehavior.textEntry,
|
|
795
|
+
safeClickPointEnabled: effectiveHumanBehavior.clickMovement,
|
|
796
|
+
actionCooldownEnabled: effectiveHumanBehavior.actionCooldown
|
|
797
|
+
});
|
|
798
|
+
const humanRestController = createHumanRestController({
|
|
799
|
+
enabled: effectiveHumanRestEnabled,
|
|
800
|
+
shortRestEnabled: effectiveHumanBehavior.shortRest,
|
|
801
|
+
batchRestEnabled: effectiveHumanBehavior.batchRest,
|
|
802
|
+
restLevel: effectiveHumanBehavior.restLevel
|
|
803
|
+
});
|
|
804
|
+
const normalizedFilter = normalizeFilter(filter);
|
|
715
805
|
const normalizedPostAction = normalizeRecommendPostAction(postAction) || "none";
|
|
716
806
|
const requestedPageScope = normalizeRecommendPageScope(pageScope) || "recommend";
|
|
717
807
|
const normalizedFallbackPageScope = normalizeRecommendPageScope(fallbackPageScope) || "recommend";
|
|
718
|
-
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
719
|
-
const useLlmScreening = normalizedScreeningMode !== "deterministic";
|
|
720
|
-
const postActionEnabled = normalizedPostAction !== "none";
|
|
721
|
-
const shouldSkipRecentColleagueContacted = skipRecentColleagueContacted !== false;
|
|
722
|
-
const normalizedColleagueContactWindowDays = Math.max(1, Number(colleagueContactWindowDays) || 14);
|
|
723
|
-
const colleagueContactReferenceDate = new Date();
|
|
724
|
-
const targetPassCount = Math.max(1, Number(maxCandidates) || 1);
|
|
808
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
809
|
+
const useLlmScreening = normalizedScreeningMode !== "deterministic";
|
|
810
|
+
const postActionEnabled = normalizedPostAction !== "none";
|
|
811
|
+
const shouldSkipRecentColleagueContacted = skipRecentColleagueContacted !== false;
|
|
812
|
+
const normalizedColleagueContactWindowDays = Math.max(1, Number(colleagueContactWindowDays) || 14);
|
|
813
|
+
const colleagueContactReferenceDate = new Date();
|
|
814
|
+
const targetPassCount = Math.max(1, Number(maxCandidates) || 1);
|
|
725
815
|
const detailCountLimit = detailLimit == null ? Number.POSITIVE_INFINITY : Math.max(0, Number(detailLimit) || 0);
|
|
726
816
|
const effectiveDetailLimit = postActionEnabled ? Number.POSITIVE_INFINITY : detailCountLimit;
|
|
727
817
|
const networkRecorder = effectiveDetailLimit > 0
|
|
@@ -747,59 +837,60 @@ export async function runRecommendWorkflow({
|
|
|
747
837
|
const results = [];
|
|
748
838
|
const refreshAttempts = [];
|
|
749
839
|
let refreshRounds = 0;
|
|
750
|
-
let contextRecoveryAttempts = 0;
|
|
751
|
-
let greetCount = 0;
|
|
840
|
+
let contextRecoveryAttempts = 0;
|
|
841
|
+
let greetCount = 0;
|
|
752
842
|
const candidateRecoveryCounts = new Map();
|
|
753
843
|
let jobSelection = null;
|
|
754
844
|
let pageScopeSelection = null;
|
|
845
|
+
let currentCityOnlyResult = null;
|
|
755
846
|
let filterResult = null;
|
|
756
847
|
let rootState = null;
|
|
757
848
|
let cardNodeIds = [];
|
|
758
849
|
let listEndReason = "";
|
|
759
850
|
let lastHumanEvent = null;
|
|
760
|
-
const listFallbackResolver = listFallbackPoint || (async ({ items = [] } = {}) => resolveInfiniteListFallbackPoint(client, {
|
|
851
|
+
const listFallbackResolver = listFallbackPoint || (async ({ items = [] } = {}) => resolveInfiniteListFallbackPoint(client, {
|
|
761
852
|
rootNodeId: rootState?.iframe?.documentNodeId,
|
|
762
853
|
containerSelectors: RECOMMEND_LIST_CONTAINER_SELECTORS,
|
|
763
854
|
itemNodeIds: items.map((item) => item.node_id).filter(Boolean),
|
|
764
855
|
itemSelectors: [RECOMMEND_CARD_SELECTOR],
|
|
765
856
|
viewportPoint: { xRatio: 0.28, yRatio: 0.5 },
|
|
766
857
|
validateViewportPoint: true
|
|
767
|
-
}));
|
|
768
|
-
|
|
769
|
-
function recordHumanEvent(event = null) {
|
|
770
|
-
if (!event) return lastHumanEvent;
|
|
771
|
-
lastHumanEvent = {
|
|
772
|
-
at: new Date().toISOString(),
|
|
773
|
-
...event
|
|
774
|
-
};
|
|
775
|
-
return lastHumanEvent;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
async function maybeHumanActionCooldown(phase, timings = {}) {
|
|
779
|
-
if (!effectiveHumanBehavior.actionCooldown) return null;
|
|
780
|
-
const pauseMs = humanDelay(280, 90, {
|
|
781
|
-
minMs: 80,
|
|
782
|
-
maxMs: 720
|
|
783
|
-
});
|
|
784
|
-
if (pauseMs > 0) {
|
|
785
|
-
await runControl.sleep(pauseMs);
|
|
786
|
-
addTiming(timings, `human_${phase}_pause_ms`, pauseMs);
|
|
787
|
-
}
|
|
788
|
-
return recordHumanEvent({
|
|
789
|
-
kind: "action_cooldown",
|
|
790
|
-
phase,
|
|
791
|
-
pause_ms: pauseMs
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
function updateRecommendProgress(extra = {}) {
|
|
796
|
-
const counts = countRecommendResultStatuses(results, { greetCount });
|
|
797
|
-
const listSnapshot = compactInfiniteListState(listState);
|
|
798
|
-
const humanRestState = humanRestController.getState();
|
|
799
|
-
runControl.updateProgress({
|
|
800
|
-
card_count: cardNodeIds.length,
|
|
801
|
-
target_count: targetPassCount,
|
|
802
|
-
target_count_semantics: "passed_candidates",
|
|
858
|
+
}));
|
|
859
|
+
|
|
860
|
+
function recordHumanEvent(event = null) {
|
|
861
|
+
if (!event) return lastHumanEvent;
|
|
862
|
+
lastHumanEvent = {
|
|
863
|
+
at: new Date().toISOString(),
|
|
864
|
+
...event
|
|
865
|
+
};
|
|
866
|
+
return lastHumanEvent;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
async function maybeHumanActionCooldown(phase, timings = {}) {
|
|
870
|
+
if (!effectiveHumanBehavior.actionCooldown) return null;
|
|
871
|
+
const pauseMs = humanDelay(280, 90, {
|
|
872
|
+
minMs: 80,
|
|
873
|
+
maxMs: 720
|
|
874
|
+
});
|
|
875
|
+
if (pauseMs > 0) {
|
|
876
|
+
await runControl.sleep(pauseMs);
|
|
877
|
+
addTiming(timings, `human_${phase}_pause_ms`, pauseMs);
|
|
878
|
+
}
|
|
879
|
+
return recordHumanEvent({
|
|
880
|
+
kind: "action_cooldown",
|
|
881
|
+
phase,
|
|
882
|
+
pause_ms: pauseMs
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function updateRecommendProgress(extra = {}) {
|
|
887
|
+
const counts = countRecommendResultStatuses(results, { greetCount });
|
|
888
|
+
const listSnapshot = compactInfiniteListState(listState);
|
|
889
|
+
const humanRestState = humanRestController.getState();
|
|
890
|
+
runControl.updateProgress({
|
|
891
|
+
card_count: cardNodeIds.length,
|
|
892
|
+
target_count: targetPassCount,
|
|
893
|
+
target_count_semantics: "passed_candidates",
|
|
803
894
|
...counts,
|
|
804
895
|
screening_mode: normalizedScreeningMode,
|
|
805
896
|
unique_seen: listSnapshot.seen_count,
|
|
@@ -807,25 +898,28 @@ export async function runRecommendWorkflow({
|
|
|
807
898
|
refresh_rounds: refreshRounds,
|
|
808
899
|
refresh_attempts: refreshAttempts.length,
|
|
809
900
|
context_recoveries: contextRecoveryAttempts,
|
|
810
|
-
list_end_reason: listEndReason || null,
|
|
811
|
-
viewport_checks: viewportGuard.getStats().checks,
|
|
812
|
-
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
813
|
-
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
901
|
+
list_end_reason: listEndReason || null,
|
|
902
|
+
viewport_checks: viewportGuard.getStats().checks,
|
|
903
|
+
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
904
|
+
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
814
905
|
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
815
906
|
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
816
907
|
human_rest_enabled: effectiveHumanRestEnabled,
|
|
817
908
|
skip_recent_colleague_contacted: shouldSkipRecentColleagueContacted,
|
|
818
909
|
colleague_contact_window_days: normalizedColleagueContactWindowDays,
|
|
910
|
+
current_city_only_requested: normalizedFilter.currentCityOnly,
|
|
911
|
+
current_city_only_effective: currentCityOnlyResult?.effective ?? null,
|
|
912
|
+
current_city_only_unavailable: Boolean(currentCityOnlyResult?.unavailable),
|
|
819
913
|
human_rest_count: humanRestState.rest_count,
|
|
820
914
|
human_rest_ms: humanRestState.total_rest_ms,
|
|
821
915
|
last_human_event: lastHumanEvent,
|
|
822
916
|
...extra
|
|
823
917
|
});
|
|
824
|
-
}
|
|
918
|
+
}
|
|
825
919
|
|
|
826
|
-
function checkpointInProgressCandidate({
|
|
827
|
-
index = results.length,
|
|
828
|
-
candidateKey = "",
|
|
920
|
+
function checkpointInProgressCandidate({
|
|
921
|
+
index = results.length,
|
|
922
|
+
candidateKey = "",
|
|
829
923
|
cardNodeId = null,
|
|
830
924
|
detailStep = "",
|
|
831
925
|
error = null
|
|
@@ -839,58 +933,59 @@ export async function runRecommendWorkflow({
|
|
|
839
933
|
counters: countRecommendResultStatuses(results, { greetCount }),
|
|
840
934
|
error: compactError(error, "RECOMMEND_IN_PROGRESS_ERROR")
|
|
841
935
|
},
|
|
842
|
-
candidate_list: compactInfiniteListState(listState)
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
async function closeRecommendBlockingPanelsForRun(phase = "cleanup") {
|
|
847
|
-
const result = await closeRecommendBlockingPanels(client, {
|
|
848
|
-
attemptsLimit: 2,
|
|
849
|
-
rootState
|
|
850
|
-
});
|
|
851
|
-
if (!result?.closed) {
|
|
852
|
-
throw createRecommendBlockingPanelCloseFailureError(result, phase);
|
|
853
|
-
}
|
|
854
|
-
return result;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
async function recoverAndReapplyRecommendContext(reason = "context_recovery", error = null, {
|
|
936
|
+
candidate_list: compactInfiniteListState(listState)
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
async function closeRecommendBlockingPanelsForRun(phase = "cleanup") {
|
|
941
|
+
const result = await closeRecommendBlockingPanels(client, {
|
|
942
|
+
attemptsLimit: 2,
|
|
943
|
+
rootState
|
|
944
|
+
});
|
|
945
|
+
if (!result?.closed) {
|
|
946
|
+
throw createRecommendBlockingPanelCloseFailureError(result, phase);
|
|
947
|
+
}
|
|
948
|
+
return result;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
async function recoverAndReapplyRecommendContext(reason = "context_recovery", error = null, {
|
|
858
952
|
forceRecentNotView = true
|
|
859
953
|
} = {}) {
|
|
860
|
-
await runControl.waitIfPaused();
|
|
861
|
-
runControl.throwIfCanceled();
|
|
862
|
-
const started = Date.now();
|
|
863
|
-
runControl.setPhase("recommend:recover-context");
|
|
864
|
-
contextRecoveryAttempts += 1;
|
|
954
|
+
await runControl.waitIfPaused();
|
|
955
|
+
runControl.throwIfCanceled();
|
|
956
|
+
const started = Date.now();
|
|
957
|
+
runControl.setPhase("recommend:recover-context");
|
|
958
|
+
contextRecoveryAttempts += 1;
|
|
959
|
+
const effectiveForceRecentNotView = normalizedFilter.enabled && forceRecentNotView;
|
|
865
960
|
const refreshResult = await refreshRecommendListAtEnd(client, {
|
|
866
961
|
rootState,
|
|
867
962
|
jobLabel,
|
|
868
|
-
pageScope: pageScopeSelection?.effective_scope || requestedPageScope,
|
|
869
|
-
fallbackPageScope: normalizedFallbackPageScope,
|
|
870
|
-
filter: normalizedFilter,
|
|
871
|
-
preferEndRefreshButton: false,
|
|
872
|
-
forceNavigate: true,
|
|
873
|
-
targetUrl: targetUrl || RECOMMEND_TARGET_URL,
|
|
874
|
-
forceRecentNotView,
|
|
875
|
-
cardTimeoutMs,
|
|
963
|
+
pageScope: pageScopeSelection?.effective_scope || requestedPageScope,
|
|
964
|
+
fallbackPageScope: normalizedFallbackPageScope,
|
|
965
|
+
filter: normalizedFilter,
|
|
966
|
+
preferEndRefreshButton: false,
|
|
967
|
+
forceNavigate: true,
|
|
968
|
+
targetUrl: targetUrl || RECOMMEND_TARGET_URL,
|
|
969
|
+
forceRecentNotView: effectiveForceRecentNotView,
|
|
970
|
+
cardTimeoutMs,
|
|
876
971
|
buttonSettleMs: refreshButtonSettleMs,
|
|
877
972
|
reloadSettleMs: refreshReloadSettleMs
|
|
878
973
|
});
|
|
879
974
|
let blockingPanelClose = null;
|
|
880
|
-
if (refreshResult.ok) {
|
|
881
|
-
blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
882
|
-
attemptsLimit: 2,
|
|
883
|
-
rootState: refreshResult.root_state || rootState
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
const compactRefresh = {
|
|
887
|
-
...compactRefreshAttempt(refreshResult),
|
|
888
|
-
context_recovery: true,
|
|
889
|
-
recovery_reason: reason,
|
|
890
|
-
trigger_error: compactError(error, "RECOMMEND_CONTEXT_RECOVERY_TRIGGER"),
|
|
891
|
-
account_rights_panel_close: compactCloseResult(blockingPanelClose),
|
|
892
|
-
elapsed_ms: Date.now() - started
|
|
893
|
-
};
|
|
975
|
+
if (refreshResult.ok) {
|
|
976
|
+
blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
977
|
+
attemptsLimit: 2,
|
|
978
|
+
rootState: refreshResult.root_state || rootState
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
const compactRefresh = {
|
|
982
|
+
...compactRefreshAttempt(refreshResult),
|
|
983
|
+
context_recovery: true,
|
|
984
|
+
recovery_reason: reason,
|
|
985
|
+
trigger_error: compactError(error, "RECOMMEND_CONTEXT_RECOVERY_TRIGGER"),
|
|
986
|
+
account_rights_panel_close: compactCloseResult(blockingPanelClose),
|
|
987
|
+
elapsed_ms: Date.now() - started
|
|
988
|
+
};
|
|
894
989
|
refreshAttempts.push(compactRefresh);
|
|
895
990
|
runControl.checkpoint({
|
|
896
991
|
context_recovery: {
|
|
@@ -900,56 +995,62 @@ export async function runRecommendWorkflow({
|
|
|
900
995
|
refresh: compactRefresh,
|
|
901
996
|
counters: countRecommendResultStatuses(results, { greetCount })
|
|
902
997
|
},
|
|
903
|
-
candidate_list: compactInfiniteListState(listState)
|
|
904
|
-
});
|
|
905
|
-
if (!refreshResult.ok) {
|
|
906
|
-
updateRecommendProgress({
|
|
907
|
-
refresh_method: refreshResult.method || null,
|
|
908
|
-
refresh_forced_recent_not_view:
|
|
909
|
-
recovery_reason: reason
|
|
998
|
+
candidate_list: compactInfiniteListState(listState)
|
|
999
|
+
});
|
|
1000
|
+
if (!refreshResult.ok) {
|
|
1001
|
+
updateRecommendProgress({
|
|
1002
|
+
refresh_method: refreshResult.method || null,
|
|
1003
|
+
refresh_forced_recent_not_view: effectiveForceRecentNotView,
|
|
1004
|
+
recovery_reason: reason
|
|
910
1005
|
});
|
|
911
1006
|
throw new Error(`Recommend context recovery failed after ${reason}: ${refreshResult.reason || refreshResult.error || "refresh returned no cards"}`);
|
|
912
1007
|
}
|
|
1008
|
+
if (refreshResult.current_city_only) {
|
|
1009
|
+
currentCityOnlyResult = refreshResult.current_city_only;
|
|
1010
|
+
}
|
|
1011
|
+
if (refreshResult.filter) {
|
|
1012
|
+
filterResult = refreshResult.filter;
|
|
1013
|
+
}
|
|
913
1014
|
if (!blockingPanelClose?.closed) {
|
|
914
1015
|
const panelError = createRecommendBlockingPanelCloseFailureError(blockingPanelClose, `recover:${reason}`);
|
|
915
1016
|
panelError.refresh_attempt = compactRefresh;
|
|
916
1017
|
throw panelError;
|
|
917
1018
|
}
|
|
918
|
-
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
1019
|
+
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
919
1020
|
rootState = await ensureRecommendViewport(rootState, "recover_after");
|
|
920
1021
|
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
921
1022
|
timeoutMs: cardTimeoutMs,
|
|
922
1023
|
intervalMs: 300
|
|
923
1024
|
});
|
|
924
1025
|
resetInfiniteListForRefreshRound(listState, {
|
|
925
|
-
reason: `context_recovery:${reason}`,
|
|
926
|
-
round: contextRecoveryAttempts,
|
|
927
|
-
method: refreshResult.method,
|
|
928
|
-
metadata: {
|
|
929
|
-
card_count: cardNodeIds.length,
|
|
930
|
-
forced_recent_not_view:
|
|
931
|
-
counters: countRecommendResultStatuses(results, { greetCount })
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
listEndReason = "";
|
|
935
|
-
updateRecommendProgress({
|
|
936
|
-
card_count: cardNodeIds.length,
|
|
937
|
-
refresh_method: refreshResult.method || null,
|
|
938
|
-
refresh_forced_recent_not_view:
|
|
939
|
-
recovery_reason: reason
|
|
940
|
-
});
|
|
941
|
-
return refreshResult;
|
|
1026
|
+
reason: `context_recovery:${reason}`,
|
|
1027
|
+
round: contextRecoveryAttempts,
|
|
1028
|
+
method: refreshResult.method,
|
|
1029
|
+
metadata: {
|
|
1030
|
+
card_count: cardNodeIds.length,
|
|
1031
|
+
forced_recent_not_view: effectiveForceRecentNotView,
|
|
1032
|
+
counters: countRecommendResultStatuses(results, { greetCount })
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
listEndReason = "";
|
|
1036
|
+
updateRecommendProgress({
|
|
1037
|
+
card_count: cardNodeIds.length,
|
|
1038
|
+
refresh_method: refreshResult.method || null,
|
|
1039
|
+
refresh_forced_recent_not_view: effectiveForceRecentNotView,
|
|
1040
|
+
recovery_reason: reason
|
|
1041
|
+
});
|
|
1042
|
+
return refreshResult;
|
|
942
1043
|
}
|
|
943
1044
|
|
|
944
|
-
runControl.setPhase("recommend:cleanup");
|
|
945
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 });
|
|
946
|
-
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 });
|
|
947
|
-
await closeRecommendBlockingPanelsForRun("cleanup");
|
|
948
|
-
|
|
949
|
-
await runControl.waitIfPaused();
|
|
950
|
-
runControl.throwIfCanceled();
|
|
951
|
-
runControl.setPhase("recommend:roots");
|
|
952
|
-
rootState = await getRecommendRoots(client);
|
|
1045
|
+
runControl.setPhase("recommend:cleanup");
|
|
1046
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 });
|
|
1047
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 });
|
|
1048
|
+
await closeRecommendBlockingPanelsForRun("cleanup");
|
|
1049
|
+
|
|
1050
|
+
await runControl.waitIfPaused();
|
|
1051
|
+
runControl.throwIfCanceled();
|
|
1052
|
+
runControl.setPhase("recommend:roots");
|
|
1053
|
+
rootState = await getRecommendRoots(client);
|
|
953
1054
|
rootState = await ensureRecommendViewport(rootState, "roots");
|
|
954
1055
|
runControl.checkpoint({
|
|
955
1056
|
iframe_selector: rootState.iframe.selector,
|
|
@@ -987,34 +1088,55 @@ export async function runRecommendWorkflow({
|
|
|
987
1088
|
throw new Error(`Recommend page scope was not selected: ${pageScopeSelection.reason || pageScopeSelection.effective_scope || requestedPageScope}`);
|
|
988
1089
|
}
|
|
989
1090
|
rootState = await getRecommendRoots(client);
|
|
990
|
-
rootState = await ensureRecommendViewport(rootState, "page_scope");
|
|
991
|
-
runControl.checkpoint({
|
|
992
|
-
page_scope: compactPageScopeSelection(pageScopeSelection)
|
|
993
|
-
});
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1091
|
+
rootState = await ensureRecommendViewport(rootState, "page_scope");
|
|
1092
|
+
runControl.checkpoint({
|
|
1093
|
+
page_scope: compactPageScopeSelection(pageScopeSelection)
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
const initialFilterStages = await applyRecommendFilterEnvelopeStages(normalizedFilter, {
|
|
1097
|
+
applyCurrentCityOnly: async () => {
|
|
1098
|
+
await runControl.waitIfPaused();
|
|
1099
|
+
runControl.throwIfCanceled();
|
|
1100
|
+
runControl.setPhase("recommend:current-city-only");
|
|
1101
|
+
const result = await ensureRecommendCurrentCityOnly(
|
|
1102
|
+
client,
|
|
1103
|
+
rootState.iframe.documentNodeId,
|
|
1104
|
+
{ enabled: normalizedFilter.currentCityOnly }
|
|
1105
|
+
);
|
|
1106
|
+
rootState = await getRecommendRoots(client);
|
|
1107
|
+
rootState = await ensureRecommendViewport(rootState, "current_city_only");
|
|
1108
|
+
runControl.checkpoint({
|
|
1109
|
+
current_city_only: compactCurrentCityOnlyResult(result)
|
|
1110
|
+
});
|
|
1111
|
+
return result;
|
|
1112
|
+
},
|
|
1113
|
+
applyFilterPanel: async () => {
|
|
1114
|
+
await runControl.waitIfPaused();
|
|
1115
|
+
runControl.throwIfCanceled();
|
|
1116
|
+
runControl.setPhase("recommend:filter");
|
|
1117
|
+
const result = await selectAndConfirmFirstSafeFilter(
|
|
1118
|
+
client,
|
|
1119
|
+
rootState.iframe.documentNodeId,
|
|
1120
|
+
buildRecommendFilterSelectionOptions(normalizedFilter)
|
|
1121
|
+
);
|
|
1122
|
+
if (!result.confirmed) {
|
|
1123
|
+
throw new Error("Recommend run filter selection was not confirmed");
|
|
1124
|
+
}
|
|
1125
|
+
rootState = await getRecommendRoots(client);
|
|
1126
|
+
rootState = await ensureRecommendViewport(rootState, "filter");
|
|
1127
|
+
runControl.checkpoint({
|
|
1128
|
+
filter: compactFilterResult(result)
|
|
1129
|
+
});
|
|
1130
|
+
return result;
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
currentCityOnlyResult = initialFilterStages.current_city_only;
|
|
1134
|
+
filterResult = initialFilterStages.filter;
|
|
1135
|
+
|
|
1136
|
+
await runControl.waitIfPaused();
|
|
1137
|
+
runControl.throwIfCanceled();
|
|
1138
|
+
runControl.setPhase("recommend:cards");
|
|
1139
|
+
rootState = await ensureRecommendViewport(rootState, "cards");
|
|
1018
1140
|
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
1019
1141
|
timeoutMs: cardTimeoutMs,
|
|
1020
1142
|
intervalMs: 300
|
|
@@ -1039,11 +1161,11 @@ export async function runRecommendWorkflow({
|
|
|
1039
1161
|
client,
|
|
1040
1162
|
state: listState,
|
|
1041
1163
|
maxScrolls: listMaxScrolls,
|
|
1042
|
-
stableSignatureLimit: listStableSignatureLimit,
|
|
1043
|
-
wheelDeltaY: listWheelDeltaY,
|
|
1044
|
-
settleMs: listSettleMs,
|
|
1045
|
-
listScrollJitterEnabled: effectiveHumanBehavior.listScrollJitter,
|
|
1046
|
-
fallbackPoint: listFallbackResolver,
|
|
1164
|
+
stableSignatureLimit: listStableSignatureLimit,
|
|
1165
|
+
wheelDeltaY: listWheelDeltaY,
|
|
1166
|
+
settleMs: listSettleMs,
|
|
1167
|
+
listScrollJitterEnabled: effectiveHumanBehavior.listScrollJitter,
|
|
1168
|
+
fallbackPoint: listFallbackResolver,
|
|
1047
1169
|
findNodeIds: async () => {
|
|
1048
1170
|
let currentRootState = await getRecommendRoots(client);
|
|
1049
1171
|
currentRootState = await ensureRecommendViewport(currentRootState, "candidate_find_nodes");
|
|
@@ -1084,55 +1206,61 @@ export async function runRecommendWorkflow({
|
|
|
1084
1206
|
runControl.setPhase("recommend:refresh");
|
|
1085
1207
|
refreshRounds += 1;
|
|
1086
1208
|
const refreshResult = await refreshRecommendListAtEnd(client, {
|
|
1087
|
-
rootState,
|
|
1088
|
-
jobLabel,
|
|
1089
|
-
pageScope: pageScopeSelection?.effective_scope || requestedPageScope,
|
|
1090
|
-
fallbackPageScope: normalizedFallbackPageScope,
|
|
1091
|
-
filter: normalizedFilter,
|
|
1092
|
-
forceRecentNotView:
|
|
1093
|
-
cardTimeoutMs,
|
|
1094
|
-
buttonSettleMs: refreshButtonSettleMs,
|
|
1095
|
-
reloadSettleMs: refreshReloadSettleMs
|
|
1096
|
-
});
|
|
1097
|
-
const compactRefresh = compactRefreshAttempt(refreshResult);
|
|
1209
|
+
rootState,
|
|
1210
|
+
jobLabel,
|
|
1211
|
+
pageScope: pageScopeSelection?.effective_scope || requestedPageScope,
|
|
1212
|
+
fallbackPageScope: normalizedFallbackPageScope,
|
|
1213
|
+
filter: normalizedFilter,
|
|
1214
|
+
forceRecentNotView: normalizedFilter.enabled,
|
|
1215
|
+
cardTimeoutMs,
|
|
1216
|
+
buttonSettleMs: refreshButtonSettleMs,
|
|
1217
|
+
reloadSettleMs: refreshReloadSettleMs
|
|
1218
|
+
});
|
|
1219
|
+
const compactRefresh = compactRefreshAttempt(refreshResult);
|
|
1098
1220
|
refreshAttempts.push(compactRefresh);
|
|
1099
1221
|
runControl.checkpoint({
|
|
1100
1222
|
refresh_round: refreshRounds,
|
|
1101
|
-
refresh: compactRefresh
|
|
1102
|
-
});
|
|
1103
|
-
updateRecommendProgress({
|
|
1104
|
-
card_count: refreshResult.card_count || cardNodeIds.length,
|
|
1105
|
-
refresh_method: refreshResult.method || null,
|
|
1106
|
-
refresh_forced_recent_not_view:
|
|
1107
|
-
list_end_reason: listEndReason
|
|
1108
|
-
});
|
|
1223
|
+
refresh: compactRefresh
|
|
1224
|
+
});
|
|
1225
|
+
updateRecommendProgress({
|
|
1226
|
+
card_count: refreshResult.card_count || cardNodeIds.length,
|
|
1227
|
+
refresh_method: refreshResult.method || null,
|
|
1228
|
+
refresh_forced_recent_not_view: Boolean(refreshResult.forced_recent_not_view),
|
|
1229
|
+
list_end_reason: listEndReason
|
|
1230
|
+
});
|
|
1109
1231
|
if (refreshResult.ok) {
|
|
1232
|
+
if (refreshResult.current_city_only) {
|
|
1233
|
+
currentCityOnlyResult = refreshResult.current_city_only;
|
|
1234
|
+
}
|
|
1235
|
+
if (refreshResult.filter) {
|
|
1236
|
+
filterResult = refreshResult.filter;
|
|
1237
|
+
}
|
|
1110
1238
|
rootState = refreshResult.root_state || await getRecommendRoots(client);
|
|
1111
1239
|
rootState = await ensureRecommendViewport(rootState, "refresh_after");
|
|
1112
|
-
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
1113
|
-
timeoutMs: cardTimeoutMs,
|
|
1114
|
-
intervalMs: 300
|
|
1240
|
+
cardNodeIds = await waitForRecommendCardNodeIds(client, rootState.iframe.documentNodeId, {
|
|
1241
|
+
timeoutMs: cardTimeoutMs,
|
|
1242
|
+
intervalMs: 300
|
|
1115
1243
|
});
|
|
1116
1244
|
resetInfiniteListForRefreshRound(listState, {
|
|
1117
|
-
reason: listEndReason,
|
|
1118
|
-
round: refreshRounds,
|
|
1119
|
-
method: refreshResult.method,
|
|
1120
|
-
metadata: {
|
|
1121
|
-
card_count: cardNodeIds.length,
|
|
1122
|
-
forced_recent_not_view:
|
|
1123
|
-
}
|
|
1124
|
-
});
|
|
1245
|
+
reason: listEndReason,
|
|
1246
|
+
round: refreshRounds,
|
|
1247
|
+
method: refreshResult.method,
|
|
1248
|
+
metadata: {
|
|
1249
|
+
card_count: cardNodeIds.length,
|
|
1250
|
+
forced_recent_not_view: Boolean(refreshResult.forced_recent_not_view)
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1125
1253
|
listEndReason = "";
|
|
1126
1254
|
continue;
|
|
1127
1255
|
}
|
|
1128
|
-
throw createRecommendRefreshFailureError(compactRefresh, {
|
|
1129
|
-
listEndReason,
|
|
1130
|
-
targetCount: targetPassCount,
|
|
1131
|
-
passedCount: countPassedResults(results)
|
|
1132
|
-
});
|
|
1133
|
-
}
|
|
1134
|
-
break;
|
|
1135
|
-
}
|
|
1256
|
+
throw createRecommendRefreshFailureError(compactRefresh, {
|
|
1257
|
+
listEndReason,
|
|
1258
|
+
targetCount: targetPassCount,
|
|
1259
|
+
passedCount: countPassedResults(results)
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1136
1264
|
|
|
1137
1265
|
const index = results.length;
|
|
1138
1266
|
let cardNodeId = nextCandidateResult.item.node_id;
|
|
@@ -1140,42 +1268,42 @@ export async function runRecommendWorkflow({
|
|
|
1140
1268
|
let cardCandidate = nextCandidateResult.item.candidate;
|
|
1141
1269
|
|
|
1142
1270
|
let screeningCandidate = cardCandidate;
|
|
1143
|
-
let detailResult = null;
|
|
1144
|
-
let recoverableDetailError = null;
|
|
1145
|
-
let colleagueContact = null;
|
|
1146
|
-
let skipRecentColleagueContact = false;
|
|
1147
|
-
let detailStep = "not_started";
|
|
1271
|
+
let detailResult = null;
|
|
1272
|
+
let recoverableDetailError = null;
|
|
1273
|
+
let colleagueContact = null;
|
|
1274
|
+
let skipRecentColleagueContact = false;
|
|
1275
|
+
let detailStep = "not_started";
|
|
1148
1276
|
if (index < effectiveDetailLimit) {
|
|
1149
1277
|
try {
|
|
1150
1278
|
await runControl.waitIfPaused();
|
|
1151
1279
|
runControl.throwIfCanceled();
|
|
1152
|
-
runControl.setPhase("recommend:detail");
|
|
1153
|
-
detailStep = "ensure_viewport";
|
|
1154
|
-
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
1155
|
-
const blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
1156
|
-
attemptsLimit: 2,
|
|
1157
|
-
rootState
|
|
1158
|
-
});
|
|
1159
|
-
if (!blockingPanelClose?.closed) {
|
|
1160
|
-
const panelError = createRecommendBlockingPanelCloseFailureError(
|
|
1161
|
-
blockingPanelClose,
|
|
1162
|
-
"before_detail_open"
|
|
1163
|
-
);
|
|
1164
|
-
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1165
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error: panelError });
|
|
1166
|
-
await recoverAndReapplyRecommendContext("account_rights_panel_before_detail", panelError, {
|
|
1167
|
-
forceRecentNotView: true
|
|
1168
|
-
});
|
|
1169
|
-
continue;
|
|
1170
|
-
}
|
|
1171
|
-
if (blockingPanelClose.already_closed === false) {
|
|
1172
|
-
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1173
|
-
}
|
|
1174
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep });
|
|
1175
|
-
detailStep = "open_detail";
|
|
1176
|
-
networkRecorder.clear();
|
|
1177
|
-
await maybeHumanActionCooldown("before_detail_open", timings);
|
|
1178
|
-
const openedDetail = await openRecommendCardDetailWithFreshRetry(client, {
|
|
1280
|
+
runControl.setPhase("recommend:detail");
|
|
1281
|
+
detailStep = "ensure_viewport";
|
|
1282
|
+
rootState = await ensureRecommendViewport(rootState, "detail");
|
|
1283
|
+
const blockingPanelClose = await closeRecommendBlockingPanels(client, {
|
|
1284
|
+
attemptsLimit: 2,
|
|
1285
|
+
rootState
|
|
1286
|
+
});
|
|
1287
|
+
if (!blockingPanelClose?.closed) {
|
|
1288
|
+
const panelError = createRecommendBlockingPanelCloseFailureError(
|
|
1289
|
+
blockingPanelClose,
|
|
1290
|
+
"before_detail_open"
|
|
1291
|
+
);
|
|
1292
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1293
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error: panelError });
|
|
1294
|
+
await recoverAndReapplyRecommendContext("account_rights_panel_before_detail", panelError, {
|
|
1295
|
+
forceRecentNotView: true
|
|
1296
|
+
});
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
if (blockingPanelClose.already_closed === false) {
|
|
1300
|
+
timings.account_rights_panel_close = compactCloseResult(blockingPanelClose);
|
|
1301
|
+
}
|
|
1302
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep });
|
|
1303
|
+
detailStep = "open_detail";
|
|
1304
|
+
networkRecorder.clear();
|
|
1305
|
+
await maybeHumanActionCooldown("before_detail_open", timings);
|
|
1306
|
+
const openedDetail = await openRecommendCardDetailWithFreshRetry(client, {
|
|
1179
1307
|
cardNodeId,
|
|
1180
1308
|
candidateKey,
|
|
1181
1309
|
cardCandidate,
|
|
@@ -1186,52 +1314,52 @@ export async function runRecommendWorkflow({
|
|
|
1186
1314
|
});
|
|
1187
1315
|
addTiming(timings, "candidate_click_ms", openedDetail.timings?.candidate_click_ms);
|
|
1188
1316
|
addTiming(timings, "detail_open_ms", openedDetail.timings?.detail_open_ms);
|
|
1189
|
-
cardNodeId = openedDetail.card_node_id || cardNodeId;
|
|
1190
|
-
cardCandidate = openedDetail.card_candidate || cardCandidate;
|
|
1191
|
-
screeningCandidate = cardCandidate;
|
|
1192
|
-
if (shouldSkipRecentColleagueContacted) {
|
|
1193
|
-
detailStep = "check_colleague_contact";
|
|
1194
|
-
try {
|
|
1195
|
-
colleagueContact = await measureTiming(timings, "colleague_contact_check_ms", () => inspectRecentColleagueContact(
|
|
1196
|
-
client,
|
|
1197
|
-
openedDetail.detail_state,
|
|
1198
|
-
{
|
|
1199
|
-
referenceDate: colleagueContactReferenceDate,
|
|
1200
|
-
windowDays: normalizedColleagueContactWindowDays
|
|
1201
|
-
}
|
|
1202
|
-
));
|
|
1203
|
-
if (colleagueContact?.recent) {
|
|
1204
|
-
skipRecentColleagueContact = true;
|
|
1205
|
-
detailResult = {
|
|
1206
|
-
candidate: screeningCandidate,
|
|
1207
|
-
detail: {
|
|
1208
|
-
popup_text: "",
|
|
1209
|
-
resume_text: ""
|
|
1210
|
-
},
|
|
1211
|
-
colleague_contact: colleagueContact,
|
|
1212
|
-
cv_acquisition: {
|
|
1213
|
-
source: "skipped_recent_colleague_contact",
|
|
1214
|
-
skipped: true,
|
|
1215
|
-
reason: "skipped_recent_colleague_contact"
|
|
1216
|
-
}
|
|
1217
|
-
};
|
|
1218
|
-
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecommendDetail(client));
|
|
1219
|
-
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
1220
|
-
}
|
|
1221
|
-
} catch (error) {
|
|
1222
|
-
colleagueContact = {
|
|
1223
|
-
checked: false,
|
|
1224
|
-
recent: false,
|
|
1225
|
-
reason: "inspection_failed",
|
|
1226
|
-
error: error?.message || String(error),
|
|
1227
|
-
window_days: normalizedColleagueContactWindowDays
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
if (!skipRecentColleagueContact) {
|
|
1232
|
-
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
1233
|
-
detailStep = "wait_network";
|
|
1234
|
-
const networkWait = await measureTiming(timings, "network_cv_wait_ms", () => waitForCvNetworkEvents(
|
|
1317
|
+
cardNodeId = openedDetail.card_node_id || cardNodeId;
|
|
1318
|
+
cardCandidate = openedDetail.card_candidate || cardCandidate;
|
|
1319
|
+
screeningCandidate = cardCandidate;
|
|
1320
|
+
if (shouldSkipRecentColleagueContacted) {
|
|
1321
|
+
detailStep = "check_colleague_contact";
|
|
1322
|
+
try {
|
|
1323
|
+
colleagueContact = await measureTiming(timings, "colleague_contact_check_ms", () => inspectRecentColleagueContact(
|
|
1324
|
+
client,
|
|
1325
|
+
openedDetail.detail_state,
|
|
1326
|
+
{
|
|
1327
|
+
referenceDate: colleagueContactReferenceDate,
|
|
1328
|
+
windowDays: normalizedColleagueContactWindowDays
|
|
1329
|
+
}
|
|
1330
|
+
));
|
|
1331
|
+
if (colleagueContact?.recent) {
|
|
1332
|
+
skipRecentColleagueContact = true;
|
|
1333
|
+
detailResult = {
|
|
1334
|
+
candidate: screeningCandidate,
|
|
1335
|
+
detail: {
|
|
1336
|
+
popup_text: "",
|
|
1337
|
+
resume_text: ""
|
|
1338
|
+
},
|
|
1339
|
+
colleague_contact: colleagueContact,
|
|
1340
|
+
cv_acquisition: {
|
|
1341
|
+
source: "skipped_recent_colleague_contact",
|
|
1342
|
+
skipped: true,
|
|
1343
|
+
reason: "skipped_recent_colleague_contact"
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecommendDetail(client));
|
|
1347
|
+
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
1348
|
+
}
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
colleagueContact = {
|
|
1351
|
+
checked: false,
|
|
1352
|
+
recent: false,
|
|
1353
|
+
reason: "inspection_failed",
|
|
1354
|
+
error: error?.message || String(error),
|
|
1355
|
+
window_days: normalizedColleagueContactWindowDays
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (!skipRecentColleagueContact) {
|
|
1360
|
+
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
1361
|
+
detailStep = "wait_network";
|
|
1362
|
+
const networkWait = await measureTiming(timings, "network_cv_wait_ms", () => waitForCvNetworkEvents(
|
|
1235
1363
|
waitForRecommendDetailNetworkEvents,
|
|
1236
1364
|
networkRecorder,
|
|
1237
1365
|
{
|
|
@@ -1255,10 +1383,10 @@ export async function runRecommendWorkflow({
|
|
|
1255
1383
|
networkParseRetryMs: waitPlan.mode_before === "image" ? 500 : 2200,
|
|
1256
1384
|
networkParseIntervalMs: 250
|
|
1257
1385
|
});
|
|
1258
|
-
addTiming(timings, "late_network_retry_ms", detailResult.network_parse_retry_elapsed_ms);
|
|
1259
|
-
if (colleagueContact) detailResult.colleague_contact = colleagueContact;
|
|
1260
|
-
|
|
1261
|
-
const parsedNetworkProfileCount = countParsedNetworkProfiles(detailResult);
|
|
1386
|
+
addTiming(timings, "late_network_retry_ms", detailResult.network_parse_retry_elapsed_ms);
|
|
1387
|
+
if (colleagueContact) detailResult.colleague_contact = colleagueContact;
|
|
1388
|
+
|
|
1389
|
+
const parsedNetworkProfileCount = countParsedNetworkProfiles(detailResult);
|
|
1262
1390
|
let source = "network";
|
|
1263
1391
|
let imageEvidence = null;
|
|
1264
1392
|
let captureTarget = null;
|
|
@@ -1295,12 +1423,12 @@ export async function runRecommendWorkflow({
|
|
|
1295
1423
|
resizeMaxWidth: 1100,
|
|
1296
1424
|
captureViewport: false,
|
|
1297
1425
|
padding: 0,
|
|
1298
|
-
maxScreenshots: maxImagePages,
|
|
1299
|
-
wheelDeltaY: imageWheelDeltaY,
|
|
1300
|
-
settleMs: 350,
|
|
1301
|
-
scrollMethod: "dom-anchor-fallback-input",
|
|
1302
|
-
scrollDeltaJitterEnabled: effectiveHumanBehavior.listScrollJitter,
|
|
1303
|
-
stepTimeoutMs: 45000,
|
|
1426
|
+
maxScreenshots: maxImagePages,
|
|
1427
|
+
wheelDeltaY: imageWheelDeltaY,
|
|
1428
|
+
settleMs: 350,
|
|
1429
|
+
scrollMethod: "dom-anchor-fallback-input",
|
|
1430
|
+
scrollDeltaJitterEnabled: effectiveHumanBehavior.listScrollJitter,
|
|
1431
|
+
stepTimeoutMs: 45000,
|
|
1304
1432
|
totalTimeoutMs: 90000,
|
|
1305
1433
|
duplicateStopCount: 1,
|
|
1306
1434
|
skipDuplicateScreenshots: true,
|
|
@@ -1324,14 +1452,14 @@ export async function runRecommendWorkflow({
|
|
|
1324
1452
|
const recoveryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
1325
1453
|
if (recoveryCount < 1) {
|
|
1326
1454
|
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1327
|
-
timings.image_capture_recovery_trigger = compactError(error, "IMAGE_CAPTURE_FAILED");
|
|
1328
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1329
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1330
|
-
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1331
|
-
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1332
|
-
await recoverAndReapplyRecommendContext(`image_capture:${detailStep}`, error, {
|
|
1333
|
-
forceRecentNotView: true
|
|
1334
|
-
});
|
|
1455
|
+
timings.image_capture_recovery_trigger = compactError(error, "IMAGE_CAPTURE_FAILED");
|
|
1456
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1457
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1458
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1459
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1460
|
+
await recoverAndReapplyRecommendContext(`image_capture:${detailStep}`, error, {
|
|
1461
|
+
forceRecentNotView: true
|
|
1462
|
+
});
|
|
1335
1463
|
continue;
|
|
1336
1464
|
}
|
|
1337
1465
|
imageEvidence = createRecoverableImageCaptureEvidence(error, {
|
|
@@ -1370,73 +1498,73 @@ export async function runRecommendWorkflow({
|
|
|
1370
1498
|
image_evidence: summarizeImageEvidence(imageEvidence),
|
|
1371
1499
|
capture_target: captureTarget || null,
|
|
1372
1500
|
capture_target_wait: captureTargetWait
|
|
1373
|
-
};
|
|
1374
|
-
screeningCandidate = detailResult.candidate;
|
|
1375
|
-
}
|
|
1376
|
-
} catch (error) {
|
|
1501
|
+
};
|
|
1502
|
+
screeningCandidate = detailResult.candidate;
|
|
1503
|
+
}
|
|
1504
|
+
} catch (error) {
|
|
1377
1505
|
if (!isRecoverableRecommendDetailError(error)) throw error;
|
|
1378
1506
|
const recoveryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
1379
1507
|
if (recoveryCount < 1) {
|
|
1380
|
-
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1381
|
-
timings.detail_recovery_trigger = compactRecoverableDetailError(error);
|
|
1382
|
-
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1383
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1384
|
-
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1385
|
-
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1386
|
-
await recoverAndReapplyRecommendContext(`detail:${detailStep}`, error, {
|
|
1387
|
-
forceRecentNotView: true
|
|
1388
|
-
});
|
|
1508
|
+
candidateRecoveryCounts.set(candidateKey, recoveryCount + 1);
|
|
1509
|
+
timings.detail_recovery_trigger = compactRecoverableDetailError(error);
|
|
1510
|
+
checkpointInProgressCandidate({ index, candidateKey, cardNodeId, detailStep, error });
|
|
1511
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1512
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1513
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1514
|
+
await recoverAndReapplyRecommendContext(`detail:${detailStep}`, error, {
|
|
1515
|
+
forceRecentNotView: true
|
|
1516
|
+
});
|
|
1389
1517
|
continue;
|
|
1390
1518
|
}
|
|
1391
|
-
recoverableDetailError = error;
|
|
1392
|
-
detailResult = null;
|
|
1393
|
-
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
1394
|
-
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1395
|
-
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1396
|
-
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1397
|
-
}
|
|
1519
|
+
recoverableDetailError = error;
|
|
1520
|
+
detailResult = null;
|
|
1521
|
+
timings.detail_recovered_error = compactRecoverableDetailError(error);
|
|
1522
|
+
await closeRecommendDetail(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1523
|
+
await closeRecommendAvatarPreview(client, { attemptsLimit: 2 }).catch(() => null);
|
|
1524
|
+
await closeRecommendBlockingPanels(client, { attemptsLimit: 2, rootState }).catch(() => null);
|
|
1525
|
+
}
|
|
1398
1526
|
}
|
|
1399
1527
|
|
|
1400
1528
|
await runControl.waitIfPaused();
|
|
1401
1529
|
runControl.throwIfCanceled();
|
|
1402
1530
|
runControl.setPhase("recommend:screening");
|
|
1403
|
-
let llmResult = null;
|
|
1404
|
-
if (useLlmScreening) {
|
|
1405
|
-
if (skipRecentColleagueContact || recoverableDetailError || detailResult?.image_evidence?.ok === false) {
|
|
1406
|
-
llmResult = null;
|
|
1407
|
-
} else if (!llmConfig) {
|
|
1531
|
+
let llmResult = null;
|
|
1532
|
+
if (useLlmScreening) {
|
|
1533
|
+
if (skipRecentColleagueContact || recoverableDetailError || detailResult?.image_evidence?.ok === false) {
|
|
1534
|
+
llmResult = null;
|
|
1535
|
+
} else if (!llmConfig) {
|
|
1408
1536
|
llmResult = createMissingLlmConfigResult();
|
|
1409
1537
|
} else {
|
|
1410
1538
|
try {
|
|
1411
1539
|
const llmTimingKey = detailResult?.image_evidence?.file_paths?.length
|
|
1412
1540
|
? "vision_model_ms"
|
|
1413
1541
|
: "text_model_ms";
|
|
1414
|
-
llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
|
|
1415
|
-
candidate: screeningCandidate,
|
|
1416
|
-
criteria,
|
|
1417
|
-
config: llmConfig,
|
|
1418
|
-
timeoutMs: llmTimeoutMs,
|
|
1542
|
+
llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
|
|
1543
|
+
candidate: screeningCandidate,
|
|
1544
|
+
criteria,
|
|
1545
|
+
config: llmConfig,
|
|
1546
|
+
timeoutMs: llmTimeoutMs,
|
|
1419
1547
|
imageEvidence: detailResult?.image_evidence || null,
|
|
1420
1548
|
maxImages: llmImageLimit,
|
|
1421
1549
|
imageDetail: llmImageDetail
|
|
1422
1550
|
}));
|
|
1423
|
-
} catch (error) {
|
|
1424
|
-
if (isFatalLlmProviderError(error)) {
|
|
1425
|
-
throw createFatalLlmRunError(error, {
|
|
1426
|
-
domain: "recommend",
|
|
1427
|
-
candidate: screeningCandidate
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
llmResult = createFailedLlmScreeningResult(error);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
if (detailResult) detailResult.llm_result = llmResult;
|
|
1434
|
-
}
|
|
1435
|
-
const screening = skipRecentColleagueContact
|
|
1436
|
-
? createRecentColleagueContactSkipScreening(screeningCandidate, colleagueContact)
|
|
1437
|
-
: recoverableDetailError
|
|
1438
|
-
? createRecoverableDetailFailureScreening(screeningCandidate, recoverableDetailError)
|
|
1439
|
-
: detailResult?.image_evidence?.ok === false
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
if (isFatalLlmProviderError(error)) {
|
|
1553
|
+
throw createFatalLlmRunError(error, {
|
|
1554
|
+
domain: "recommend",
|
|
1555
|
+
candidate: screeningCandidate
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
llmResult = createFailedLlmScreeningResult(error);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (detailResult) detailResult.llm_result = llmResult;
|
|
1562
|
+
}
|
|
1563
|
+
const screening = skipRecentColleagueContact
|
|
1564
|
+
? createRecentColleagueContactSkipScreening(screeningCandidate, colleagueContact)
|
|
1565
|
+
: recoverableDetailError
|
|
1566
|
+
? createRecoverableDetailFailureScreening(screeningCandidate, recoverableDetailError)
|
|
1567
|
+
: detailResult?.image_evidence?.ok === false
|
|
1440
1568
|
? createImageCaptureFailureScreening(screeningCandidate, {
|
|
1441
1569
|
code: detailResult.image_evidence.error_code,
|
|
1442
1570
|
message: detailResult.image_evidence.error
|
|
@@ -1444,17 +1572,17 @@ export async function runRecommendWorkflow({
|
|
|
1444
1572
|
: useLlmScreening
|
|
1445
1573
|
? llmResultToScreening(llmResult, screeningCandidate)
|
|
1446
1574
|
: screenCandidate(screeningCandidate, { criteria });
|
|
1447
|
-
let actionDiscovery = null;
|
|
1448
|
-
let postActionResult = null;
|
|
1449
|
-
let closeFailureError = null;
|
|
1450
|
-
let closeRecoveryFailure = null;
|
|
1451
|
-
if (postActionEnabled && detailResult && !skipRecentColleagueContact) {
|
|
1452
|
-
const postActionStarted = Date.now();
|
|
1453
|
-
await runControl.waitIfPaused();
|
|
1454
|
-
runControl.throwIfCanceled();
|
|
1455
|
-
runControl.setPhase("recommend:post-action");
|
|
1456
|
-
await maybeHumanActionCooldown("before_post_action", timings);
|
|
1457
|
-
actionDiscovery = await waitForRecommendDetailActionControls(client, {
|
|
1575
|
+
let actionDiscovery = null;
|
|
1576
|
+
let postActionResult = null;
|
|
1577
|
+
let closeFailureError = null;
|
|
1578
|
+
let closeRecoveryFailure = null;
|
|
1579
|
+
if (postActionEnabled && detailResult && !skipRecentColleagueContact) {
|
|
1580
|
+
const postActionStarted = Date.now();
|
|
1581
|
+
await runControl.waitIfPaused();
|
|
1582
|
+
runControl.throwIfCanceled();
|
|
1583
|
+
runControl.setPhase("recommend:post-action");
|
|
1584
|
+
await maybeHumanActionCooldown("before_post_action", timings);
|
|
1585
|
+
actionDiscovery = await waitForRecommendDetailActionControls(client, {
|
|
1458
1586
|
timeoutMs: actionTimeoutMs,
|
|
1459
1587
|
intervalMs: actionIntervalMs,
|
|
1460
1588
|
requireAny: true
|
|
@@ -1473,34 +1601,34 @@ export async function runRecommendWorkflow({
|
|
|
1473
1601
|
greetCount += 1;
|
|
1474
1602
|
}
|
|
1475
1603
|
addTiming(timings, "post_action_ms", Date.now() - postActionStarted);
|
|
1476
|
-
}
|
|
1477
|
-
if (detailResult && closeDetail && !detailResult.close_result?.closed) {
|
|
1478
|
-
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecommendDetail(client));
|
|
1479
|
-
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
1480
|
-
if (!detailResult.close_result?.closed) {
|
|
1481
|
-
closeFailureError = createRecommendCloseFailureError(detailResult.close_result);
|
|
1482
|
-
try {
|
|
1483
|
-
const recovery = await recoverAndReapplyRecommendContext("detail_close_failed", closeFailureError, {
|
|
1484
|
-
forceRecentNotView: true
|
|
1485
|
-
});
|
|
1486
|
-
detailResult.cv_acquisition = {
|
|
1487
|
-
...(detailResult.cv_acquisition || {}),
|
|
1488
|
-
close_recovery: {
|
|
1489
|
-
ok: Boolean(recovery.ok),
|
|
1490
|
-
method: recovery.method || "",
|
|
1491
|
-
forced_recent_not_view: Boolean(recovery.forced_recent_not_view),
|
|
1492
|
-
card_count: recovery.card_count || 0
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
} catch (error) {
|
|
1496
|
-
closeRecoveryFailure = error;
|
|
1497
|
-
detailResult.cv_acquisition = {
|
|
1604
|
+
}
|
|
1605
|
+
if (detailResult && closeDetail && !detailResult.close_result?.closed) {
|
|
1606
|
+
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecommendDetail(client));
|
|
1607
|
+
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
1608
|
+
if (!detailResult.close_result?.closed) {
|
|
1609
|
+
closeFailureError = createRecommendCloseFailureError(detailResult.close_result);
|
|
1610
|
+
try {
|
|
1611
|
+
const recovery = await recoverAndReapplyRecommendContext("detail_close_failed", closeFailureError, {
|
|
1612
|
+
forceRecentNotView: true
|
|
1613
|
+
});
|
|
1614
|
+
detailResult.cv_acquisition = {
|
|
1615
|
+
...(detailResult.cv_acquisition || {}),
|
|
1616
|
+
close_recovery: {
|
|
1617
|
+
ok: Boolean(recovery.ok),
|
|
1618
|
+
method: recovery.method || "",
|
|
1619
|
+
forced_recent_not_view: Boolean(recovery.forced_recent_not_view),
|
|
1620
|
+
card_count: recovery.card_count || 0
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
closeRecoveryFailure = error;
|
|
1625
|
+
detailResult.cv_acquisition = {
|
|
1498
1626
|
...(detailResult.cv_acquisition || {}),
|
|
1499
1627
|
close_recovery: {
|
|
1500
1628
|
ok: false,
|
|
1501
1629
|
reason: "context_recovery_failed",
|
|
1502
1630
|
error: error?.message || String(error),
|
|
1503
|
-
forced_recent_not_view:
|
|
1631
|
+
forced_recent_not_view: Boolean(normalizedFilter.enabled)
|
|
1504
1632
|
}
|
|
1505
1633
|
};
|
|
1506
1634
|
}
|
|
@@ -1516,14 +1644,14 @@ export async function runRecommendWorkflow({
|
|
|
1516
1644
|
llm_screening: detailResult ? null : compactScreeningLlmResult(llmResult),
|
|
1517
1645
|
screening: compactScreening(screening),
|
|
1518
1646
|
action_discovery: compactActionDiscovery(actionDiscovery),
|
|
1519
|
-
post_action: postActionResult,
|
|
1520
|
-
error: recoverableDetailError
|
|
1521
|
-
? compactRecoverableDetailError(recoverableDetailError)
|
|
1522
|
-
: closeRecoveryFailure
|
|
1523
|
-
? compactError(closeFailureError, "DETAIL_CLOSE_FAILED")
|
|
1524
|
-
: detailResult?.image_evidence?.ok === false
|
|
1525
|
-
? compactError({
|
|
1526
|
-
code: detailResult.image_evidence.error_code,
|
|
1647
|
+
post_action: postActionResult,
|
|
1648
|
+
error: recoverableDetailError
|
|
1649
|
+
? compactRecoverableDetailError(recoverableDetailError)
|
|
1650
|
+
: closeRecoveryFailure
|
|
1651
|
+
? compactError(closeFailureError, "DETAIL_CLOSE_FAILED")
|
|
1652
|
+
: detailResult?.image_evidence?.ok === false
|
|
1653
|
+
? compactError({
|
|
1654
|
+
code: detailResult.image_evidence.error_code,
|
|
1527
1655
|
message: detailResult.image_evidence.error
|
|
1528
1656
|
}, "IMAGE_CAPTURE_FAILED")
|
|
1529
1657
|
: null,
|
|
@@ -1558,66 +1686,67 @@ export async function runRecommendWorkflow({
|
|
|
1558
1686
|
error: compactResult.error,
|
|
1559
1687
|
post_action: postActionResult
|
|
1560
1688
|
}
|
|
1561
|
-
});
|
|
1562
|
-
addTiming(compactResult.timings, "checkpoint_save_ms", Date.now() - checkpointStarted);
|
|
1563
|
-
|
|
1564
|
-
if (closeRecoveryFailure) {
|
|
1565
|
-
throw closeRecoveryFailure;
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
if (postActionResult?.stop_run) {
|
|
1569
|
-
listEndReason = postActionResult.reason || "post_action_stop";
|
|
1570
|
-
break;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
if (effectiveHumanRestEnabled) {
|
|
1574
|
-
const restStarted = Date.now();
|
|
1575
|
-
const restResult = await humanRestController.takeBreakIfNeeded({
|
|
1576
|
-
sleepFn: (ms) => runControl.sleep(ms)
|
|
1577
|
-
});
|
|
1578
|
-
const restElapsed = Date.now() - restStarted;
|
|
1579
|
-
if (restResult.rested) {
|
|
1580
|
-
recordHumanEvent({
|
|
1581
|
-
kind: "rest",
|
|
1582
|
-
pause_ms: restResult.pause_ms || restElapsed,
|
|
1583
|
-
events: restResult.events || []
|
|
1584
|
-
});
|
|
1585
|
-
compactResult.human_rest = restResult;
|
|
1586
|
-
addTiming(compactResult.timings, "human_rest_ms", restElapsed);
|
|
1587
|
-
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
1588
|
-
updateRecommendProgress({
|
|
1589
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1590
|
-
human_rest_last: restResult
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
if (delayMs > 0) {
|
|
1596
|
-
const sleepStarted = Date.now();
|
|
1597
|
-
await runControl.sleep(delayMs);
|
|
1598
|
-
addTiming(compactResult.timings, "sleep_ms", Date.now() - sleepStarted);
|
|
1599
|
-
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
1689
|
+
});
|
|
1690
|
+
addTiming(compactResult.timings, "checkpoint_save_ms", Date.now() - checkpointStarted);
|
|
1691
|
+
|
|
1692
|
+
if (closeRecoveryFailure) {
|
|
1693
|
+
throw closeRecoveryFailure;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
if (postActionResult?.stop_run) {
|
|
1697
|
+
listEndReason = postActionResult.reason || "post_action_stop";
|
|
1698
|
+
break;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
if (effectiveHumanRestEnabled) {
|
|
1702
|
+
const restStarted = Date.now();
|
|
1703
|
+
const restResult = await humanRestController.takeBreakIfNeeded({
|
|
1704
|
+
sleepFn: (ms) => runControl.sleep(ms)
|
|
1705
|
+
});
|
|
1706
|
+
const restElapsed = Date.now() - restStarted;
|
|
1707
|
+
if (restResult.rested) {
|
|
1708
|
+
recordHumanEvent({
|
|
1709
|
+
kind: "rest",
|
|
1710
|
+
pause_ms: restResult.pause_ms || restElapsed,
|
|
1711
|
+
events: restResult.events || []
|
|
1712
|
+
});
|
|
1713
|
+
compactResult.human_rest = restResult;
|
|
1714
|
+
addTiming(compactResult.timings, "human_rest_ms", restElapsed);
|
|
1715
|
+
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
1716
|
+
updateRecommendProgress({
|
|
1717
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1718
|
+
human_rest_last: restResult
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
if (delayMs > 0) {
|
|
1724
|
+
const sleepStarted = Date.now();
|
|
1725
|
+
await runControl.sleep(delayMs);
|
|
1726
|
+
addTiming(compactResult.timings, "sleep_ms", Date.now() - sleepStarted);
|
|
1727
|
+
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
1600
1728
|
}
|
|
1601
1729
|
}
|
|
1602
1730
|
|
|
1603
1731
|
runControl.setPhase("recommend:done");
|
|
1604
|
-
return {
|
|
1605
|
-
domain: "recommend",
|
|
1606
|
-
target_url: targetUrl,
|
|
1607
|
-
job_selection: compactJobSelection(jobSelection),
|
|
1608
|
-
page_scope: compactPageScopeSelection(pageScopeSelection),
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1732
|
+
return {
|
|
1733
|
+
domain: "recommend",
|
|
1734
|
+
target_url: targetUrl,
|
|
1735
|
+
job_selection: compactJobSelection(jobSelection),
|
|
1736
|
+
page_scope: compactPageScopeSelection(pageScopeSelection),
|
|
1737
|
+
current_city_only: compactCurrentCityOnlyResult(currentCityOnlyResult),
|
|
1738
|
+
filter: compactFilterResult(filterResult),
|
|
1739
|
+
card_count: cardNodeIds.length,
|
|
1740
|
+
candidate_list: compactInfiniteListState(listState),
|
|
1612
1741
|
viewport_health: {
|
|
1613
1742
|
stats: viewportGuard.getStats(),
|
|
1614
|
-
events: viewportGuard.getEvents()
|
|
1615
|
-
},
|
|
1616
|
-
human_behavior: effectiveHumanBehavior,
|
|
1617
|
-
human_rest: humanRestController.getState(),
|
|
1618
|
-
last_human_event: lastHumanEvent,
|
|
1619
|
-
list_end_reason: listEndReason || null,
|
|
1620
|
-
refresh_rounds: refreshRounds,
|
|
1743
|
+
events: viewportGuard.getEvents()
|
|
1744
|
+
},
|
|
1745
|
+
human_behavior: effectiveHumanBehavior,
|
|
1746
|
+
human_rest: humanRestController.getState(),
|
|
1747
|
+
last_human_event: lastHumanEvent,
|
|
1748
|
+
list_end_reason: listEndReason || null,
|
|
1749
|
+
refresh_rounds: refreshRounds,
|
|
1621
1750
|
refresh_attempts: refreshAttempts,
|
|
1622
1751
|
context_recoveries: contextRecoveryAttempts,
|
|
1623
1752
|
...countRecommendResultStatuses(results, { greetCount }),
|
|
@@ -1667,30 +1796,30 @@ export function createRecommendRunService({
|
|
|
1667
1796
|
actionIntervalMs = 500,
|
|
1668
1797
|
actionAfterClickDelayMs = 900,
|
|
1669
1798
|
screeningMode = "llm",
|
|
1670
|
-
llmConfig = null,
|
|
1671
|
-
llmTimeoutMs = 120000,
|
|
1672
|
-
llmImageLimit = 8,
|
|
1673
|
-
llmImageDetail = "high",
|
|
1674
|
-
imageOutputDir = "",
|
|
1675
|
-
humanRestEnabled = false,
|
|
1676
|
-
humanBehavior = null,
|
|
1677
|
-
skipRecentColleagueContacted = true,
|
|
1678
|
-
colleagueContactWindowDays = 14,
|
|
1679
|
-
name = "recommend-domain-run"
|
|
1680
|
-
} = {}) {
|
|
1681
|
-
if (!client) throw new Error("startRecommendRun requires a guarded CDP client");
|
|
1682
|
-
const normalizedFilter = normalizeFilter(filter);
|
|
1683
|
-
const normalizedPostAction = normalizeRecommendPostAction(postAction) || "none";
|
|
1684
|
-
const requestedPageScope = normalizeRecommendPageScope(pageScope) || "recommend";
|
|
1685
|
-
const normalizedFallbackPageScope = normalizeRecommendPageScope(fallbackPageScope) || "recommend";
|
|
1686
|
-
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
1687
|
-
const shouldSkipRecentColleagueContacted = skipRecentColleagueContacted !== false;
|
|
1688
|
-
const normalizedColleagueContactWindowDays = Math.max(1, Number(colleagueContactWindowDays) || 14);
|
|
1689
|
-
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
1690
|
-
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
1691
|
-
});
|
|
1692
|
-
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
1693
|
-
const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
1799
|
+
llmConfig = null,
|
|
1800
|
+
llmTimeoutMs = 120000,
|
|
1801
|
+
llmImageLimit = 8,
|
|
1802
|
+
llmImageDetail = "high",
|
|
1803
|
+
imageOutputDir = "",
|
|
1804
|
+
humanRestEnabled = false,
|
|
1805
|
+
humanBehavior = null,
|
|
1806
|
+
skipRecentColleagueContacted = true,
|
|
1807
|
+
colleagueContactWindowDays = 14,
|
|
1808
|
+
name = "recommend-domain-run"
|
|
1809
|
+
} = {}) {
|
|
1810
|
+
if (!client) throw new Error("startRecommendRun requires a guarded CDP client");
|
|
1811
|
+
const normalizedFilter = normalizeFilter(filter);
|
|
1812
|
+
const normalizedPostAction = normalizeRecommendPostAction(postAction) || "none";
|
|
1813
|
+
const requestedPageScope = normalizeRecommendPageScope(pageScope) || "recommend";
|
|
1814
|
+
const normalizedFallbackPageScope = normalizeRecommendPageScope(fallbackPageScope) || "recommend";
|
|
1815
|
+
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
1816
|
+
const shouldSkipRecentColleagueContacted = skipRecentColleagueContacted !== false;
|
|
1817
|
+
const normalizedColleagueContactWindowDays = Math.max(1, Number(colleagueContactWindowDays) || 14);
|
|
1818
|
+
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
1819
|
+
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
1820
|
+
});
|
|
1821
|
+
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
1822
|
+
const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
1694
1823
|
const normalizedDetailLimit = detailLimit == null ? null : Math.max(0, Number(detailLimit) || 0);
|
|
1695
1824
|
return manager.startRun({
|
|
1696
1825
|
runId,
|
|
@@ -1699,16 +1828,17 @@ export function createRecommendRunService({
|
|
|
1699
1828
|
context: {
|
|
1700
1829
|
domain: "recommend",
|
|
1701
1830
|
target_url: targetUrl,
|
|
1702
|
-
criteria_present: Boolean(criteria),
|
|
1703
|
-
job_label: jobLabel || "",
|
|
1704
|
-
requested_page_scope: requestedPageScope,
|
|
1705
|
-
fallback_page_scope: normalizedFallbackPageScope,
|
|
1706
|
-
filter: normalizedFilter,
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1831
|
+
criteria_present: Boolean(criteria),
|
|
1832
|
+
job_label: jobLabel || "",
|
|
1833
|
+
requested_page_scope: requestedPageScope,
|
|
1834
|
+
fallback_page_scope: normalizedFallbackPageScope,
|
|
1835
|
+
filter: normalizedFilter,
|
|
1836
|
+
current_city_only_requested: normalizedFilter.currentCityOnly,
|
|
1837
|
+
max_candidates: maxCandidates,
|
|
1838
|
+
max_candidates_semantics: "passed_candidates",
|
|
1839
|
+
detail_limit: normalizedDetailLimit,
|
|
1840
|
+
close_detail: closeDetail,
|
|
1841
|
+
cv_acquisition_mode: cvAcquisitionMode,
|
|
1712
1842
|
max_image_pages: maxImagePages,
|
|
1713
1843
|
image_wheel_delta_y: imageWheelDeltaY,
|
|
1714
1844
|
list_max_scrolls: listMaxScrolls,
|
|
@@ -1726,45 +1856,48 @@ export function createRecommendRunService({
|
|
|
1726
1856
|
action_timeout_ms: actionTimeoutMs,
|
|
1727
1857
|
screening_mode: normalizedScreeningMode,
|
|
1728
1858
|
llm_configured: Boolean(llmConfig),
|
|
1729
|
-
llm_timeout_ms: llmTimeoutMs,
|
|
1730
|
-
llm_image_limit: llmImageLimit,
|
|
1731
|
-
llm_image_detail: llmImageDetail,
|
|
1732
|
-
image_output_dir: imageOutputDir || "",
|
|
1733
|
-
skip_recent_colleague_contacted: shouldSkipRecentColleagueContacted,
|
|
1734
|
-
colleague_contact_window_days: normalizedColleagueContactWindowDays,
|
|
1735
|
-
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1736
|
-
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1737
|
-
human_behavior: effectiveHumanBehavior,
|
|
1738
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1739
|
-
human_rest_enabled: effectiveHumanRestEnabled
|
|
1740
|
-
},
|
|
1741
|
-
progress: {
|
|
1859
|
+
llm_timeout_ms: llmTimeoutMs,
|
|
1860
|
+
llm_image_limit: llmImageLimit,
|
|
1861
|
+
llm_image_detail: llmImageDetail,
|
|
1862
|
+
image_output_dir: imageOutputDir || "",
|
|
1863
|
+
skip_recent_colleague_contacted: shouldSkipRecentColleagueContacted,
|
|
1864
|
+
colleague_contact_window_days: normalizedColleagueContactWindowDays,
|
|
1865
|
+
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1866
|
+
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1867
|
+
human_behavior: effectiveHumanBehavior,
|
|
1868
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1869
|
+
human_rest_enabled: effectiveHumanRestEnabled
|
|
1870
|
+
},
|
|
1871
|
+
progress: {
|
|
1742
1872
|
card_count: 0,
|
|
1743
1873
|
target_count: candidateLimit,
|
|
1744
1874
|
target_count_semantics: "passed_candidates",
|
|
1745
1875
|
processed: 0,
|
|
1746
1876
|
screened: 0,
|
|
1747
1877
|
detail_opened: 0,
|
|
1748
|
-
llm_screened: 0,
|
|
1749
|
-
passed: 0,
|
|
1750
|
-
skipped: 0,
|
|
1751
|
-
greet_count: 0,
|
|
1878
|
+
llm_screened: 0,
|
|
1879
|
+
passed: 0,
|
|
1880
|
+
skipped: 0,
|
|
1881
|
+
greet_count: 0,
|
|
1752
1882
|
post_action_clicked: 0,
|
|
1753
|
-
image_capture_failed: 0,
|
|
1754
|
-
detail_open_failed: 0,
|
|
1883
|
+
image_capture_failed: 0,
|
|
1884
|
+
detail_open_failed: 0,
|
|
1755
1885
|
transient_recovered: 0,
|
|
1756
1886
|
colleague_contact_checked: 0,
|
|
1757
1887
|
recent_colleague_contact_skipped: 0,
|
|
1758
1888
|
colleague_contact_panel_missing: 0,
|
|
1759
1889
|
context_recoveries: 0,
|
|
1890
|
+
current_city_only_requested: normalizedFilter.currentCityOnly,
|
|
1891
|
+
current_city_only_effective: null,
|
|
1892
|
+
current_city_only_unavailable: false,
|
|
1760
1893
|
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1761
1894
|
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1762
1895
|
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1763
1896
|
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1764
1897
|
human_rest_count: 0,
|
|
1765
|
-
human_rest_ms: 0,
|
|
1766
|
-
last_human_event: null
|
|
1767
|
-
},
|
|
1898
|
+
human_rest_ms: 0,
|
|
1899
|
+
last_human_event: null
|
|
1900
|
+
},
|
|
1768
1901
|
checkpoint: {},
|
|
1769
1902
|
task: (runControl) => workflow({
|
|
1770
1903
|
client,
|
|
@@ -1799,17 +1932,17 @@ export function createRecommendRunService({
|
|
|
1799
1932
|
actionAfterClickDelayMs,
|
|
1800
1933
|
screeningMode: normalizedScreeningMode,
|
|
1801
1934
|
llmConfig,
|
|
1802
|
-
llmTimeoutMs,
|
|
1803
|
-
llmImageLimit,
|
|
1804
|
-
llmImageDetail,
|
|
1805
|
-
imageOutputDir,
|
|
1806
|
-
humanRestEnabled: effectiveHumanRestEnabled,
|
|
1807
|
-
humanBehavior: effectiveHumanBehavior,
|
|
1808
|
-
skipRecentColleagueContacted: shouldSkipRecentColleagueContacted,
|
|
1809
|
-
colleagueContactWindowDays: normalizedColleagueContactWindowDays
|
|
1810
|
-
}, runControl)
|
|
1811
|
-
});
|
|
1812
|
-
}
|
|
1935
|
+
llmTimeoutMs,
|
|
1936
|
+
llmImageLimit,
|
|
1937
|
+
llmImageDetail,
|
|
1938
|
+
imageOutputDir,
|
|
1939
|
+
humanRestEnabled: effectiveHumanRestEnabled,
|
|
1940
|
+
humanBehavior: effectiveHumanBehavior,
|
|
1941
|
+
skipRecentColleagueContacted: shouldSkipRecentColleagueContacted,
|
|
1942
|
+
colleagueContactWindowDays: normalizedColleagueContactWindowDays
|
|
1943
|
+
}, runControl)
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1813
1946
|
|
|
1814
1947
|
return {
|
|
1815
1948
|
startRecommendRun,
|