@reconcrap/boss-recommend-mcp 2.1.14 → 2.1.15
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 -5
- package/package.json +1 -1
- package/skills/boss-chat/README.md +2 -2
- package/skills/boss-chat/SKILL.md +7 -7
- package/skills/boss-recruit-pipeline/SKILL.md +23 -1
- package/src/chat-mcp.js +70 -73
- package/src/domains/chat/detail.js +79 -47
- package/src/domains/chat/run-service.js +400 -158
- package/src/domains/recruit/constants.js +65 -0
- package/src/domains/recruit/instruction-parser.js +362 -86
- package/src/domains/recruit/run-service.js +281 -10
- package/src/domains/recruit/search.js +2076 -298
- package/src/index.js +18 -12
- package/src/recommend-mcp.js +77 -8
- package/src/recruit-mcp.js +228 -3
|
@@ -53,6 +53,10 @@ import {
|
|
|
53
53
|
openRecruitCardDetail,
|
|
54
54
|
waitForRecruitDetailNetworkEvents
|
|
55
55
|
} from "./detail.js";
|
|
56
|
+
import {
|
|
57
|
+
clickRecruitActionControl,
|
|
58
|
+
waitForRecruitDetailActionControls
|
|
59
|
+
} from "./actions.js";
|
|
56
60
|
import {
|
|
57
61
|
readRecruitCardCandidate,
|
|
58
62
|
waitForRecruitCardNodeIds
|
|
@@ -70,6 +74,7 @@ import {
|
|
|
70
74
|
RECRUIT_CARD_SELECTOR,
|
|
71
75
|
RECRUIT_LIST_CONTAINER_SELECTORS
|
|
72
76
|
} from "./constants.js";
|
|
77
|
+
import { GREET_CREDITS_EXHAUSTED_CODE } from "../../core/greet-quota/index.js";
|
|
73
78
|
|
|
74
79
|
function compactScreening(screening) {
|
|
75
80
|
return {
|
|
@@ -120,6 +125,164 @@ function createMissingLlmConfigResult() {
|
|
|
120
125
|
return createFailedLlmScreeningResult(new Error("LLM screening config is required for production search runs"));
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
function normalizeRecruitPostAction(value) {
|
|
129
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
130
|
+
if (["", "none", "skip", "no", "不执行", "无", "什么也不做"].includes(normalized)) return "none";
|
|
131
|
+
if (["greet", "chat", "打招呼", "直接沟通", "沟通"].includes(normalized)) return "greet";
|
|
132
|
+
return "none";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveRecruitPostAction({
|
|
136
|
+
postAction = "none",
|
|
137
|
+
greetCount = 0,
|
|
138
|
+
maxGreetCount = null
|
|
139
|
+
} = {}) {
|
|
140
|
+
const requested = normalizeRecruitPostAction(postAction);
|
|
141
|
+
const currentGreetCount = Number.isInteger(greetCount) && greetCount >= 0 ? greetCount : 0;
|
|
142
|
+
const limit = Number.isInteger(maxGreetCount) && maxGreetCount > 0 ? maxGreetCount : null;
|
|
143
|
+
if (requested === "greet" && limit !== null && currentGreetCount >= limit) {
|
|
144
|
+
return {
|
|
145
|
+
requested,
|
|
146
|
+
effective: "none",
|
|
147
|
+
reason: "greet_limit_reached",
|
|
148
|
+
greet_count: currentGreetCount,
|
|
149
|
+
max_greet_count: limit
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
requested,
|
|
154
|
+
effective: requested,
|
|
155
|
+
reason: "requested_action",
|
|
156
|
+
greet_count: currentGreetCount,
|
|
157
|
+
max_greet_count: limit
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function compactActionDiscovery(discovery) {
|
|
162
|
+
if (!discovery) return null;
|
|
163
|
+
return {
|
|
164
|
+
ok: Boolean(discovery.ok),
|
|
165
|
+
elapsed_ms: discovery.elapsed_ms,
|
|
166
|
+
summary: discovery.summary || null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function runRecruitPostAction({
|
|
171
|
+
client,
|
|
172
|
+
rootNodeIds = [],
|
|
173
|
+
screening,
|
|
174
|
+
actionDiscovery,
|
|
175
|
+
postAction = "none",
|
|
176
|
+
greetCount = 0,
|
|
177
|
+
maxGreetCount = null,
|
|
178
|
+
executePostAction = true,
|
|
179
|
+
afterClickDelayMs = 900
|
|
180
|
+
} = {}) {
|
|
181
|
+
const plan = resolveRecruitPostAction({
|
|
182
|
+
postAction,
|
|
183
|
+
greetCount,
|
|
184
|
+
maxGreetCount
|
|
185
|
+
});
|
|
186
|
+
const result = {
|
|
187
|
+
requested: postAction,
|
|
188
|
+
execute_post_action: Boolean(executePostAction),
|
|
189
|
+
plan,
|
|
190
|
+
eligible: Boolean(screening?.passed),
|
|
191
|
+
action_attempted: false,
|
|
192
|
+
action_clicked: false,
|
|
193
|
+
counted_as_greet: false,
|
|
194
|
+
reason: ""
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
if (!screening?.passed) {
|
|
198
|
+
result.reason = "screening_not_passed";
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
if (plan.effective === "none") {
|
|
202
|
+
result.reason = plan.reason === "greet_limit_reached" ? "greet_limit_reached" : "post_action_none";
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const summary = actionDiscovery?.summary || {};
|
|
207
|
+
const control = summary.greet?.control || summary.greet;
|
|
208
|
+
if (!control?.found && !control?.node_id) {
|
|
209
|
+
result.reason = `${plan.effective}_control_not_found`;
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
result.control = control;
|
|
213
|
+
|
|
214
|
+
if (plan.effective === "greet" && control.continue_chat) {
|
|
215
|
+
result.reason = "already_connected_continue_chat";
|
|
216
|
+
result.already_connected = true;
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
if (plan.effective === "greet" && control.greet_quota?.exhausted) {
|
|
220
|
+
result.reason = "greet_credits_exhausted";
|
|
221
|
+
result.out_of_greet_credits = true;
|
|
222
|
+
result.stop_run = true;
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
if (plan.effective === "greet" && control.available === false) {
|
|
226
|
+
result.reason = "greet_control_not_available";
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
if (control.disabled) {
|
|
230
|
+
result.reason = `${plan.effective}_control_disabled`;
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
if (!executePostAction) {
|
|
234
|
+
result.reason = "dry_run_post_action";
|
|
235
|
+
result.would_click = true;
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
result.action_attempted = true;
|
|
240
|
+
result.control_before = control;
|
|
241
|
+
let clickResult;
|
|
242
|
+
try {
|
|
243
|
+
clickResult = await clickRecruitActionControl(client, {
|
|
244
|
+
...control,
|
|
245
|
+
kind: plan.effective
|
|
246
|
+
});
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error?.code === GREET_CREDITS_EXHAUSTED_CODE) {
|
|
249
|
+
result.reason = "greet_credits_exhausted";
|
|
250
|
+
result.out_of_greet_credits = true;
|
|
251
|
+
result.stop_run = true;
|
|
252
|
+
result.greet_quota = error.greet_quota || control.greet_quota || null;
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
result.click_result = clickResult;
|
|
258
|
+
result.action_clicked = true;
|
|
259
|
+
result.counted_as_greet = plan.effective === "greet";
|
|
260
|
+
result.reason = "clicked";
|
|
261
|
+
if (afterClickDelayMs > 0) await sleep(afterClickDelayMs);
|
|
262
|
+
try {
|
|
263
|
+
const afterDiscovery = await waitForRecruitDetailActionControls(client, {
|
|
264
|
+
rootNodeIds,
|
|
265
|
+
timeoutMs: 2500,
|
|
266
|
+
intervalMs: 300,
|
|
267
|
+
requireAny: false
|
|
268
|
+
});
|
|
269
|
+
const afterControl = afterDiscovery?.summary?.greet?.control || afterDiscovery?.summary?.greet || null;
|
|
270
|
+
result.action_discovery_after = compactActionDiscovery(afterDiscovery);
|
|
271
|
+
result.control_after = afterControl;
|
|
272
|
+
if (plan.effective === "greet") {
|
|
273
|
+
result.verified_after_click = Boolean(
|
|
274
|
+
afterControl?.continue_chat
|
|
275
|
+
|| String(afterControl?.label || "").includes("继续沟通")
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
result.verify_error = {
|
|
280
|
+
message: error?.message || String(error)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
123
286
|
function normalizeSearchParams(searchParams = {}) {
|
|
124
287
|
return normalizeRecruitSearchParams(searchParams);
|
|
125
288
|
}
|
|
@@ -328,13 +491,17 @@ function createRecoverableDetailFailureScreening(candidate, error) {
|
|
|
328
491
|
};
|
|
329
492
|
}
|
|
330
493
|
|
|
331
|
-
export function countRecruitResultStatuses(results = []
|
|
494
|
+
export function countRecruitResultStatuses(results = [], {
|
|
495
|
+
greetCount = 0
|
|
496
|
+
} = {}) {
|
|
332
497
|
return {
|
|
333
498
|
processed: results.length,
|
|
334
499
|
screened: results.length,
|
|
335
500
|
detail_opened: results.filter((item) => item.detail).length,
|
|
336
501
|
passed: results.filter((item) => item.screening?.passed).length,
|
|
337
502
|
llm_screened: results.filter((item) => item.detail?.llm_screening || item.llm_screening).length,
|
|
503
|
+
greet_count: greetCount,
|
|
504
|
+
post_action_clicked: results.filter((item) => item.post_action?.action_clicked).length,
|
|
338
505
|
image_capture_failed: results.filter((item) => item.detail?.image_evidence?.ok === false).length,
|
|
339
506
|
detail_open_failed: results.filter((item) => (
|
|
340
507
|
item.error?.code === "DETAIL_STALE_NODE"
|
|
@@ -380,7 +547,13 @@ export async function runRecruitWorkflow({
|
|
|
380
547
|
llmImageDetail = "high",
|
|
381
548
|
imageOutputDir = "",
|
|
382
549
|
humanRestEnabled = false,
|
|
383
|
-
humanBehavior = null
|
|
550
|
+
humanBehavior = null,
|
|
551
|
+
postAction = "none",
|
|
552
|
+
maxGreetCount = null,
|
|
553
|
+
executePostAction = true,
|
|
554
|
+
actionTimeoutMs = 8000,
|
|
555
|
+
actionIntervalMs = 400,
|
|
556
|
+
actionAfterClickDelayMs = 900
|
|
384
557
|
} = {}, runControl) {
|
|
385
558
|
if (!client) throw new Error("runRecruitWorkflow requires a guarded CDP client");
|
|
386
559
|
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
@@ -402,6 +575,8 @@ export async function runRecruitWorkflow({
|
|
|
402
575
|
});
|
|
403
576
|
const normalizedSearchParams = normalizeSearchParams(searchParams);
|
|
404
577
|
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
578
|
+
const normalizedPostAction = normalizeRecruitPostAction(postAction);
|
|
579
|
+
const postActionEnabled = normalizedPostAction !== "none";
|
|
405
580
|
const useLlmScreening = normalizedScreeningMode !== "deterministic";
|
|
406
581
|
const limit = Math.max(1, Number(maxCandidates) || 1);
|
|
407
582
|
const detailCountLimit = detailLimit == null ? limit : Math.max(0, Number(detailLimit) || 0);
|
|
@@ -427,6 +602,7 @@ export async function runRecruitWorkflow({
|
|
|
427
602
|
}
|
|
428
603
|
const results = [];
|
|
429
604
|
const refreshAttempts = [];
|
|
605
|
+
let greetCount = 0;
|
|
430
606
|
let refreshRounds = 0;
|
|
431
607
|
let contextRecoveryAttempts = 0;
|
|
432
608
|
const candidateRecoveryCounts = new Map();
|
|
@@ -470,7 +646,7 @@ export async function runRecruitWorkflow({
|
|
|
470
646
|
}
|
|
471
647
|
|
|
472
648
|
function updateRecruitProgress(extra = {}) {
|
|
473
|
-
const counts = countRecruitResultStatuses(results);
|
|
649
|
+
const counts = countRecruitResultStatuses(results, { greetCount });
|
|
474
650
|
const listSnapshot = compactInfiniteListState(listState);
|
|
475
651
|
const humanRestState = humanRestController.getState();
|
|
476
652
|
runControl.updateProgress({
|
|
@@ -510,7 +686,7 @@ export async function runRecruitWorkflow({
|
|
|
510
686
|
key: candidateKey,
|
|
511
687
|
card_node_id: cardNodeId,
|
|
512
688
|
detail_step: detailStep || null,
|
|
513
|
-
counters: countRecruitResultStatuses(results),
|
|
689
|
+
counters: countRecruitResultStatuses(results, { greetCount }),
|
|
514
690
|
error: compactError(error, "RECRUIT_IN_PROGRESS_ERROR")
|
|
515
691
|
},
|
|
516
692
|
candidate_list: compactInfiniteListState(listState)
|
|
@@ -566,7 +742,7 @@ export async function runRecruitWorkflow({
|
|
|
566
742
|
reason,
|
|
567
743
|
trigger_error: compactError(error, "RECRUIT_CONTEXT_RECOVERY_TRIGGER"),
|
|
568
744
|
refresh: compactRefresh,
|
|
569
|
-
counters: countRecruitResultStatuses(results)
|
|
745
|
+
counters: countRecruitResultStatuses(results, { greetCount })
|
|
570
746
|
},
|
|
571
747
|
candidate_list: compactInfiniteListState(listState)
|
|
572
748
|
});
|
|
@@ -596,7 +772,7 @@ export async function runRecruitWorkflow({
|
|
|
596
772
|
metadata: {
|
|
597
773
|
card_count: cardNodeIds.length,
|
|
598
774
|
forced_recent_viewed: forceRecentViewed,
|
|
599
|
-
counters: countRecruitResultStatuses(results)
|
|
775
|
+
counters: countRecruitResultStatuses(results, { greetCount })
|
|
600
776
|
}
|
|
601
777
|
});
|
|
602
778
|
listEndReason = "";
|
|
@@ -782,6 +958,7 @@ export async function runRecruitWorkflow({
|
|
|
782
958
|
|
|
783
959
|
let screeningCandidate = cardCandidate;
|
|
784
960
|
let detailResult = null;
|
|
961
|
+
let detailActionRootNodeIds = [];
|
|
785
962
|
let recoverableDetailError = null;
|
|
786
963
|
let detailStep = "not_started";
|
|
787
964
|
if (index < detailCountLimit) {
|
|
@@ -815,6 +992,7 @@ export async function runRecruitWorkflow({
|
|
|
815
992
|
networkRecorder.clear();
|
|
816
993
|
await maybeHumanActionCooldown("before_detail_open", timings);
|
|
817
994
|
const openedDetail = await openRecruitCardDetail(client, cardNodeId);
|
|
995
|
+
detailActionRootNodeIds = (openedDetail.detail_state?.roots || []).map((root) => root.nodeId).filter(Boolean);
|
|
818
996
|
addTiming(timings, "candidate_click_ms", openedDetail.timings?.candidate_click_ms);
|
|
819
997
|
addTiming(timings, "detail_open_ms", openedDetail.timings?.detail_open_ms);
|
|
820
998
|
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
@@ -957,7 +1135,7 @@ export async function runRecruitWorkflow({
|
|
|
957
1135
|
capture_target_wait: captureTargetWait
|
|
958
1136
|
};
|
|
959
1137
|
screeningCandidate = detailResult.candidate;
|
|
960
|
-
if (closeDetail) {
|
|
1138
|
+
if (closeDetail && !postActionEnabled) {
|
|
961
1139
|
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecruitDetail(client));
|
|
962
1140
|
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
963
1141
|
if (!detailResult.close_result?.closed) {
|
|
@@ -1045,6 +1223,70 @@ export async function runRecruitWorkflow({
|
|
|
1045
1223
|
: useLlmScreening
|
|
1046
1224
|
? llmResultToScreening(llmResult, screeningCandidate)
|
|
1047
1225
|
: screenCandidate(screeningCandidate, { criteria });
|
|
1226
|
+
let actionDiscovery = null;
|
|
1227
|
+
let postActionResult = null;
|
|
1228
|
+
let closeFailureError = null;
|
|
1229
|
+
let closeRecoveryFailure = null;
|
|
1230
|
+
if (postActionEnabled && detailResult) {
|
|
1231
|
+
const postActionStarted = Date.now();
|
|
1232
|
+
await runControl.waitIfPaused();
|
|
1233
|
+
runControl.throwIfCanceled();
|
|
1234
|
+
runControl.setPhase("recruit:post-action");
|
|
1235
|
+
await maybeHumanActionCooldown("before_post_action", timings);
|
|
1236
|
+
actionDiscovery = await waitForRecruitDetailActionControls(client, {
|
|
1237
|
+
rootNodeIds: detailActionRootNodeIds,
|
|
1238
|
+
timeoutMs: actionTimeoutMs,
|
|
1239
|
+
intervalMs: actionIntervalMs,
|
|
1240
|
+
requireAny: true
|
|
1241
|
+
});
|
|
1242
|
+
postActionResult = await runRecruitPostAction({
|
|
1243
|
+
client,
|
|
1244
|
+
rootNodeIds: detailActionRootNodeIds,
|
|
1245
|
+
screening,
|
|
1246
|
+
actionDiscovery,
|
|
1247
|
+
postAction: normalizedPostAction,
|
|
1248
|
+
greetCount,
|
|
1249
|
+
maxGreetCount: Number.isInteger(maxGreetCount) ? maxGreetCount : null,
|
|
1250
|
+
executePostAction,
|
|
1251
|
+
afterClickDelayMs: actionAfterClickDelayMs
|
|
1252
|
+
});
|
|
1253
|
+
if (postActionResult.counted_as_greet && postActionResult.action_clicked) {
|
|
1254
|
+
greetCount += 1;
|
|
1255
|
+
}
|
|
1256
|
+
addTiming(timings, "post_action_ms", Date.now() - postActionStarted);
|
|
1257
|
+
}
|
|
1258
|
+
if (postActionEnabled && detailResult && closeDetail) {
|
|
1259
|
+
detailResult.close_result = await measureTiming(timings, "close_detail_ms", () => closeRecruitDetail(client));
|
|
1260
|
+
await maybeHumanActionCooldown("after_detail_close", timings);
|
|
1261
|
+
if (!detailResult.close_result?.closed) {
|
|
1262
|
+
closeFailureError = createRecruitCloseFailureError(detailResult.close_result);
|
|
1263
|
+
try {
|
|
1264
|
+
const recovery = await recoverAndReapplyRecruitContext("detail_close_failed", closeFailureError, {
|
|
1265
|
+
forceRecentViewed: true
|
|
1266
|
+
});
|
|
1267
|
+
detailResult.cv_acquisition = {
|
|
1268
|
+
...(detailResult.cv_acquisition || {}),
|
|
1269
|
+
close_recovery: {
|
|
1270
|
+
ok: Boolean(recovery.ok),
|
|
1271
|
+
method: recovery.method || "",
|
|
1272
|
+
forced_recent_viewed: Boolean(recovery.forced_recent_viewed),
|
|
1273
|
+
card_count: recovery.card_count || 0
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
closeRecoveryFailure = error;
|
|
1278
|
+
detailResult.cv_acquisition = {
|
|
1279
|
+
...(detailResult.cv_acquisition || {}),
|
|
1280
|
+
close_recovery: {
|
|
1281
|
+
ok: false,
|
|
1282
|
+
reason: "context_recovery_failed",
|
|
1283
|
+
error: error?.message || String(error),
|
|
1284
|
+
forced_recent_viewed: true
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1048
1290
|
timings.total_ms = Date.now() - candidateStarted;
|
|
1049
1291
|
const compactResult = {
|
|
1050
1292
|
index,
|
|
@@ -1054,8 +1296,12 @@ export async function runRecruitWorkflow({
|
|
|
1054
1296
|
detail: compactDetail(detailResult),
|
|
1055
1297
|
llm_screening: detailResult ? null : compactScreeningLlmResult(llmResult),
|
|
1056
1298
|
screening: compactScreening(screening),
|
|
1299
|
+
action_discovery: compactActionDiscovery(actionDiscovery),
|
|
1300
|
+
post_action: postActionResult,
|
|
1057
1301
|
error: recoverableDetailError
|
|
1058
1302
|
? compactRecoverableDetailError(recoverableDetailError)
|
|
1303
|
+
: closeRecoveryFailure
|
|
1304
|
+
? compactError(closeFailureError, "DETAIL_CLOSE_FAILED")
|
|
1059
1305
|
: detailResult?.image_evidence?.ok === false
|
|
1060
1306
|
? compactError({
|
|
1061
1307
|
code: detailResult.image_evidence.error_code,
|
|
@@ -1123,6 +1369,10 @@ export async function runRecruitWorkflow({
|
|
|
1123
1369
|
addTiming(compactResult.timings, "sleep_ms", Date.now() - sleepStarted);
|
|
1124
1370
|
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
1125
1371
|
}
|
|
1372
|
+
if (postActionResult?.stop_run) {
|
|
1373
|
+
listEndReason = postActionResult.reason || "post_action_stop";
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1126
1376
|
}
|
|
1127
1377
|
|
|
1128
1378
|
runControl.setPhase("recruit:done");
|
|
@@ -1143,7 +1393,7 @@ export async function runRecruitWorkflow({
|
|
|
1143
1393
|
refresh_rounds: refreshRounds,
|
|
1144
1394
|
refresh_attempts: refreshAttempts,
|
|
1145
1395
|
context_recoveries: contextRecoveryAttempts,
|
|
1146
|
-
...countRecruitResultStatuses(results),
|
|
1396
|
+
...countRecruitResultStatuses(results, { greetCount }),
|
|
1147
1397
|
results
|
|
1148
1398
|
};
|
|
1149
1399
|
}
|
|
@@ -1189,11 +1439,18 @@ export function createRecruitRunService({
|
|
|
1189
1439
|
imageOutputDir = "",
|
|
1190
1440
|
humanRestEnabled = false,
|
|
1191
1441
|
humanBehavior = null,
|
|
1442
|
+
postAction = "none",
|
|
1443
|
+
maxGreetCount = null,
|
|
1444
|
+
executePostAction = true,
|
|
1445
|
+
actionTimeoutMs = 8000,
|
|
1446
|
+
actionIntervalMs = 400,
|
|
1447
|
+
actionAfterClickDelayMs = 900,
|
|
1192
1448
|
name = "recruit-domain-run"
|
|
1193
1449
|
} = {}) {
|
|
1194
1450
|
if (!client) throw new Error("startRecruitRun requires a guarded CDP client");
|
|
1195
1451
|
const normalizedSearchParams = normalizeSearchParams(searchParams);
|
|
1196
1452
|
const normalizedScreeningMode = normalizeScreeningMode(screeningMode);
|
|
1453
|
+
const normalizedPostAction = normalizeRecruitPostAction(postAction);
|
|
1197
1454
|
const candidateLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
1198
1455
|
const normalizedDetailLimit = detailLimit == null ? candidateLimit : Math.max(0, Number(detailLimit) || 0);
|
|
1199
1456
|
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
@@ -1235,7 +1492,13 @@ export function createRecruitRunService({
|
|
|
1235
1492
|
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1236
1493
|
human_behavior: effectiveHumanBehavior,
|
|
1237
1494
|
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1238
|
-
human_rest_enabled: effectiveHumanRestEnabled
|
|
1495
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1496
|
+
post_action: normalizedPostAction,
|
|
1497
|
+
max_greet_count: Number.isInteger(maxGreetCount) ? maxGreetCount : null,
|
|
1498
|
+
execute_post_action: Boolean(executePostAction),
|
|
1499
|
+
action_timeout_ms: actionTimeoutMs,
|
|
1500
|
+
action_interval_ms: actionIntervalMs,
|
|
1501
|
+
action_after_click_delay_ms: actionAfterClickDelayMs
|
|
1239
1502
|
},
|
|
1240
1503
|
progress: {
|
|
1241
1504
|
card_count: 0,
|
|
@@ -1255,6 +1518,8 @@ export function createRecruitRunService({
|
|
|
1255
1518
|
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1256
1519
|
human_rest_count: 0,
|
|
1257
1520
|
human_rest_ms: 0,
|
|
1521
|
+
greet_count: 0,
|
|
1522
|
+
post_action_clicked: 0,
|
|
1258
1523
|
last_human_event: null
|
|
1259
1524
|
},
|
|
1260
1525
|
checkpoint: {},
|
|
@@ -1289,7 +1554,13 @@ export function createRecruitRunService({
|
|
|
1289
1554
|
llmImageDetail,
|
|
1290
1555
|
imageOutputDir,
|
|
1291
1556
|
humanRestEnabled: effectiveHumanRestEnabled,
|
|
1292
|
-
humanBehavior: effectiveHumanBehavior
|
|
1557
|
+
humanBehavior: effectiveHumanBehavior,
|
|
1558
|
+
postAction: normalizedPostAction,
|
|
1559
|
+
maxGreetCount,
|
|
1560
|
+
executePostAction,
|
|
1561
|
+
actionTimeoutMs,
|
|
1562
|
+
actionIntervalMs,
|
|
1563
|
+
actionAfterClickDelayMs
|
|
1293
1564
|
}, runControl)
|
|
1294
1565
|
});
|
|
1295
1566
|
}
|