@reconcrap/boss-recommend-mcp 2.1.22 → 2.1.24
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 -0
- package/bin/boss-recommend-mcp.js +4 -4
- package/package.json +14 -8
- package/scripts/install-macos.sh +280 -280
- package/scripts/postinstall.cjs +44 -44
- package/skills/boss-chat/README.md +43 -42
- package/skills/boss-chat/SKILL.md +107 -106
- package/skills/boss-recommend-pipeline/README.md +13 -13
- package/skills/boss-recommend-pipeline/SKILL.md +47 -47
- package/skills/boss-recruit-pipeline/README.md +19 -19
- package/skills/boss-recruit-pipeline/SKILL.md +89 -89
- package/src/chat-mcp.js +301 -127
- package/src/core/boss-cards/index.js +199 -199
- package/src/core/browser/index.js +291 -114
- package/src/core/capture/index.js +2930 -1201
- package/src/core/cv-acquisition/index.js +512 -238
- package/src/core/cv-capture-target/index.js +513 -299
- package/src/core/greet-quota/index.js +71 -71
- package/src/core/infinite-list/index.js +11 -2
- package/src/core/reporting/legacy-csv.js +12 -12
- package/src/core/run/detached-launcher.js +305 -0
- package/src/core/run/index.js +112 -42
- package/src/core/run/timing.js +33 -33
- package/src/core/run/windows-detached-worker.ps1 +106 -0
- package/src/core/screening/index.js +2135 -2135
- package/src/core/self-heal/index.js +989 -973
- package/src/core/self-heal/viewport.js +1505 -564
- package/src/detached-worker.js +99 -99
- package/src/domains/chat/action-journal.js +443 -0
- package/src/domains/chat/cards.js +137 -137
- package/src/domains/chat/constants.js +9 -9
- package/src/domains/chat/detail.js +1684 -401
- package/src/domains/chat/index.js +8 -7
- package/src/domains/chat/jobs.js +620 -620
- package/src/domains/chat/page-guard.js +157 -122
- package/src/domains/chat/roots.js +56 -56
- package/src/domains/chat/run-service.js +1801 -760
- 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 +515 -472
- package/src/domains/recommend/cards.js +243 -243
- package/src/domains/recommend/colleague-contact.js +333 -333
- package/src/domains/recommend/constants.js +92 -92
- package/src/domains/recommend/detail.js +12 -3
- package/src/domains/recommend/filters.js +611 -611
- package/src/domains/recommend/index.js +9 -9
- package/src/domains/recommend/jobs.js +542 -542
- package/src/domains/recommend/location.js +736 -736
- package/src/domains/recommend/refresh.js +410 -329
- package/src/domains/recommend/roots.js +80 -80
- package/src/domains/recommend/run-service.js +1783 -592
- 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 +1817 -1620
- package/src/domains/recruit/search.js +3229 -3229
- package/src/index.js +124 -5
- package/src/parser.js +1296 -1296
- package/src/recommend-mcp.js +515 -80
- package/src/recommend-scheduler.js +66 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
captureScrolledNodeScreenshots,
|
|
3
|
-
captureViewportScreenshot
|
|
4
|
-
} from "../../core/capture/index.js";
|
|
1
|
+
import {
|
|
2
|
+
captureScrolledNodeScreenshots,
|
|
3
|
+
captureViewportScreenshot
|
|
4
|
+
} from "../../core/capture/index.js";
|
|
5
5
|
import { waitForCvCaptureTarget } from "../../core/cv-capture-target/index.js";
|
|
6
6
|
import {
|
|
7
7
|
clickPoint,
|
|
@@ -13,16 +13,24 @@ import {
|
|
|
13
13
|
scrollNodeIntoView,
|
|
14
14
|
sleep
|
|
15
15
|
} from "../../core/browser/index.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
attemptImageCaptureCheckpointResume,
|
|
18
|
+
compactCvAcquisitionState,
|
|
19
|
+
createImageCaptureWorkflowRetryTracker,
|
|
20
|
+
createRequiredImageEvidenceFailure,
|
|
21
|
+
imageCaptureResumeCheckpoint,
|
|
18
22
|
countParsedNetworkProfiles,
|
|
19
23
|
createCvAcquisitionState,
|
|
20
24
|
DEFAULT_MAX_IMAGE_PAGES,
|
|
21
|
-
getCvNetworkWaitPlan,
|
|
22
|
-
|
|
25
|
+
getCvNetworkWaitPlan,
|
|
26
|
+
isIncompleteImageEvidence,
|
|
27
|
+
isRecoverableImageCaptureWorkflowError,
|
|
28
|
+
recordCvImageFallback,
|
|
23
29
|
recordCvNetworkHit,
|
|
24
|
-
recordCvNetworkMiss,
|
|
25
|
-
|
|
30
|
+
recordCvNetworkMiss,
|
|
31
|
+
reacquireImageCaptureResumeTarget,
|
|
32
|
+
requireCompleteImageEvidence,
|
|
33
|
+
summarizeImageEvidence,
|
|
26
34
|
waitForCvNetworkEvents
|
|
27
35
|
} from "../../core/cv-acquisition/index.js";
|
|
28
36
|
import {
|
|
@@ -35,66 +43,323 @@ import {
|
|
|
35
43
|
resolveInfiniteListFallbackPoint
|
|
36
44
|
} from "../../core/infinite-list/index.js";
|
|
37
45
|
import { createViewportRunGuard } from "../../core/self-heal/index.js";
|
|
38
|
-
import {
|
|
39
|
-
createRunLifecycleManager,
|
|
40
|
-
RunCanceledError
|
|
41
|
-
} from "../../core/run/index.js";
|
|
46
|
+
import {
|
|
47
|
+
createRunLifecycleManager,
|
|
48
|
+
RunCanceledError
|
|
49
|
+
} from "../../core/run/index.js";
|
|
42
50
|
import {
|
|
43
51
|
addTiming,
|
|
44
52
|
imageEvidenceFilePath,
|
|
45
53
|
measureTiming
|
|
46
54
|
} from "../../core/run/timing.js";
|
|
47
|
-
import {
|
|
48
|
-
callScreeningLlm,
|
|
49
|
-
createFatalLlmRunError,
|
|
50
|
-
isFatalLlmProviderError,
|
|
51
|
-
normalizeText,
|
|
52
|
-
screenCandidate
|
|
53
|
-
} from "../../core/screening/index.js";
|
|
55
|
+
import {
|
|
56
|
+
callScreeningLlm,
|
|
57
|
+
createFatalLlmRunError,
|
|
58
|
+
isFatalLlmProviderError,
|
|
59
|
+
normalizeText,
|
|
60
|
+
screenCandidate
|
|
61
|
+
} from "../../core/screening/index.js";
|
|
54
62
|
import {
|
|
55
63
|
CHAT_BOTTOM_MARKER_SELECTORS,
|
|
56
64
|
CHAT_CARD_SELECTORS,
|
|
57
65
|
CHAT_LIST_CONTAINER_SELECTORS,
|
|
58
66
|
CHAT_TARGET_URL
|
|
59
67
|
} from "./constants.js";
|
|
60
|
-
import {
|
|
61
|
-
chatCandidateKeyFromProfile,
|
|
68
|
+
import {
|
|
69
|
+
chatCandidateKeyFromProfile,
|
|
62
70
|
findChatCandidateNodeIdById,
|
|
63
71
|
readChatCardCandidate,
|
|
64
|
-
waitForChatCandidateNodeIds
|
|
65
|
-
} from "./cards.js";
|
|
66
|
-
import {
|
|
72
|
+
waitForChatCandidateNodeIds
|
|
73
|
+
} from "./cards.js";
|
|
74
|
+
import { createChatActionJournal } from "./action-journal.js";
|
|
75
|
+
import {
|
|
67
76
|
closeChatBlockingPanels,
|
|
68
77
|
closeChatResumeModal,
|
|
69
78
|
createChatProfileNetworkRecorder,
|
|
70
|
-
extractChatProfileCandidate,
|
|
71
|
-
isChatOnlineResumeModalOpenFailureError,
|
|
72
|
-
isUnsafeChatOnlineResumeLinkError,
|
|
73
|
-
openChatOnlineResume,
|
|
74
|
-
quickChatResumeModalOpenProbe,
|
|
75
|
-
readChatActiveCandidateState,
|
|
76
|
-
readChatConversationReadyState,
|
|
77
|
-
|
|
79
|
+
extractChatProfileCandidate,
|
|
80
|
+
isChatOnlineResumeModalOpenFailureError,
|
|
81
|
+
isUnsafeChatOnlineResumeLinkError,
|
|
82
|
+
openChatOnlineResume,
|
|
83
|
+
quickChatResumeModalOpenProbe,
|
|
84
|
+
readChatActiveCandidateState,
|
|
85
|
+
readChatConversationReadyState,
|
|
86
|
+
readChatRequestVerificationEvidence,
|
|
87
|
+
getChatGreetingMessageState,
|
|
88
|
+
requestChatResumeForPassedCandidate,
|
|
78
89
|
selectChatMessageFilter,
|
|
79
90
|
selectChatPrimaryLabel,
|
|
80
91
|
waitForChatOnlineResumeButton,
|
|
81
|
-
waitForChatProfileNetworkEvents,
|
|
82
|
-
|
|
92
|
+
waitForChatProfileNetworkEvents,
|
|
93
|
+
waitForChatResumeModal,
|
|
94
|
+
waitForChatResumeContent
|
|
83
95
|
} from "./detail.js";
|
|
84
96
|
import { selectChatJob } from "./jobs.js";
|
|
85
|
-
import {
|
|
86
|
-
getChatTopLevelState,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
import {
|
|
98
|
+
getChatTopLevelState,
|
|
99
|
+
makeBossSecurityVerificationRequiredError,
|
|
100
|
+
isForbiddenChatResumeNavigationError,
|
|
101
|
+
makeForbiddenChatResumeNavigationError,
|
|
102
|
+
recoverChatShell
|
|
90
103
|
} from "./page-guard.js";
|
|
91
|
-
import { getChatRoots } from "./roots.js";
|
|
92
|
-
|
|
93
|
-
const DETAIL_SOURCES = new Set(["cascade", "network", "dom", "image"]);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
import { getChatRoots } from "./roots.js";
|
|
105
|
+
|
|
106
|
+
const DETAIL_SOURCES = new Set(["cascade", "network", "dom", "image"]);
|
|
107
|
+
export const CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS = 10000;
|
|
108
|
+
export const CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS = 15000;
|
|
109
|
+
export const CHAT_EDITOR_PRE_ACTION_RETRY_LIMIT = 1;
|
|
110
|
+
export const CHAT_EDITOR_PRE_ACTION_RETRY_SETTLE_MS = 1500;
|
|
111
|
+
|
|
112
|
+
export function shouldRetryChatEditorPreAction(error, journalRecord, retryCount = 0, {
|
|
113
|
+
retryLimit = CHAT_EDITOR_PRE_ACTION_RETRY_LIMIT
|
|
114
|
+
} = {}) {
|
|
115
|
+
const code = String(error?.code || error?.message || "").trim();
|
|
116
|
+
const state = String(journalRecord?.state || "").trim();
|
|
117
|
+
const count = Math.max(0, Math.floor(Number(retryCount) || 0));
|
|
118
|
+
const limit = Math.max(0, Math.floor(Number(retryLimit) || 0));
|
|
119
|
+
return code === "CHAT_EDITOR_MESSAGE_MISMATCH"
|
|
120
|
+
&& state === "pre_action"
|
|
121
|
+
&& count < limit;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function sampleChatCollectCvProcessingFloorMs({
|
|
125
|
+
minMs = CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS,
|
|
126
|
+
maxMs = CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS,
|
|
127
|
+
random = Math.random
|
|
128
|
+
} = {}) {
|
|
129
|
+
const lower = Math.max(0, Math.round(Number(minMs) || 0));
|
|
130
|
+
const upper = Math.max(lower, Math.round(Number(maxMs) || lower));
|
|
131
|
+
const raw = typeof random === "function" ? Number(random()) : Number.NaN;
|
|
132
|
+
const unit = Number.isFinite(raw) ? Math.max(0, Math.min(1, raw)) : 0.5;
|
|
133
|
+
return Math.round(lower + (upper - lower) * unit);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function enforceChatCollectCvProcessingFloor({
|
|
137
|
+
enabled = true,
|
|
138
|
+
candidateStartedAt,
|
|
139
|
+
targetMs = null,
|
|
140
|
+
minMs = CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS,
|
|
141
|
+
maxMs = CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS,
|
|
142
|
+
random = Math.random,
|
|
143
|
+
nowFn = Date.now,
|
|
144
|
+
sleepFn = sleep
|
|
145
|
+
} = {}) {
|
|
146
|
+
if (!enabled) {
|
|
147
|
+
return {
|
|
148
|
+
enabled: false,
|
|
149
|
+
target_ms: 0,
|
|
150
|
+
elapsed_before_ms: 0,
|
|
151
|
+
delay_requested_ms: 0,
|
|
152
|
+
delay_elapsed_ms: 0,
|
|
153
|
+
elapsed_after_ms: 0,
|
|
154
|
+
verified: true
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const readNow = typeof nowFn === "function" ? nowFn : Date.now;
|
|
158
|
+
const sleeper = typeof sleepFn === "function" ? sleepFn : sleep;
|
|
159
|
+
const startedAt = Number(candidateStartedAt);
|
|
160
|
+
if (!Number.isFinite(startedAt)) {
|
|
161
|
+
throw new Error("enforceChatCollectCvProcessingFloor requires candidateStartedAt");
|
|
162
|
+
}
|
|
163
|
+
const sampledTarget = targetMs == null
|
|
164
|
+
? sampleChatCollectCvProcessingFloorMs({ minMs, maxMs, random })
|
|
165
|
+
: sampleChatCollectCvProcessingFloorMs({ minMs: targetMs, maxMs: targetMs, random });
|
|
166
|
+
const elapsedBefore = Math.max(0, Number(readNow()) - startedAt);
|
|
167
|
+
const delayStartedAt = Number(readNow());
|
|
168
|
+
let delayRequestedMs = 0;
|
|
169
|
+
let elapsedAfter = elapsedBefore;
|
|
170
|
+
let stagnantReads = 0;
|
|
171
|
+
while (elapsedAfter < sampledTarget) {
|
|
172
|
+
const remainingMs = Math.max(1, Math.ceil(sampledTarget - elapsedAfter));
|
|
173
|
+
delayRequestedMs += remainingMs;
|
|
174
|
+
await sleeper(remainingMs);
|
|
175
|
+
const nextElapsed = Math.max(0, Number(readNow()) - startedAt);
|
|
176
|
+
stagnantReads = nextElapsed > elapsedAfter ? 0 : stagnantReads + 1;
|
|
177
|
+
elapsedAfter = nextElapsed;
|
|
178
|
+
if (stagnantReads >= 3) {
|
|
179
|
+
const error = new Error("Chat collect-CV processing-floor clock did not advance after sleep");
|
|
180
|
+
error.code = "CHAT_COLLECT_CV_PROCESSING_FLOOR_CLOCK_STALLED";
|
|
181
|
+
error.target_ms = sampledTarget;
|
|
182
|
+
error.elapsed_ms = elapsedAfter;
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const delayElapsedMs = Math.max(0, Number(readNow()) - delayStartedAt);
|
|
187
|
+
return {
|
|
188
|
+
enabled: true,
|
|
189
|
+
target_ms: sampledTarget,
|
|
190
|
+
elapsed_before_ms: Math.round(elapsedBefore),
|
|
191
|
+
delay_requested_ms: Math.round(delayRequestedMs),
|
|
192
|
+
delay_elapsed_ms: Math.round(delayElapsedMs),
|
|
193
|
+
elapsed_after_ms: Math.round(elapsedAfter),
|
|
194
|
+
verified: elapsedAfter >= sampledTarget
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function compactChatActionJournalRecord(record = null, filePath = null) {
|
|
199
|
+
if (!record) return null;
|
|
200
|
+
return {
|
|
201
|
+
schema_version: record.schema_version || 1,
|
|
202
|
+
action_key: record.action_key || null,
|
|
203
|
+
candidate_id: record.candidate_id || null,
|
|
204
|
+
state: record.state || null,
|
|
205
|
+
evidence: record.evidence || {},
|
|
206
|
+
created_at: record.created_at || null,
|
|
207
|
+
updated_at: record.updated_at || null,
|
|
208
|
+
first_run_id: record.first_run_id || null,
|
|
209
|
+
last_run_id: record.last_run_id || null,
|
|
210
|
+
file_path: filePath || null
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function chatActionUnknownOrigin(record = null) {
|
|
215
|
+
const history = Array.isArray(record?.history) ? record.history : [];
|
|
216
|
+
const last = history[history.length - 1];
|
|
217
|
+
return record?.state === "outcome_unknown" ? last?.from_state || null : null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function reconcileChatRequestJournalFromExactExistingState({
|
|
221
|
+
actionJournal = null,
|
|
222
|
+
actionJournalScope = "",
|
|
223
|
+
actionJournalRecord = null,
|
|
224
|
+
candidateId = "",
|
|
225
|
+
selectionReadyState = null,
|
|
226
|
+
readyState = null,
|
|
227
|
+
runId = "",
|
|
228
|
+
greeting = "",
|
|
229
|
+
checkpointCritical = null
|
|
230
|
+
} = {}) {
|
|
231
|
+
const normalizedCandidateId = normalizeText(candidateId);
|
|
232
|
+
const expectedCandidateId = normalizeText(selectionReadyState?.expected_candidate_id || "");
|
|
233
|
+
const activeCandidateId = normalizeText(selectionReadyState?.active_candidate_id || "");
|
|
234
|
+
const exactCandidateBound = Boolean(
|
|
235
|
+
normalizedCandidateId
|
|
236
|
+
&& selectionReadyState?.candidate_selection_verified === true
|
|
237
|
+
&& expectedCandidateId === normalizedCandidateId
|
|
238
|
+
&& activeCandidateId === normalizedCandidateId
|
|
239
|
+
);
|
|
240
|
+
const exactExistingRequestState = Boolean(
|
|
241
|
+
readyState?.already_requested_resume
|
|
242
|
+
|| readyState?.attachment_resume_enabled
|
|
243
|
+
);
|
|
244
|
+
const recoverableRequestJournal = Boolean(
|
|
245
|
+
actionJournalRecord?.state === "request_in_flight"
|
|
246
|
+
|| (
|
|
247
|
+
actionJournalRecord?.state === "outcome_unknown"
|
|
248
|
+
&& chatActionUnknownOrigin(actionJournalRecord) === "request_in_flight"
|
|
249
|
+
)
|
|
250
|
+
);
|
|
251
|
+
if (
|
|
252
|
+
!exactCandidateBound
|
|
253
|
+
|| !exactExistingRequestState
|
|
254
|
+
|| !recoverableRequestJournal
|
|
255
|
+
|| actionJournalRecord?.candidate_id !== normalizedCandidateId
|
|
256
|
+
|| typeof actionJournal?.transition !== "function"
|
|
257
|
+
|| typeof checkpointCritical !== "function"
|
|
258
|
+
) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const confirmationSource = readyState.already_requested_resume
|
|
263
|
+
? "exact_requested_resume_state"
|
|
264
|
+
: "attachment_resume_available";
|
|
265
|
+
const transitioned = actionJournal.transition({
|
|
266
|
+
scope: actionJournalScope,
|
|
267
|
+
candidateId: normalizedCandidateId,
|
|
268
|
+
state: "request_confirmed",
|
|
269
|
+
runId,
|
|
270
|
+
greeting,
|
|
271
|
+
evidence: {
|
|
272
|
+
active_candidate_id: activeCandidateId,
|
|
273
|
+
reason: "recovered_from_exact_existing_request_state_before_screening",
|
|
274
|
+
message_observed: Boolean(readyState.already_requested_resume),
|
|
275
|
+
request_ready_state_observed: true,
|
|
276
|
+
request_confirmation_source: confirmationSource
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
const actionTransaction = compactChatActionJournalRecord(
|
|
280
|
+
transitioned.record,
|
|
281
|
+
transitioned.file_path
|
|
282
|
+
);
|
|
283
|
+
checkpointCritical({ action_transaction: actionTransaction });
|
|
284
|
+
return {
|
|
285
|
+
requested: false,
|
|
286
|
+
skipped: true,
|
|
287
|
+
satisfied: true,
|
|
288
|
+
reason: "request_confirmed_from_exact_existing_state",
|
|
289
|
+
initial_state: readyState,
|
|
290
|
+
journal_reconciled_existing_state: true,
|
|
291
|
+
action_transaction: actionTransaction
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function preserveChatRequestOutcomeUnknownWithoutReplay({
|
|
296
|
+
actionJournalRecord = null,
|
|
297
|
+
candidateId = "",
|
|
298
|
+
activeCandidateId = "",
|
|
299
|
+
readyEvidence = null,
|
|
300
|
+
messageEvidence = null
|
|
301
|
+
} = {}) {
|
|
302
|
+
const normalizedCandidateId = normalizeText(candidateId);
|
|
303
|
+
const normalizedActiveCandidateId = normalizeText(activeCandidateId);
|
|
304
|
+
const exactCandidateBound = Boolean(
|
|
305
|
+
normalizedCandidateId
|
|
306
|
+
&& normalizedActiveCandidateId === normalizedCandidateId
|
|
307
|
+
&& normalizeText(actionJournalRecord?.candidate_id || "") === normalizedCandidateId
|
|
308
|
+
);
|
|
309
|
+
if (
|
|
310
|
+
!exactCandidateBound
|
|
311
|
+
|| actionJournalRecord?.state !== "outcome_unknown"
|
|
312
|
+
|| chatActionUnknownOrigin(actionJournalRecord) !== "request_in_flight"
|
|
313
|
+
) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const requestMessageCount = Number(messageEvidence?.count);
|
|
318
|
+
const resumeAttachmentCount = Number(messageEvidence?.resume_attachment_count);
|
|
319
|
+
return {
|
|
320
|
+
requested: false,
|
|
321
|
+
skipped: true,
|
|
322
|
+
satisfied: false,
|
|
323
|
+
reason: "outcome_unknown_preserved_no_replay",
|
|
324
|
+
initial_state: readyEvidence || null,
|
|
325
|
+
journal_preserved_outcome_unknown: true,
|
|
326
|
+
no_replay: true,
|
|
327
|
+
action_unknown_origin: "request_in_flight",
|
|
328
|
+
request_verification: {
|
|
329
|
+
ready_state_readable: Boolean(readyEvidence),
|
|
330
|
+
message_state_readable: messageEvidence?.ok === true,
|
|
331
|
+
request_message_count: Number.isFinite(requestMessageCount) ? requestMessageCount : null,
|
|
332
|
+
resume_attachment_count: Number.isFinite(resumeAttachmentCount) ? resumeAttachmentCount : null,
|
|
333
|
+
request_marker_observed: Boolean(readyEvidence?.already_requested_resume),
|
|
334
|
+
attachment_resume_observed: Boolean(readyEvidence?.attachment_resume_enabled)
|
|
335
|
+
},
|
|
336
|
+
action_transaction: compactChatActionJournalRecord(actionJournalRecord)
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function applyChatProtectedOutcomeSkip(screening = null, postAction = null, candidate = null) {
|
|
341
|
+
if (postAction?.journal_preserved_outcome_unknown !== true) return screening;
|
|
342
|
+
return {
|
|
343
|
+
status: "skip",
|
|
344
|
+
passed: false,
|
|
345
|
+
score: 0,
|
|
346
|
+
reasons: [postAction.reason || "outcome_unknown_preserved_no_replay"],
|
|
347
|
+
candidate: candidate || screening?.candidate || null
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function makeChatPostActionOutcomeUnknownError(candidateId, record, evidence = {}) {
|
|
352
|
+
const error = new Error(`POST_ACTION_OUTCOME_UNKNOWN: candidate=${candidateId || "unknown"}; state=${record?.state || "unknown"}`);
|
|
353
|
+
error.code = "POST_ACTION_OUTCOME_UNKNOWN";
|
|
354
|
+
error.retryable = false;
|
|
355
|
+
error.candidate_id = candidateId || null;
|
|
356
|
+
error.action_state = record?.state || null;
|
|
357
|
+
error.action_unknown_origin = chatActionUnknownOrigin(record);
|
|
358
|
+
error.action_evidence = evidence;
|
|
359
|
+
return error;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function normalizeDetailSource(value) {
|
|
98
363
|
const normalized = String(value || "").trim().toLowerCase();
|
|
99
364
|
return DETAIL_SOURCES.has(normalized) ? normalized : "cascade";
|
|
100
365
|
}
|
|
@@ -116,35 +381,35 @@ function compactScreening(screening) {
|
|
|
116
381
|
|
|
117
382
|
function compactLlmResult(llmResult) {
|
|
118
383
|
if (!llmResult) return null;
|
|
119
|
-
return {
|
|
120
|
-
ok: Boolean(llmResult.ok),
|
|
121
|
-
provider: llmResult.provider || null,
|
|
122
|
-
passed: llmResult.passed,
|
|
123
|
-
review_required: typeof llmResult.review_required === "boolean" ? llmResult.review_required : null,
|
|
124
|
-
cot: llmResult.cot || llmResult.decision_cot || "",
|
|
125
|
-
reasoning_content: llmResult.reasoning_content || "",
|
|
126
|
-
raw_model_output: llmResult.raw_model_output || "",
|
|
127
|
-
evidence_count: llmResult.evidence?.length || 0,
|
|
128
|
-
usage: llmResult.usage || null,
|
|
384
|
+
return {
|
|
385
|
+
ok: Boolean(llmResult.ok),
|
|
386
|
+
provider: llmResult.provider || null,
|
|
387
|
+
passed: llmResult.passed,
|
|
388
|
+
review_required: typeof llmResult.review_required === "boolean" ? llmResult.review_required : null,
|
|
389
|
+
cot: llmResult.cot || llmResult.decision_cot || "",
|
|
390
|
+
reasoning_content: llmResult.reasoning_content || "",
|
|
391
|
+
raw_model_output: llmResult.raw_model_output || "",
|
|
392
|
+
evidence_count: llmResult.evidence?.length || 0,
|
|
393
|
+
usage: llmResult.usage || null,
|
|
129
394
|
finish_reason: llmResult.finish_reason || null,
|
|
130
|
-
image_input_count: llmResult.image_input_count || 0,
|
|
131
|
-
attempt_count: llmResult.attempt_count || 0,
|
|
132
|
-
fallback_count: llmResult.fallback_count || 0,
|
|
133
|
-
llm_model_failures: Array.isArray(llmResult.llm_model_failures) ? llmResult.llm_model_failures : [],
|
|
134
|
-
screening_strategy: llmResult.screening_strategy || "",
|
|
135
|
-
fast_thinking_level: llmResult.fast_thinking_level || "",
|
|
136
|
-
verify_thinking_level: llmResult.verify_thinking_level || "",
|
|
137
|
-
verified: typeof llmResult.verified === "boolean" ? llmResult.verified : null,
|
|
138
|
-
verification_reason: llmResult.verification_reason || "",
|
|
139
|
-
decision_source: llmResult.decision_source || "",
|
|
140
|
-
fast_result: llmResult.fast_result || null,
|
|
141
|
-
verify_result: llmResult.verify_result || null,
|
|
142
|
-
error_code: llmResult.error_code || null,
|
|
143
|
-
fatal: Boolean(llmResult.fatal),
|
|
144
|
-
fatal_reason: llmResult.fatal_reason || "",
|
|
145
|
-
error: llmResult.error || null
|
|
146
|
-
};
|
|
147
|
-
}
|
|
395
|
+
image_input_count: llmResult.image_input_count || 0,
|
|
396
|
+
attempt_count: llmResult.attempt_count || 0,
|
|
397
|
+
fallback_count: llmResult.fallback_count || 0,
|
|
398
|
+
llm_model_failures: Array.isArray(llmResult.llm_model_failures) ? llmResult.llm_model_failures : [],
|
|
399
|
+
screening_strategy: llmResult.screening_strategy || "",
|
|
400
|
+
fast_thinking_level: llmResult.fast_thinking_level || "",
|
|
401
|
+
verify_thinking_level: llmResult.verify_thinking_level || "",
|
|
402
|
+
verified: typeof llmResult.verified === "boolean" ? llmResult.verified : null,
|
|
403
|
+
verification_reason: llmResult.verification_reason || "",
|
|
404
|
+
decision_source: llmResult.decision_source || "",
|
|
405
|
+
fast_result: llmResult.fast_result || null,
|
|
406
|
+
verify_result: llmResult.verify_result || null,
|
|
407
|
+
error_code: llmResult.error_code || null,
|
|
408
|
+
fatal: Boolean(llmResult.fatal),
|
|
409
|
+
fatal_reason: llmResult.fatal_reason || "",
|
|
410
|
+
error: llmResult.error || null
|
|
411
|
+
};
|
|
412
|
+
}
|
|
148
413
|
|
|
149
414
|
function compactCandidate(candidate) {
|
|
150
415
|
return {
|
|
@@ -171,9 +436,9 @@ function compactChatJobGuard(result = null) {
|
|
|
171
436
|
};
|
|
172
437
|
}
|
|
173
438
|
|
|
174
|
-
function compactDetail(detailResult) {
|
|
175
|
-
if (!detailResult) return null;
|
|
176
|
-
return {
|
|
439
|
+
function compactDetail(detailResult) {
|
|
440
|
+
if (!detailResult) return null;
|
|
441
|
+
return {
|
|
177
442
|
popup_text_length: detailResult.detail?.popup_text?.length || 0,
|
|
178
443
|
content_text_length: detailResult.detail?.content_text?.length || 0,
|
|
179
444
|
resume_iframe_text_length: detailResult.detail?.resume_iframe_text?.length || 0,
|
|
@@ -213,10 +478,61 @@ export function makeChatResumeModalOpenBeforeCandidateClickError(closeResult = n
|
|
|
213
478
|
return error;
|
|
214
479
|
}
|
|
215
480
|
|
|
216
|
-
export function isChatResumeModalCloseFailureError(error) {
|
|
217
|
-
return error?.code === "CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK"
|
|
218
|
-
|| /CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK/i.test(String(error?.message || error || ""));
|
|
219
|
-
}
|
|
481
|
+
export function isChatResumeModalCloseFailureError(error) {
|
|
482
|
+
return error?.code === "CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK"
|
|
483
|
+
|| /CHAT_RESUME_MODAL_OPEN_BEFORE_CANDIDATE_CLICK/i.test(String(error?.message || error || ""));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export function consumeChatDetailRecoveryBudget(recoveryCounts, candidateKey, {
|
|
487
|
+
retryLimit = 1
|
|
488
|
+
} = {}) {
|
|
489
|
+
if (!(recoveryCounts instanceof Map)) {
|
|
490
|
+
throw new TypeError("consumeChatDetailRecoveryBudget requires a recovery-count Map");
|
|
491
|
+
}
|
|
492
|
+
const key = String(candidateKey || "").trim() || "__unknown_candidate__";
|
|
493
|
+
const limit = Math.max(0, Math.floor(Number(retryLimit) || 0));
|
|
494
|
+
const previousCount = Math.max(0, Math.floor(Number(recoveryCounts.get(key)) || 0));
|
|
495
|
+
if (previousCount >= limit) {
|
|
496
|
+
return {
|
|
497
|
+
allowed: false,
|
|
498
|
+
previous_count: previousCount,
|
|
499
|
+
count: previousCount,
|
|
500
|
+
retry_limit: limit
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const count = previousCount + 1;
|
|
504
|
+
recoveryCounts.set(key, count);
|
|
505
|
+
return {
|
|
506
|
+
allowed: true,
|
|
507
|
+
previous_count: previousCount,
|
|
508
|
+
count,
|
|
509
|
+
retry_limit: limit
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export function compactChatCandidateSelectionReadyState(selectionReadyState = null) {
|
|
514
|
+
if (!selectionReadyState) return null;
|
|
515
|
+
const activeCandidate = selectionReadyState.activeCandidate || selectionReadyState.active_candidate || null;
|
|
516
|
+
return {
|
|
517
|
+
ok: selectionReadyState.ok === true,
|
|
518
|
+
reason: selectionReadyState.reason || null,
|
|
519
|
+
elapsed_ms: Number.isFinite(Number(selectionReadyState.elapsed_ms))
|
|
520
|
+
? Number(selectionReadyState.elapsed_ms)
|
|
521
|
+
: null,
|
|
522
|
+
expected_candidate_id: normalizeText(selectionReadyState.expected_candidate_id || "") || null,
|
|
523
|
+
active_candidate_id: normalizeText(selectionReadyState.active_candidate_id || "") || null,
|
|
524
|
+
candidate_selection_verified: selectionReadyState.candidate_selection_verified === true,
|
|
525
|
+
active_candidate: activeCandidate
|
|
526
|
+
? {
|
|
527
|
+
node_id: activeCandidate.node_id || null,
|
|
528
|
+
selector: activeCandidate.selector || null,
|
|
529
|
+
candidate_id: normalizeText(activeCandidate.candidate_id || "") || null,
|
|
530
|
+
label: normalizeText(activeCandidate.label || "") || null,
|
|
531
|
+
outer_html_length: activeCandidate.outer_html_length || 0
|
|
532
|
+
}
|
|
533
|
+
: null
|
|
534
|
+
};
|
|
535
|
+
}
|
|
220
536
|
|
|
221
537
|
export function makeChatCandidateSelectionMismatchError(selected = null, candidate = null) {
|
|
222
538
|
const expectedId = candidate?.id || selected?.ready?.expected_candidate_id || "";
|
|
@@ -229,10 +545,27 @@ export function makeChatCandidateSelectionMismatchError(selected = null, candida
|
|
|
229
545
|
return error;
|
|
230
546
|
}
|
|
231
547
|
|
|
232
|
-
export function isChatCandidateSelectionMismatchError(error) {
|
|
233
|
-
return error?.code === "CHAT_ACTIVE_CANDIDATE_MISMATCH"
|
|
234
|
-
|| /CHAT_ACTIVE_CANDIDATE_MISMATCH/i.test(String(error?.message || error || ""));
|
|
235
|
-
}
|
|
548
|
+
export function isChatCandidateSelectionMismatchError(error) {
|
|
549
|
+
return error?.code === "CHAT_ACTIVE_CANDIDATE_MISMATCH"
|
|
550
|
+
|| /CHAT_ACTIVE_CANDIDATE_MISMATCH/i.test(String(error?.message || error || ""));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export function requireExactChatCandidateSelection(selected = null, candidate = null) {
|
|
554
|
+
const candidateId = normalizeText(candidate?.id || "");
|
|
555
|
+
const selectionReadyState = selected?.ready || null;
|
|
556
|
+
const expectedCandidateId = normalizeText(selectionReadyState?.expected_candidate_id || "");
|
|
557
|
+
const activeCandidateId = normalizeText(selectionReadyState?.active_candidate_id || "");
|
|
558
|
+
const exactCandidateBound = Boolean(
|
|
559
|
+
candidateId
|
|
560
|
+
&& selectionReadyState?.candidate_selection_verified === true
|
|
561
|
+
&& expectedCandidateId === candidateId
|
|
562
|
+
&& activeCandidateId === candidateId
|
|
563
|
+
);
|
|
564
|
+
if (!exactCandidateBound) {
|
|
565
|
+
throw makeChatCandidateSelectionMismatchError(selected, candidate);
|
|
566
|
+
}
|
|
567
|
+
return selectionReadyState;
|
|
568
|
+
}
|
|
236
569
|
|
|
237
570
|
export async function ensureNoOpenChatResumeModalBeforeCandidateClick(client, {
|
|
238
571
|
closeAttempts = 3
|
|
@@ -352,7 +685,7 @@ export function resolveChatDomFallbackWait({
|
|
|
352
685
|
};
|
|
353
686
|
}
|
|
354
687
|
|
|
355
|
-
function isRecoverableCdpNodeError(error) {
|
|
688
|
+
function isRecoverableCdpNodeError(error) {
|
|
356
689
|
return /(?:Could not find node|No node with given id|Cannot find node|Could not compute box model)/i
|
|
357
690
|
.test(String(error?.message || error || ""));
|
|
358
691
|
}
|
|
@@ -363,9 +696,9 @@ function isRecoverableLlmScreeningError(error) {
|
|
|
363
696
|
}
|
|
364
697
|
|
|
365
698
|
function createFailedLlmResult(error) {
|
|
366
|
-
return {
|
|
367
|
-
ok: false,
|
|
368
|
-
passed: false,
|
|
699
|
+
return {
|
|
700
|
+
ok: false,
|
|
701
|
+
passed: false,
|
|
369
702
|
reason: "",
|
|
370
703
|
evidence: [],
|
|
371
704
|
cot: "",
|
|
@@ -373,110 +706,114 @@ function createFailedLlmResult(error) {
|
|
|
373
706
|
reasoning_content: "",
|
|
374
707
|
raw_model_output: "",
|
|
375
708
|
attempt_count: Number(error?.llm_attempt_count) || 0,
|
|
376
|
-
fallback_count: Array.isArray(error?.llm_model_failures) ? error.llm_model_failures.length : 0,
|
|
377
|
-
llm_model_failures: Array.isArray(error?.llm_model_failures) ? error.llm_model_failures : [],
|
|
378
|
-
error_code: error?.code || null,
|
|
379
|
-
fatal: Boolean(isFatalLlmProviderError(error)),
|
|
380
|
-
fatal_reason: error?.llm_fatal_reason || "",
|
|
381
|
-
error: error?.message || String(error || "unknown"),
|
|
382
|
-
screened_at: new Date().toISOString()
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
709
|
+
fallback_count: Array.isArray(error?.llm_model_failures) ? error.llm_model_failures.length : 0,
|
|
710
|
+
llm_model_failures: Array.isArray(error?.llm_model_failures) ? error.llm_model_failures : [],
|
|
711
|
+
error_code: error?.code || null,
|
|
712
|
+
fatal: Boolean(isFatalLlmProviderError(error)),
|
|
713
|
+
fatal_reason: error?.llm_fatal_reason || "",
|
|
714
|
+
error: error?.message || String(error || "unknown"),
|
|
715
|
+
screened_at: new Date().toISOString()
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
export function isRecoverableChatImageCaptureError(error) {
|
|
720
|
+
return isRecoverableImageCaptureWorkflowError(error);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function normalizeScreeningMode(value) {
|
|
724
|
+
const normalized = String(value || "llm").trim().toLowerCase();
|
|
725
|
+
if (["collect_cv", "collect-cv", "cv_collection", "request_cv", "request_resume"].includes(normalized)) {
|
|
726
|
+
return "collect_cv";
|
|
727
|
+
}
|
|
728
|
+
return ["deterministic", "local", "local_scorer"].includes(normalized)
|
|
729
|
+
? "deterministic"
|
|
730
|
+
: "llm";
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function isCvAcquiredOrAvailable(detailResult = null, preActionState = null) {
|
|
734
|
+
return Boolean(
|
|
735
|
+
preActionState?.attachment_resume_enabled
|
|
736
|
+
|| detailResult?.cv_acquisition?.full_cv_evidence?.full_cv_acquired
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function isChatResumeRequestAvailable(preActionState = null) {
|
|
741
|
+
return Boolean(preActionState?.ask_resume?.node_id && !preActionState.ask_resume.disabled);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function shouldSkipCvCollectionForDetailReason(reason = "") {
|
|
745
|
+
const normalized = normalizeText(reason);
|
|
746
|
+
return [
|
|
747
|
+
"active_candidate_mismatch",
|
|
748
|
+
"forbidden_top_level_resume_navigation",
|
|
749
|
+
"online_resume_modal_did_not_open",
|
|
750
|
+
"unsafe_online_resume_navigation_link"
|
|
751
|
+
].includes(normalized)
|
|
752
|
+
|| normalized.startsWith("recoverable_cdp_node_stale:")
|
|
753
|
+
|| normalized.startsWith("resume_modal_close_failed:");
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function createCvCollectionScreening(screeningCandidate, {
|
|
757
|
+
detailResult = null,
|
|
758
|
+
detailUnavailableReason = "",
|
|
759
|
+
preActionState = null
|
|
760
|
+
} = {}) {
|
|
761
|
+
if (preActionState?.already_requested_resume) {
|
|
762
|
+
return {
|
|
763
|
+
status: "skip",
|
|
764
|
+
passed: false,
|
|
765
|
+
score: 0,
|
|
766
|
+
reasons: ["resume_request_already_pending"],
|
|
767
|
+
candidate: screeningCandidate
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (isCvAcquiredOrAvailable(detailResult, preActionState)) {
|
|
771
|
+
const reason = preActionState?.attachment_resume_enabled
|
|
772
|
+
? "attachment_resume_already_available"
|
|
773
|
+
: "online_cv_already_available";
|
|
774
|
+
return {
|
|
775
|
+
status: "skip",
|
|
776
|
+
passed: false,
|
|
777
|
+
score: 0,
|
|
778
|
+
reasons: [reason],
|
|
779
|
+
candidate: screeningCandidate
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
if (isChatResumeRequestAvailable(preActionState)) {
|
|
783
|
+
const reason = detailUnavailableReason || "request_cv_available";
|
|
784
|
+
return {
|
|
785
|
+
status: "pass",
|
|
786
|
+
passed: true,
|
|
787
|
+
score: 100,
|
|
788
|
+
reasons: [`collect_cv:${reason}`],
|
|
789
|
+
candidate: screeningCandidate
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
if (shouldSkipCvCollectionForDetailReason(detailUnavailableReason)) {
|
|
793
|
+
return {
|
|
794
|
+
status: "skip",
|
|
795
|
+
passed: false,
|
|
796
|
+
score: 0,
|
|
797
|
+
reasons: [detailUnavailableReason],
|
|
798
|
+
candidate: screeningCandidate
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
const reason = detailUnavailableReason || "cv_collection_missing_online_cv";
|
|
802
|
+
return {
|
|
803
|
+
status: "pass",
|
|
804
|
+
passed: true,
|
|
805
|
+
score: 100,
|
|
806
|
+
reasons: [`collect_cv:${reason}`],
|
|
807
|
+
candidate: screeningCandidate
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export function shouldOpenOnlineResumeForChatDetail({
|
|
812
|
+
collectCvOnly = false,
|
|
813
|
+
detailResult = null
|
|
814
|
+
} = {}) {
|
|
815
|
+
return !collectCvOnly && !detailResult;
|
|
816
|
+
}
|
|
480
817
|
|
|
481
818
|
function createMissingLlmConfigResult() {
|
|
482
819
|
return createFailedLlmResult(new Error("LLM screening config is required for production chat runs"));
|
|
@@ -498,81 +835,74 @@ function createSkippedDetailResult(cardCandidate, reason, error = null) {
|
|
|
498
835
|
};
|
|
499
836
|
}
|
|
500
837
|
|
|
501
|
-
function compactChatRuntimeError(error) {
|
|
502
|
-
if (!error) return null;
|
|
503
|
-
return {
|
|
504
|
-
name: error.name || "Error",
|
|
505
|
-
code: error.code || null,
|
|
506
|
-
message: error.message || String(error),
|
|
507
|
-
retryable: typeof error.retryable === "boolean" ? error.retryable : null,
|
|
508
|
-
attempts: Array.isArray(error.attempts) ? error.attempts : null,
|
|
509
|
-
close_result: error.close_result || null,
|
|
510
|
-
selection_ready_state: error.selection_ready_state || null,
|
|
511
|
-
page_state: error.page_state || null
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async function captureChatFinalFailureArtifact(client, {
|
|
516
|
-
runControl,
|
|
517
|
-
imageOutputDir = "",
|
|
518
|
-
error = null
|
|
519
|
-
} = {}) {
|
|
520
|
-
if (!client || !imageOutputDir || !runControl?.runId) return null;
|
|
521
|
-
const artifact = {
|
|
522
|
-
schema_version: 1,
|
|
523
|
-
kind: "chat_final_failure_page",
|
|
524
|
-
captured_at: new Date().toISOString(),
|
|
525
|
-
run_id: runControl.runId,
|
|
526
|
-
error: compactChatRuntimeError(error),
|
|
527
|
-
page_state: null,
|
|
528
|
-
active_candidate_state: null,
|
|
529
|
-
conversation_ready_state: null,
|
|
530
|
-
screenshot: null,
|
|
531
|
-
screenshot_error: null
|
|
532
|
-
};
|
|
533
|
-
try {
|
|
534
|
-
artifact.page_state = await getChatTopLevelState(client);
|
|
535
|
-
} catch (pageError) {
|
|
536
|
-
artifact.page_state = {
|
|
537
|
-
error: pageError?.message || String(pageError)
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
try {
|
|
541
|
-
artifact.active_candidate_state = await readChatActiveCandidateState(client);
|
|
542
|
-
} catch (activeCandidateError) {
|
|
543
|
-
artifact.active_candidate_state = {
|
|
544
|
-
error: activeCandidateError?.message || String(activeCandidateError)
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
try {
|
|
548
|
-
artifact.conversation_ready_state = await readChatConversationReadyState(client);
|
|
549
|
-
} catch (conversationError) {
|
|
550
|
-
artifact.conversation_ready_state = {
|
|
551
|
-
error: conversationError?.message || String(conversationError)
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
try {
|
|
555
|
-
artifact.screenshot = await captureViewportScreenshot(client, {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
} catch (screenshotError) {
|
|
572
|
-
artifact.screenshot_error = screenshotError?.message || String(screenshotError);
|
|
573
|
-
}
|
|
574
|
-
return artifact;
|
|
575
|
-
}
|
|
838
|
+
function compactChatRuntimeError(error) {
|
|
839
|
+
if (!error) return null;
|
|
840
|
+
return {
|
|
841
|
+
name: error.name || "Error",
|
|
842
|
+
code: error.code || null,
|
|
843
|
+
message: error.message || String(error),
|
|
844
|
+
retryable: typeof error.retryable === "boolean" ? error.retryable : null,
|
|
845
|
+
attempts: Array.isArray(error.attempts) ? error.attempts : null,
|
|
846
|
+
close_result: error.close_result || null,
|
|
847
|
+
selection_ready_state: error.selection_ready_state || null,
|
|
848
|
+
page_state: error.page_state || null
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async function captureChatFinalFailureArtifact(client, {
|
|
853
|
+
runControl,
|
|
854
|
+
imageOutputDir = "",
|
|
855
|
+
error = null
|
|
856
|
+
} = {}) {
|
|
857
|
+
if (!client || !imageOutputDir || !runControl?.runId) return null;
|
|
858
|
+
const artifact = {
|
|
859
|
+
schema_version: 1,
|
|
860
|
+
kind: "chat_final_failure_page",
|
|
861
|
+
captured_at: new Date().toISOString(),
|
|
862
|
+
run_id: runControl.runId,
|
|
863
|
+
error: compactChatRuntimeError(error),
|
|
864
|
+
page_state: null,
|
|
865
|
+
active_candidate_state: null,
|
|
866
|
+
conversation_ready_state: null,
|
|
867
|
+
screenshot: null,
|
|
868
|
+
screenshot_error: null
|
|
869
|
+
};
|
|
870
|
+
try {
|
|
871
|
+
artifact.page_state = await getChatTopLevelState(client);
|
|
872
|
+
} catch (pageError) {
|
|
873
|
+
artifact.page_state = {
|
|
874
|
+
error: pageError?.message || String(pageError)
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
artifact.active_candidate_state = await readChatActiveCandidateState(client);
|
|
879
|
+
} catch (activeCandidateError) {
|
|
880
|
+
artifact.active_candidate_state = {
|
|
881
|
+
error: activeCandidateError?.message || String(activeCandidateError)
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
try {
|
|
885
|
+
artifact.conversation_ready_state = await readChatConversationReadyState(client);
|
|
886
|
+
} catch (conversationError) {
|
|
887
|
+
artifact.conversation_ready_state = {
|
|
888
|
+
error: conversationError?.message || String(conversationError)
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
artifact.screenshot = await captureViewportScreenshot(client, {
|
|
893
|
+
format: "jpeg",
|
|
894
|
+
quality: 72,
|
|
895
|
+
metadata: {
|
|
896
|
+
domain: "chat",
|
|
897
|
+
run_id: runControl.runId,
|
|
898
|
+
reason: "final_failure"
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
} catch (screenshotError) {
|
|
902
|
+
artifact.screenshot_error = screenshotError?.message || String(screenshotError);
|
|
903
|
+
}
|
|
904
|
+
return artifact;
|
|
905
|
+
}
|
|
576
906
|
|
|
577
907
|
const CHAT_FULL_CV_DOM_MIN_TEXT_LENGTH = 500;
|
|
578
908
|
const CHAT_FULL_CV_DOM_MIN_SECTION_TEXT_LENGTH = 180;
|
|
@@ -677,8 +1007,13 @@ function isFullCvNetworkProfile(profileResult = {}) {
|
|
|
677
1007
|
return false;
|
|
678
1008
|
}
|
|
679
1009
|
|
|
680
|
-
function hasUsableImageEvidence(imageEvidence = null) {
|
|
681
|
-
if (
|
|
1010
|
+
function hasUsableImageEvidence(imageEvidence = null) {
|
|
1011
|
+
if (
|
|
1012
|
+
!imageEvidence
|
|
1013
|
+
|| imageEvidence.ok === false
|
|
1014
|
+
|| imageEvidence.coverage_complete !== true
|
|
1015
|
+
|| isIncompleteImageEvidence(imageEvidence)
|
|
1016
|
+
) return false;
|
|
682
1017
|
return Boolean(
|
|
683
1018
|
(Array.isArray(imageEvidence.llm_file_paths) && imageEvidence.llm_file_paths.length)
|
|
684
1019
|
|| (Array.isArray(imageEvidence.file_paths) && imageEvidence.file_paths.length)
|
|
@@ -689,7 +1024,7 @@ function hasUsableImageEvidence(imageEvidence = null) {
|
|
|
689
1024
|
);
|
|
690
1025
|
}
|
|
691
1026
|
|
|
692
|
-
export function summarizeChatFullCvEvidence({
|
|
1027
|
+
export function summarizeChatFullCvEvidence({
|
|
693
1028
|
detailResult = null,
|
|
694
1029
|
contentWait = null,
|
|
695
1030
|
imageEvidence = null
|
|
@@ -740,15 +1075,15 @@ async function resolveFreshChatCardNodeId(client, {
|
|
|
740
1075
|
return freshNodeId || fallbackNodeId;
|
|
741
1076
|
}
|
|
742
1077
|
|
|
743
|
-
async function selectFreshChatCandidate(client, {
|
|
744
|
-
cardNodeId,
|
|
745
|
-
candidate,
|
|
746
|
-
timeoutMs,
|
|
747
|
-
settleMs = 1200,
|
|
748
|
-
onlineResumeProbe = true
|
|
749
|
-
} = {}) {
|
|
750
|
-
let lastError = null;
|
|
751
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1078
|
+
async function selectFreshChatCandidate(client, {
|
|
1079
|
+
cardNodeId,
|
|
1080
|
+
candidate,
|
|
1081
|
+
timeoutMs,
|
|
1082
|
+
settleMs = 1200,
|
|
1083
|
+
onlineResumeProbe = true
|
|
1084
|
+
} = {}) {
|
|
1085
|
+
let lastError = null;
|
|
1086
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
752
1087
|
const modalGuard = await ensureNoOpenChatResumeModalBeforeCandidateClick(client);
|
|
753
1088
|
const rootState = await getChatRoots(client);
|
|
754
1089
|
const freshNodeId = await resolveFreshChatCardNodeId(client, {
|
|
@@ -759,58 +1094,78 @@ async function selectFreshChatCandidate(client, {
|
|
|
759
1094
|
try {
|
|
760
1095
|
await scrollNodeIntoView(client, freshNodeId);
|
|
761
1096
|
await sleep(250);
|
|
762
|
-
const box = await getNodeBox(client, freshNodeId);
|
|
763
|
-
await clickPoint(client, box.center.x, box.center.y);
|
|
764
|
-
if (settleMs > 0) await sleep(settleMs);
|
|
765
|
-
const ready = onlineResumeProbe
|
|
766
|
-
? await waitForChatOnlineResumeButton(client, {
|
|
767
|
-
timeoutMs,
|
|
768
|
-
expectedCandidateId: candidate?.id || ""
|
|
769
|
-
})
|
|
770
|
-
: await readSelectedChatCandidateState(client, candidate);
|
|
771
|
-
|
|
772
|
-
card_box: box,
|
|
773
|
-
ready,
|
|
774
|
-
card_node_id: freshNodeId,
|
|
775
|
-
refreshed_node: freshNodeId !== cardNodeId,
|
|
776
|
-
modal_guard: modalGuard,
|
|
777
|
-
attempt: attempt + 1
|
|
778
|
-
};
|
|
1097
|
+
const box = await getNodeBox(client, freshNodeId);
|
|
1098
|
+
await clickPoint(client, box.center.x, box.center.y);
|
|
1099
|
+
if (settleMs > 0) await sleep(settleMs);
|
|
1100
|
+
const ready = onlineResumeProbe
|
|
1101
|
+
? await waitForChatOnlineResumeButton(client, {
|
|
1102
|
+
timeoutMs,
|
|
1103
|
+
expectedCandidateId: candidate?.id || ""
|
|
1104
|
+
})
|
|
1105
|
+
: await readSelectedChatCandidateState(client, candidate);
|
|
1106
|
+
const selected = {
|
|
1107
|
+
card_box: box,
|
|
1108
|
+
ready,
|
|
1109
|
+
card_node_id: freshNodeId,
|
|
1110
|
+
refreshed_node: freshNodeId !== cardNodeId,
|
|
1111
|
+
modal_guard: modalGuard,
|
|
1112
|
+
attempt: attempt + 1
|
|
1113
|
+
};
|
|
1114
|
+
if (!ready?.forbidden_top_level_navigation) {
|
|
1115
|
+
requireExactChatCandidateSelection(selected, candidate);
|
|
1116
|
+
}
|
|
1117
|
+
return selected;
|
|
779
1118
|
} catch (error) {
|
|
780
1119
|
lastError = error;
|
|
781
1120
|
if (!isRecoverableCdpNodeError(error)) throw error;
|
|
782
1121
|
await sleep(350);
|
|
783
1122
|
}
|
|
784
1123
|
}
|
|
785
|
-
throw lastError || new Error("Chat candidate selection failed");
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
async function readSelectedChatCandidateState(client, candidate = null) {
|
|
789
|
-
const topLevelState = await getChatTopLevelState(client);
|
|
790
|
-
if (topLevelState.
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1124
|
+
throw lastError || new Error("Chat candidate selection failed");
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
async function readSelectedChatCandidateState(client, candidate = null) {
|
|
1128
|
+
const topLevelState = await getChatTopLevelState(client);
|
|
1129
|
+
if (topLevelState.is_security_verification) {
|
|
1130
|
+
throw makeBossSecurityVerificationRequiredError(
|
|
1131
|
+
topLevelState,
|
|
1132
|
+
"chat_candidate_selection"
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
if (topLevelState.is_forbidden_resume_top_level) {
|
|
1136
|
+
return {
|
|
1137
|
+
forbidden_top_level_navigation: true,
|
|
1138
|
+
top_level_state: topLevelState
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
const activeState = await readChatActiveCandidateState(client);
|
|
1142
|
+
const expectedId = normalizeText(candidate?.id || "");
|
|
1143
|
+
const activeCandidateId = normalizeText(activeState?.active_candidate?.candidate_id || "");
|
|
1144
|
+
const candidateSelectionVerified = expectedId
|
|
1145
|
+
? activeCandidateId === expectedId
|
|
1146
|
+
: undefined;
|
|
1147
|
+
return {
|
|
1148
|
+
ok: !expectedId || candidateSelectionVerified === true,
|
|
1149
|
+
reason: expectedId && candidateSelectionVerified !== true
|
|
1150
|
+
? "active_candidate_mismatch"
|
|
1151
|
+
: "online_resume_probe_skipped",
|
|
1152
|
+
roots: activeState.roots,
|
|
1153
|
+
activeCandidate: activeState.active_candidate,
|
|
1154
|
+
expected_candidate_id: expectedId || null,
|
|
1155
|
+
active_candidate_id: activeCandidateId || null,
|
|
1156
|
+
candidate_selection_verified: candidateSelectionVerified
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
export function hasScreenableChatFullCvEvidence(fullCvEvidence = null, imageEvidence = null) {
|
|
1161
|
+
if (fullCvEvidence?.source === "image") {
|
|
1162
|
+
return Boolean(fullCvEvidence.full_cv_acquired && hasUsableImageEvidence(imageEvidence));
|
|
1163
|
+
}
|
|
1164
|
+
return Boolean(
|
|
1165
|
+
fullCvEvidence?.full_cv_acquired
|
|
1166
|
+
&& !isIncompleteImageEvidence(imageEvidence)
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
814
1169
|
|
|
815
1170
|
function selectedDetailNetworkEvents(detailSource, selectionEvents, resumeEvents) {
|
|
816
1171
|
if (detailSource !== "network" && detailSource !== "cascade") return [];
|
|
@@ -936,37 +1291,55 @@ export async function runChatWorkflow({
|
|
|
936
1291
|
listWheelDeltaY = 850,
|
|
937
1292
|
listSettleMs = 2200,
|
|
938
1293
|
listFallbackPoint = null,
|
|
939
|
-
imageOutputDir = "",
|
|
940
|
-
humanRestEnabled = false,
|
|
941
|
-
humanBehavior = null
|
|
942
|
-
|
|
1294
|
+
imageOutputDir = "",
|
|
1295
|
+
humanRestEnabled = false,
|
|
1296
|
+
humanBehavior = null,
|
|
1297
|
+
actionJournal = null,
|
|
1298
|
+
actionJournalScope = "boss-chat:default"
|
|
1299
|
+
} = {}, runControl) {
|
|
943
1300
|
if (!client) throw new Error("runChatWorkflow requires a guarded CDP client");
|
|
944
|
-
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
945
|
-
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
946
|
-
});
|
|
947
|
-
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
948
|
-
const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
|
|
949
|
-
const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
|
|
950
|
-
const useLlmScreening = normalizedScreeningMode === "llm" && !collectCvOnly;
|
|
951
|
-
const
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1301
|
+
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
1302
|
+
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
1303
|
+
});
|
|
1304
|
+
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
1305
|
+
const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
|
|
1306
|
+
const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
|
|
1307
|
+
const useLlmScreening = normalizedScreeningMode === "llm" && !collectCvOnly;
|
|
1308
|
+
const collectCvProcessingFloorEnabled = collectCvOnly;
|
|
1309
|
+
const collectCvPerCandidateRestEnabled = false;
|
|
1310
|
+
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
1311
|
+
const effectiveActionJournal = requestResumeForPassed
|
|
1312
|
+
? actionJournal || createChatActionJournal()
|
|
1313
|
+
: actionJournal;
|
|
1314
|
+
configureHumanInteraction(client, {
|
|
1315
|
+
enabled: effectiveHumanBehavior.enabled,
|
|
1316
|
+
clickMovementEnabled: effectiveHumanBehavior.clickMovement,
|
|
956
1317
|
textEntryEnabled: effectiveHumanBehavior.textEntry,
|
|
957
1318
|
safeClickPointEnabled: effectiveHumanBehavior.clickMovement,
|
|
958
1319
|
actionCooldownEnabled: effectiveHumanBehavior.actionCooldown
|
|
959
1320
|
});
|
|
960
|
-
const humanRestController = createHumanRestController({
|
|
961
|
-
enabled: effectiveHumanRestEnabled,
|
|
962
|
-
shortRestEnabled: effectiveHumanBehavior.shortRest,
|
|
963
|
-
batchRestEnabled: effectiveHumanBehavior.batchRest,
|
|
964
|
-
restLevel: effectiveHumanBehavior.restLevel
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1321
|
+
const humanRestController = createHumanRestController({
|
|
1322
|
+
enabled: effectiveHumanRestEnabled,
|
|
1323
|
+
shortRestEnabled: effectiveHumanBehavior.shortRest,
|
|
1324
|
+
batchRestEnabled: effectiveHumanBehavior.batchRest,
|
|
1325
|
+
restLevel: effectiveHumanBehavior.restLevel
|
|
1326
|
+
});
|
|
1327
|
+
const collectCvProcessingFloorState = {
|
|
1328
|
+
enabled: collectCvProcessingFloorEnabled,
|
|
1329
|
+
min_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS : null,
|
|
1330
|
+
max_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS : null,
|
|
1331
|
+
count: 0,
|
|
1332
|
+
total_delay_requested_ms: 0,
|
|
1333
|
+
total_delay_elapsed_ms: 0,
|
|
1334
|
+
last: null
|
|
1335
|
+
};
|
|
1336
|
+
const collectCvProcessingFloorSnapshot = () => ({
|
|
1337
|
+
...collectCvProcessingFloorState,
|
|
1338
|
+
last: collectCvProcessingFloorState.last
|
|
1339
|
+
? { ...collectCvProcessingFloorState.last }
|
|
1340
|
+
: null
|
|
1341
|
+
});
|
|
1342
|
+
const processedLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
970
1343
|
const passTarget = Number.isFinite(Number(targetPassCount)) && Number(targetPassCount) > 0
|
|
971
1344
|
? Number(targetPassCount)
|
|
972
1345
|
: null;
|
|
@@ -1006,12 +1379,13 @@ export async function runChatWorkflow({
|
|
|
1006
1379
|
let requestedCount = 0;
|
|
1007
1380
|
let requestSatisfiedCount = 0;
|
|
1008
1381
|
let requestSkippedCount = 0;
|
|
1009
|
-
let contextSetup = {};
|
|
1010
|
-
let contextRecoveryAttempts = 0;
|
|
1011
|
-
const candidateRecoveryCounts = new Map();
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1382
|
+
let contextSetup = {};
|
|
1383
|
+
let contextRecoveryAttempts = 0;
|
|
1384
|
+
const candidateRecoveryCounts = new Map();
|
|
1385
|
+
const imageCaptureWorkflowRetries = createImageCaptureWorkflowRetryTracker();
|
|
1386
|
+
let lastHumanEvent = null;
|
|
1387
|
+
|
|
1388
|
+
function recordHumanEvent(event = null) {
|
|
1015
1389
|
if (!event) return lastHumanEvent;
|
|
1016
1390
|
lastHumanEvent = {
|
|
1017
1391
|
at: new Date().toISOString(),
|
|
@@ -1037,9 +1411,15 @@ export async function runChatWorkflow({
|
|
|
1037
1411
|
});
|
|
1038
1412
|
}
|
|
1039
1413
|
|
|
1040
|
-
runControl.setPhase("chat:cleanup");
|
|
1041
|
-
let initialTopLevelState = await getChatTopLevelState(client);
|
|
1042
|
-
if (
|
|
1414
|
+
runControl.setPhase("chat:cleanup");
|
|
1415
|
+
let initialTopLevelState = await getChatTopLevelState(client);
|
|
1416
|
+
if (initialTopLevelState.is_security_verification) {
|
|
1417
|
+
throw makeBossSecurityVerificationRequiredError(
|
|
1418
|
+
initialTopLevelState,
|
|
1419
|
+
"chat_run_setup"
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
if (!initialTopLevelState.is_chat_shell) {
|
|
1043
1423
|
const recovery = await recoverChatShell(client, {
|
|
1044
1424
|
targetUrl,
|
|
1045
1425
|
timeoutMs: readyTimeoutMs,
|
|
@@ -1075,13 +1455,13 @@ export async function runChatWorkflow({
|
|
|
1075
1455
|
...setup.contextSetup,
|
|
1076
1456
|
initial_top_level_state: initialTopLevelState
|
|
1077
1457
|
};
|
|
1078
|
-
runControl.checkpoint({
|
|
1079
|
-
chat_context: contextSetup
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
async function recoverAndReapplyChatContext(reason, error = null, {
|
|
1083
|
-
forceRefresh = false
|
|
1084
|
-
} = {}) {
|
|
1458
|
+
runControl.checkpoint({
|
|
1459
|
+
chat_context: contextSetup
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
async function recoverAndReapplyChatContext(reason, error = null, {
|
|
1463
|
+
forceRefresh = false
|
|
1464
|
+
} = {}) {
|
|
1085
1465
|
runControl.setPhase("chat:recover_shell");
|
|
1086
1466
|
contextRecoveryAttempts += 1;
|
|
1087
1467
|
const shellRecovery = await recoverChatShell(client, {
|
|
@@ -1183,14 +1563,20 @@ export async function runChatWorkflow({
|
|
|
1183
1563
|
viewport_checks: viewportGuard.getStats().checks,
|
|
1184
1564
|
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
1185
1565
|
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1186
|
-
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1187
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1188
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1189
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
1190
|
-
human_rest_per_candidate_min_ms:
|
|
1191
|
-
human_rest_per_candidate_max_ms:
|
|
1192
|
-
|
|
1193
|
-
|
|
1566
|
+
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1567
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1568
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1569
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
1570
|
+
human_rest_per_candidate_min_ms: null,
|
|
1571
|
+
human_rest_per_candidate_max_ms: null,
|
|
1572
|
+
collect_cv_processing_floor_enabled: collectCvProcessingFloorEnabled,
|
|
1573
|
+
collect_cv_processing_floor_min_ms: collectCvProcessingFloorState.min_ms,
|
|
1574
|
+
collect_cv_processing_floor_max_ms: collectCvProcessingFloorState.max_ms,
|
|
1575
|
+
collect_cv_processing_floor_count: collectCvProcessingFloorState.count,
|
|
1576
|
+
collect_cv_processing_floor_delay_requested_ms: collectCvProcessingFloorState.total_delay_requested_ms,
|
|
1577
|
+
collect_cv_processing_floor_delay_ms: collectCvProcessingFloorState.total_delay_elapsed_ms,
|
|
1578
|
+
human_rest_count: humanRestController.getState().rest_count,
|
|
1579
|
+
human_rest_ms: humanRestController.getState().total_rest_ms,
|
|
1194
1580
|
last_human_event: lastHumanEvent
|
|
1195
1581
|
});
|
|
1196
1582
|
runControl.setPhase("chat:done");
|
|
@@ -1208,16 +1594,17 @@ export async function runChatWorkflow({
|
|
|
1208
1594
|
viewport_health: {
|
|
1209
1595
|
stats: viewportGuard.getStats(),
|
|
1210
1596
|
events: viewportGuard.getEvents()
|
|
1211
|
-
},
|
|
1212
|
-
human_behavior: effectiveHumanBehavior,
|
|
1213
|
-
human_rest: humanRestController.getState(),
|
|
1214
|
-
|
|
1597
|
+
},
|
|
1598
|
+
human_behavior: effectiveHumanBehavior,
|
|
1599
|
+
human_rest: humanRestController.getState(),
|
|
1600
|
+
collect_cv_processing_floor: collectCvProcessingFloorSnapshot(),
|
|
1601
|
+
last_human_event: lastHumanEvent,
|
|
1215
1602
|
list_end_reason: listEndReason,
|
|
1216
1603
|
target_pass_count: passTarget,
|
|
1217
|
-
process_until_list_end: Boolean(processUntilListEnd),
|
|
1218
|
-
processed_limit: processedLimit,
|
|
1219
|
-
detail_source: normalizedDetailSource,
|
|
1220
|
-
processed: 0,
|
|
1604
|
+
process_until_list_end: Boolean(processUntilListEnd),
|
|
1605
|
+
processed_limit: processedLimit,
|
|
1606
|
+
detail_source: normalizedDetailSource,
|
|
1607
|
+
processed: 0,
|
|
1221
1608
|
screened: 0,
|
|
1222
1609
|
detail_opened: 0,
|
|
1223
1610
|
llm_screened: 0,
|
|
@@ -1248,20 +1635,27 @@ export async function runChatWorkflow({
|
|
|
1248
1635
|
screening_mode: normalizedScreeningMode,
|
|
1249
1636
|
unique_seen: compactInfiniteListState(listState).seen_count,
|
|
1250
1637
|
scroll_count: 0,
|
|
1251
|
-
context_recoveries: contextRecoveryAttempts,
|
|
1252
|
-
viewport_checks: viewportGuard.getStats().checks,
|
|
1253
|
-
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
1254
|
-
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1255
|
-
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1256
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1257
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1258
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
1259
|
-
human_rest_per_candidate_min_ms:
|
|
1260
|
-
human_rest_per_candidate_max_ms:
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1638
|
+
context_recoveries: contextRecoveryAttempts,
|
|
1639
|
+
viewport_checks: viewportGuard.getStats().checks,
|
|
1640
|
+
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
1641
|
+
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
1642
|
+
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
1643
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
1644
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
1645
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
1646
|
+
human_rest_per_candidate_min_ms: null,
|
|
1647
|
+
human_rest_per_candidate_max_ms: null,
|
|
1648
|
+
collect_cv_processing_floor_enabled: collectCvProcessingFloorEnabled,
|
|
1649
|
+
collect_cv_processing_floor_min_ms: collectCvProcessingFloorState.min_ms,
|
|
1650
|
+
collect_cv_processing_floor_max_ms: collectCvProcessingFloorState.max_ms,
|
|
1651
|
+
collect_cv_processing_floor_count: collectCvProcessingFloorState.count,
|
|
1652
|
+
collect_cv_processing_floor_delay_requested_ms: collectCvProcessingFloorState.total_delay_requested_ms,
|
|
1653
|
+
collect_cv_processing_floor_delay_ms: collectCvProcessingFloorState.total_delay_elapsed_ms,
|
|
1654
|
+
collect_cv_processing_floor_current: null,
|
|
1655
|
+
human_rest_count: humanRestController.getState().rest_count,
|
|
1656
|
+
human_rest_ms: humanRestController.getState().total_rest_ms,
|
|
1657
|
+
last_human_event: lastHumanEvent
|
|
1658
|
+
});
|
|
1265
1659
|
|
|
1266
1660
|
while (
|
|
1267
1661
|
results.length < processedLimit
|
|
@@ -1275,9 +1669,15 @@ export async function runChatWorkflow({
|
|
|
1275
1669
|
await runControl.waitIfPaused();
|
|
1276
1670
|
runControl.throwIfCanceled();
|
|
1277
1671
|
runControl.setPhase("chat:candidate");
|
|
1278
|
-
rootState = await ensureChatViewport(
|
|
1279
|
-
const loopTopLevelState = await getChatTopLevelState(client);
|
|
1280
|
-
if (
|
|
1672
|
+
rootState = await ensureChatViewport(await getChatRoots(client), "candidate_loop");
|
|
1673
|
+
const loopTopLevelState = await getChatTopLevelState(client);
|
|
1674
|
+
if (loopTopLevelState.is_security_verification) {
|
|
1675
|
+
throw makeBossSecurityVerificationRequiredError(
|
|
1676
|
+
loopTopLevelState,
|
|
1677
|
+
"chat_candidate_loop"
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
if (!loopTopLevelState.is_chat_shell) {
|
|
1281
1681
|
await recoverAndReapplyChatContext("candidate_loop_non_chat_shell", {
|
|
1282
1682
|
message: `Unexpected chat top-level URL: ${loopTopLevelState.url}`
|
|
1283
1683
|
});
|
|
@@ -1370,9 +1770,15 @@ export async function runChatWorkflow({
|
|
|
1370
1770
|
maxTextScanNodes: 500
|
|
1371
1771
|
})
|
|
1372
1772
|
}));
|
|
1373
|
-
if (!nextCandidateResult.ok) {
|
|
1374
|
-
const endTopLevelState = await getChatTopLevelState(client);
|
|
1375
|
-
if (
|
|
1773
|
+
if (!nextCandidateResult.ok) {
|
|
1774
|
+
const endTopLevelState = await getChatTopLevelState(client);
|
|
1775
|
+
if (endTopLevelState.is_security_verification) {
|
|
1776
|
+
throw makeBossSecurityVerificationRequiredError(
|
|
1777
|
+
endTopLevelState,
|
|
1778
|
+
"chat_candidate_list_end"
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
if (!endTopLevelState.is_chat_shell) {
|
|
1376
1782
|
await recoverAndReapplyChatContext("candidate_list_end_non_chat_shell", {
|
|
1377
1783
|
message: `Unexpected chat top-level URL at list end: ${endTopLevelState.url}`
|
|
1378
1784
|
});
|
|
@@ -1392,15 +1798,21 @@ export async function runChatWorkflow({
|
|
|
1392
1798
|
}
|
|
1393
1799
|
|
|
1394
1800
|
const index = results.length;
|
|
1395
|
-
const cardNodeId = nextCandidateResult.item.node_id;
|
|
1396
|
-
let effectiveCardNodeId = cardNodeId;
|
|
1397
|
-
const candidateKey = nextCandidateResult.item.key;
|
|
1398
|
-
const cardCandidate = nextCandidateResult.item.candidate;
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1801
|
+
const cardNodeId = nextCandidateResult.item.node_id;
|
|
1802
|
+
let effectiveCardNodeId = cardNodeId;
|
|
1803
|
+
const candidateKey = nextCandidateResult.item.key;
|
|
1804
|
+
const cardCandidate = nextCandidateResult.item.candidate;
|
|
1805
|
+
const candidateProcessingStarted = Date.now();
|
|
1806
|
+
const candidateProcessingFloorTargetMs = collectCvProcessingFloorEnabled
|
|
1807
|
+
? sampleChatCollectCvProcessingFloorMs()
|
|
1808
|
+
: 0;
|
|
1809
|
+
|
|
1810
|
+
let screeningCandidate = cardCandidate;
|
|
1811
|
+
let detailResult = null;
|
|
1812
|
+
let preActionState = null;
|
|
1813
|
+
let selectionReadyState = null;
|
|
1814
|
+
let preScreeningActionReconciliation = null;
|
|
1815
|
+
let detailUnavailableReason = "";
|
|
1404
1816
|
if (index < detailCountLimit) {
|
|
1405
1817
|
let detailStep = "start";
|
|
1406
1818
|
const checkpointInProgressCandidate = (patch = {}) => runControl.checkpoint({
|
|
@@ -1415,45 +1827,75 @@ export async function runChatWorkflow({
|
|
|
1415
1827
|
}
|
|
1416
1828
|
});
|
|
1417
1829
|
try {
|
|
1418
|
-
await runControl.waitIfPaused();
|
|
1419
|
-
runControl.throwIfCanceled();
|
|
1420
|
-
runControl.setPhase("chat:detail");
|
|
1421
|
-
|
|
1422
|
-
|
|
1830
|
+
await runControl.waitIfPaused();
|
|
1831
|
+
runControl.throwIfCanceled();
|
|
1832
|
+
runControl.setPhase("chat:detail");
|
|
1833
|
+
checkpointInProgressCandidate({ event: "detail_start" });
|
|
1834
|
+
rootState = await ensureChatViewport(rootState, "detail");
|
|
1423
1835
|
|
|
1424
1836
|
detailStep = "select_candidate";
|
|
1425
1837
|
networkRecorder.clear();
|
|
1426
1838
|
await maybeHumanActionCooldown("before_detail_open", timings);
|
|
1427
|
-
const selected = await measureTiming(timings, "candidate_click_ms", () => selectFreshChatCandidate(client, {
|
|
1428
|
-
cardNodeId,
|
|
1429
|
-
candidate: cardCandidate,
|
|
1430
|
-
timeoutMs: onlineResumeButtonTimeoutMs,
|
|
1431
|
-
onlineResumeProbe: !collectCvOnly
|
|
1432
|
-
}));
|
|
1433
|
-
if (selected.ready?.forbidden_top_level_navigation) {
|
|
1434
|
-
throw makeForbiddenChatResumeNavigationError(selected.ready.top_level_state);
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1839
|
+
const selected = await measureTiming(timings, "candidate_click_ms", () => selectFreshChatCandidate(client, {
|
|
1840
|
+
cardNodeId,
|
|
1841
|
+
candidate: cardCandidate,
|
|
1842
|
+
timeoutMs: onlineResumeButtonTimeoutMs,
|
|
1843
|
+
onlineResumeProbe: !collectCvOnly
|
|
1844
|
+
}));
|
|
1845
|
+
if (selected.ready?.forbidden_top_level_navigation) {
|
|
1846
|
+
throw makeForbiddenChatResumeNavigationError(selected.ready.top_level_state);
|
|
1847
|
+
}
|
|
1848
|
+
selectionReadyState = selected.ready || null;
|
|
1849
|
+
effectiveCardNodeId = selected.card_node_id || cardNodeId;
|
|
1850
|
+
checkpointInProgressCandidate({
|
|
1851
|
+
event: "candidate_selection_verified",
|
|
1852
|
+
selection_ready_state: compactChatCandidateSelectionReadyState(selectionReadyState)
|
|
1853
|
+
});
|
|
1854
|
+
const selectionNetworkEvents = networkRecorder.events.slice();
|
|
1855
|
+
try {
|
|
1856
|
+
preActionState = await readChatConversationReadyState(client);
|
|
1857
|
+
} catch (error) {
|
|
1441
1858
|
preActionState = {
|
|
1442
|
-
error: error?.message || String(error)
|
|
1443
|
-
};
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1859
|
+
error: error?.message || String(error)
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
if (
|
|
1863
|
+
requestResumeForPassed
|
|
1864
|
+
&& !dryRunRequestCv
|
|
1865
|
+
&& effectiveActionJournal
|
|
1866
|
+
&& (preActionState?.already_requested_resume || preActionState?.attachment_resume_enabled)
|
|
1867
|
+
) {
|
|
1868
|
+
const candidateId = normalizeText(cardCandidate.id || "");
|
|
1869
|
+
const actionJournalRecord = candidateId
|
|
1870
|
+
? effectiveActionJournal.read({
|
|
1871
|
+
scope: actionJournalScope,
|
|
1872
|
+
candidateId
|
|
1873
|
+
})
|
|
1874
|
+
: null;
|
|
1875
|
+
preScreeningActionReconciliation = reconcileChatRequestJournalFromExactExistingState({
|
|
1876
|
+
actionJournal: effectiveActionJournal,
|
|
1877
|
+
actionJournalScope,
|
|
1878
|
+
actionJournalRecord,
|
|
1879
|
+
candidateId,
|
|
1880
|
+
selectionReadyState: selected.ready,
|
|
1881
|
+
readyState: preActionState,
|
|
1882
|
+
runId: runControl.runId,
|
|
1883
|
+
greeting: greetingText,
|
|
1884
|
+
checkpointCritical: (patch) => runControl.checkpointCritical(patch)
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
const preDetailSkipReason = chatDetailSkipReasonFromReadyState(preActionState);
|
|
1446
1888
|
if (preDetailSkipReason) {
|
|
1447
1889
|
detailUnavailableReason = preDetailSkipReason;
|
|
1448
1890
|
detailResult = createSkippedDetailResult(cardCandidate, preDetailSkipReason);
|
|
1449
1891
|
detailResult.cv_acquisition.pre_detail_state = preActionState;
|
|
1450
1892
|
detailResult.cv_acquisition.selection_ready_state = selected.ready;
|
|
1451
1893
|
}
|
|
1452
|
-
if (!selected.ready?.ok) {
|
|
1453
|
-
if (detailResult) {
|
|
1454
|
-
// Already classified by the pre-detail conversation state.
|
|
1455
|
-
} else if (selected.ready?.reason === "active_candidate_mismatch") {
|
|
1456
|
-
throw makeChatCandidateSelectionMismatchError(selected, cardCandidate);
|
|
1894
|
+
if (!selected.ready?.ok) {
|
|
1895
|
+
if (detailResult) {
|
|
1896
|
+
// Already classified by the pre-detail conversation state.
|
|
1897
|
+
} else if (selected.ready?.reason === "active_candidate_mismatch") {
|
|
1898
|
+
throw makeChatCandidateSelectionMismatchError(selected, cardCandidate);
|
|
1457
1899
|
} else {
|
|
1458
1900
|
detailStep = "read_conversation_ready_state";
|
|
1459
1901
|
if (preActionState.attachment_resume_enabled) {
|
|
@@ -1463,18 +1905,18 @@ export async function runChatWorkflow({
|
|
|
1463
1905
|
} else {
|
|
1464
1906
|
detailUnavailableReason = "online_resume_button_unavailable";
|
|
1465
1907
|
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason);
|
|
1466
|
-
detailResult.cv_acquisition.pre_detail_state = preActionState;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
if (collectCvOnly && !detailResult) {
|
|
1471
|
-
detailUnavailableReason = preActionState?.has_online_resume
|
|
1472
|
-
? "collect_cv_request_candidate"
|
|
1473
|
-
: "collect_cv_missing_online_resume";
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
if (shouldOpenOnlineResumeForChatDetail({ collectCvOnly, detailResult })) {
|
|
1477
|
-
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
1908
|
+
detailResult.cv_acquisition.pre_detail_state = preActionState;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
if (collectCvOnly && !detailResult) {
|
|
1913
|
+
detailUnavailableReason = preActionState?.has_online_resume
|
|
1914
|
+
? "collect_cv_request_candidate"
|
|
1915
|
+
: "collect_cv_missing_online_resume";
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
if (shouldOpenOnlineResumeForChatDetail({ collectCvOnly, detailResult })) {
|
|
1919
|
+
const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
|
|
1478
1920
|
let networkWait = null;
|
|
1479
1921
|
let contentWait = {
|
|
1480
1922
|
ok: false,
|
|
@@ -1665,57 +2107,212 @@ export async function runChatWorkflow({
|
|
|
1665
2107
|
timeoutMs: 6000,
|
|
1666
2108
|
intervalMs: 250
|
|
1667
2109
|
});
|
|
1668
|
-
captureTarget = captureTargetWait.target || null;
|
|
1669
|
-
const captureNodeId = captureTarget?.node_id || null;
|
|
1670
|
-
if (captureNodeId) {
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
2110
|
+
captureTarget = captureTargetWait.target || null;
|
|
2111
|
+
const captureNodeId = captureTarget?.node_id || null;
|
|
2112
|
+
if (captureNodeId) {
|
|
2113
|
+
const imageEvidencePath = imageEvidenceFilePath({
|
|
2114
|
+
imageOutputDir,
|
|
2115
|
+
domain: "chat",
|
|
2116
|
+
runId: runControl?.runId,
|
|
2117
|
+
index,
|
|
2118
|
+
extension: "jpg"
|
|
2119
|
+
});
|
|
2120
|
+
const captureImageForTarget = (target, targetWait, resumeCheckpoint = null) => captureScrolledNodeScreenshots(
|
|
2121
|
+
client,
|
|
2122
|
+
target.node_id,
|
|
2123
|
+
{
|
|
2124
|
+
filePath: imageEvidencePath,
|
|
2125
|
+
format: "jpeg",
|
|
2126
|
+
quality: 72,
|
|
2127
|
+
optimize: true,
|
|
2128
|
+
resizeMaxWidth: 1100,
|
|
2129
|
+
captureViewport: false,
|
|
2130
|
+
iframeOwnerNodeId: target?.iframe_node_id || null,
|
|
2131
|
+
padding: 0,
|
|
2132
|
+
maxScreenshots: maxImagePages,
|
|
2133
|
+
wheelDeltaY: imageWheelDeltaY,
|
|
2134
|
+
settleMs: 350,
|
|
2135
|
+
scrollMethod: "dom-anchor-fallback-input",
|
|
2136
|
+
scrollDeltaJitterEnabled: effectiveHumanBehavior.listScrollJitter,
|
|
2137
|
+
stepTimeoutMs: 45000,
|
|
2138
|
+
totalTimeoutMs: 90000,
|
|
2139
|
+
duplicateStopCount: 2,
|
|
2140
|
+
skipDuplicateScreenshots: true,
|
|
2141
|
+
requireTerminalProof: true,
|
|
2142
|
+
composeForLlm: true,
|
|
2143
|
+
llmPagesPerImage: 3,
|
|
2144
|
+
llmResizeMaxWidth: 1100,
|
|
2145
|
+
llmQuality: 72,
|
|
2146
|
+
stopBoundarySelector: CHAT_RESUME_IMAGE_STOP_BOUNDARY_SELECTOR,
|
|
2147
|
+
stopBoundaryTextPatterns: CHAT_RESUME_IMAGE_STOP_BOUNDARY_TEXT,
|
|
2148
|
+
stopBoundaryMaxProbeNodes: 360,
|
|
2149
|
+
stopBoundaryTopPadding: 10,
|
|
2150
|
+
stopBoundaryMinCaptureHeight: 180,
|
|
2151
|
+
resumeCheckpoint,
|
|
2152
|
+
metadata: {
|
|
2153
|
+
domain: "chat",
|
|
2154
|
+
capture_mode: "scroll_sequence",
|
|
2155
|
+
capture_scope: "resume_modal_clip",
|
|
2156
|
+
acquisition_reason: normalizedDetailSource === "image"
|
|
2157
|
+
? "forced_image"
|
|
2158
|
+
: "network_miss_image_fallback",
|
|
2159
|
+
run_candidate_index: index,
|
|
2160
|
+
candidate_key: candidateKey,
|
|
2161
|
+
capture_target: target,
|
|
2162
|
+
capture_target_wait: targetWait,
|
|
2163
|
+
resumed_from_checkpoint: Boolean(resumeCheckpoint)
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
);
|
|
2167
|
+
detailStep = "capture_image_fallback";
|
|
2168
|
+
try {
|
|
2169
|
+
imageEvidence = await measureTiming(
|
|
2170
|
+
timings,
|
|
2171
|
+
"screenshot_capture_ms",
|
|
2172
|
+
() => captureImageForTarget(captureTarget, captureTargetWait)
|
|
2173
|
+
);
|
|
2174
|
+
imageEvidence = requireCompleteImageEvidence(imageEvidence, {
|
|
2175
|
+
code: "IMAGE_CAPTURE_EVIDENCE_MISSING",
|
|
2176
|
+
message: "Chat CV capture returned no complete persisted evidence",
|
|
2177
|
+
metadata: { domain: "chat", candidate_key: candidateKey }
|
|
2178
|
+
});
|
|
2179
|
+
} catch (error) {
|
|
2180
|
+
if (!isRecoverableChatImageCaptureError(error)) throw error;
|
|
2181
|
+
const retryReservation = imageCaptureWorkflowRetries.consume(candidateKey);
|
|
2182
|
+
const resumeCheckpoint = retryReservation.allowed
|
|
2183
|
+
? imageCaptureResumeCheckpoint(error)
|
|
2184
|
+
: null;
|
|
2185
|
+
timings.image_capture_workflow_retry = retryReservation;
|
|
2186
|
+
if (!resumeCheckpoint) {
|
|
2187
|
+
imageEvidence = createRequiredImageEvidenceFailure({
|
|
2188
|
+
code: error?.code || "IMAGE_CAPTURE_CHECKPOINT_UNAVAILABLE",
|
|
2189
|
+
message: error?.message || "Chat CV capture failed without a resumable checkpoint",
|
|
2190
|
+
metadata: {
|
|
2191
|
+
domain: "chat",
|
|
2192
|
+
candidate_key: candidateKey,
|
|
2193
|
+
image_capture_workflow_retry: retryReservation
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
timings.image_capture_resume = {
|
|
2197
|
+
attempted: false,
|
|
2198
|
+
ok: false,
|
|
2199
|
+
phase: "checkpoint",
|
|
2200
|
+
error: compactChatRuntimeError(error)
|
|
2201
|
+
};
|
|
2202
|
+
} else {
|
|
2203
|
+
checkpointInProgressCandidate({
|
|
2204
|
+
event: "resume_image_capture_reacquire",
|
|
2205
|
+
error: compactChatRuntimeError(error),
|
|
2206
|
+
checkpoint_id: resumeCheckpoint.checkpoint_id || null
|
|
2207
|
+
});
|
|
2208
|
+
detailStep = "resume_image_capture_reacquire";
|
|
2209
|
+
const resumeAttempt = await attemptImageCaptureCheckpointResume({
|
|
2210
|
+
checkpoint: resumeCheckpoint,
|
|
2211
|
+
reacquire: () => reacquireImageCaptureResumeTarget({
|
|
2212
|
+
domain: "chat",
|
|
2213
|
+
getRoots: () => getChatRoots(client),
|
|
2214
|
+
ensureViewport: (freshRoots) => ensureChatViewport(
|
|
2215
|
+
freshRoots,
|
|
2216
|
+
"image_capture_resume_reacquire"
|
|
2217
|
+
),
|
|
2218
|
+
getDetailState: () => waitForChatResumeModal(client, {
|
|
2219
|
+
timeoutMs: 4000,
|
|
2220
|
+
intervalMs: 200
|
|
2221
|
+
}),
|
|
2222
|
+
isDetailAvailable: (state) => Boolean(
|
|
2223
|
+
!state?.forbidden_top_level_navigation
|
|
2224
|
+
&& (state?.popup || state?.content || state?.resumeIframe)
|
|
2225
|
+
),
|
|
2226
|
+
waitForTarget: (detailState) => waitForCvCaptureTarget(client, detailState, {
|
|
2227
|
+
domain: "chat",
|
|
2228
|
+
timeoutMs: 4000,
|
|
2229
|
+
intervalMs: 200
|
|
2230
|
+
})
|
|
2231
|
+
}),
|
|
2232
|
+
capture: (reacquired) => {
|
|
2233
|
+
rootState = reacquired.root_state;
|
|
2234
|
+
resumeState = reacquired.detail_state;
|
|
2235
|
+
captureTarget = reacquired.target;
|
|
2236
|
+
captureTargetWait = reacquired.target_wait;
|
|
2237
|
+
detailStep = "resume_image_capture";
|
|
2238
|
+
return measureTiming(
|
|
2239
|
+
timings,
|
|
2240
|
+
"screenshot_capture_ms",
|
|
2241
|
+
() => captureImageForTarget(captureTarget, captureTargetWait, resumeCheckpoint)
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
if (resumeAttempt.outcome === "reacquire_failed") {
|
|
2246
|
+
const reacquireError = resumeAttempt.error;
|
|
2247
|
+
imageEvidence = createRequiredImageEvidenceFailure({
|
|
2248
|
+
code: reacquireError?.code || "IMAGE_CAPTURE_RESUME_REACQUIRE_FAILED",
|
|
2249
|
+
message: reacquireError?.message || "Chat CV target reacquisition failed",
|
|
2250
|
+
metadata: {
|
|
2251
|
+
domain: "chat",
|
|
2252
|
+
candidate_key: candidateKey,
|
|
2253
|
+
resumed_from_checkpoint: true,
|
|
2254
|
+
resume_checkpoint_id: resumeCheckpoint.checkpoint_id || null
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
imageEvidence.resumed_from_checkpoint = true;
|
|
2258
|
+
imageEvidence.resume_checkpoint_id = resumeCheckpoint.checkpoint_id || null;
|
|
2259
|
+
imageEvidence.coverage_checkpoint = resumeCheckpoint;
|
|
2260
|
+
timings.image_capture_resume = {
|
|
2261
|
+
attempted: true,
|
|
2262
|
+
ok: false,
|
|
2263
|
+
phase: "reacquire",
|
|
2264
|
+
checkpoint_id: resumeCheckpoint.checkpoint_id || null,
|
|
2265
|
+
error: compactChatRuntimeError(reacquireError)
|
|
2266
|
+
};
|
|
2267
|
+
}
|
|
2268
|
+
if (resumeAttempt.outcome === "completed") {
|
|
2269
|
+
imageEvidence = requireCompleteImageEvidence(resumeAttempt.evidence, {
|
|
2270
|
+
code: "IMAGE_CAPTURE_RESUME_EVIDENCE_MISSING",
|
|
2271
|
+
message: "Chat resumed CV capture returned no complete persisted evidence",
|
|
2272
|
+
metadata: { domain: "chat", candidate_key: candidateKey }
|
|
2273
|
+
});
|
|
2274
|
+
timings.image_capture_resume = {
|
|
2275
|
+
attempted: true,
|
|
2276
|
+
ok: !isIncompleteImageEvidence(imageEvidence),
|
|
2277
|
+
phase: "capture",
|
|
2278
|
+
checkpoint_id: resumeCheckpoint.checkpoint_id || null,
|
|
2279
|
+
confirmed_screenshot_count: resumeCheckpoint.unique_screenshot_count || 0
|
|
2280
|
+
};
|
|
2281
|
+
} else if (resumeAttempt.outcome === "capture_failed") {
|
|
2282
|
+
const resumeError = resumeAttempt.error;
|
|
2283
|
+
const failedCheckpoint = resumeError.capture_checkpoint || resumeCheckpoint;
|
|
2284
|
+
const failedScreenshots = Array.isArray(failedCheckpoint?.screenshots)
|
|
2285
|
+
? failedCheckpoint.screenshots
|
|
2286
|
+
: [];
|
|
2287
|
+
imageEvidence = {
|
|
2288
|
+
schema_version: 1,
|
|
2289
|
+
ok: false,
|
|
2290
|
+
source: "image-scroll-sequence",
|
|
2291
|
+
elapsed_ms: timings.screenshot_capture_ms || 0,
|
|
2292
|
+
capture_count: failedCheckpoint?.confirmed_capture_count || failedScreenshots.length,
|
|
2293
|
+
screenshot_count: failedScreenshots.length,
|
|
2294
|
+
unique_screenshot_count: failedCheckpoint?.unique_screenshot_count || 0,
|
|
2295
|
+
coverage_complete: false,
|
|
2296
|
+
resumed_from_checkpoint: true,
|
|
2297
|
+
resume_checkpoint_id: resumeCheckpoint.checkpoint_id || null,
|
|
2298
|
+
error_code: resumeError?.code || "IMAGE_CAPTURE_RESUME_FAILED",
|
|
2299
|
+
error: resumeError?.message || String(resumeError),
|
|
2300
|
+
file_paths: failedScreenshots.map((item) => item?.file_path).filter(Boolean),
|
|
2301
|
+
llm_file_paths: [],
|
|
2302
|
+
coverage_checkpoint: failedCheckpoint
|
|
2303
|
+
};
|
|
2304
|
+
timings.image_capture_resume = {
|
|
2305
|
+
attempted: true,
|
|
2306
|
+
ok: false,
|
|
2307
|
+
phase: "capture",
|
|
2308
|
+
checkpoint_id: resumeCheckpoint.checkpoint_id || null,
|
|
2309
|
+
error: compactChatRuntimeError(resumeError)
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
source = isIncompleteImageEvidence(imageEvidence) ? "image_capture_failed" : "image";
|
|
2315
|
+
fullCvEvidence = summarizeChatFullCvEvidence({
|
|
1719
2316
|
detailResult,
|
|
1720
2317
|
contentWait,
|
|
1721
2318
|
imageEvidence
|
|
@@ -1728,35 +2325,47 @@ export async function runChatWorkflow({
|
|
|
1728
2325
|
waitResult: networkWait,
|
|
1729
2326
|
imageEvidence
|
|
1730
2327
|
});
|
|
1731
|
-
if (
|
|
2328
|
+
if (
|
|
2329
|
+
callLlmOnImage
|
|
2330
|
+
&& hasScreenableChatFullCvEvidence(fullCvEvidence, imageEvidence)
|
|
2331
|
+
) {
|
|
1732
2332
|
detailStep = "llm_image_screening";
|
|
1733
2333
|
if (!llmConfig) {
|
|
1734
2334
|
llmResult = createMissingLlmConfigResult();
|
|
1735
2335
|
} else {
|
|
1736
2336
|
try {
|
|
1737
|
-
llmResult = await measureTiming(timings, "vision_model_ms", () => callScreeningLlm({
|
|
1738
|
-
candidate: detailResult.candidate,
|
|
1739
|
-
criteria,
|
|
1740
|
-
config: llmConfig,
|
|
1741
|
-
timeoutMs: llmTimeoutMs,
|
|
2337
|
+
llmResult = await measureTiming(timings, "vision_model_ms", () => callScreeningLlm({
|
|
2338
|
+
candidate: detailResult.candidate,
|
|
2339
|
+
criteria,
|
|
2340
|
+
config: llmConfig,
|
|
2341
|
+
timeoutMs: llmTimeoutMs,
|
|
1742
2342
|
imageEvidence,
|
|
1743
2343
|
maxImages: llmImageLimit,
|
|
1744
2344
|
imageDetail: llmImageDetail
|
|
1745
2345
|
}));
|
|
1746
|
-
} catch (error) {
|
|
1747
|
-
if (isFatalLlmProviderError(error)) {
|
|
1748
|
-
throw createFatalLlmRunError(error, {
|
|
1749
|
-
domain: "chat",
|
|
1750
|
-
candidate: detailResult.candidate
|
|
1751
|
-
});
|
|
1752
|
-
}
|
|
1753
|
-
llmResult = createFailedLlmResult(error);
|
|
1754
|
-
}
|
|
2346
|
+
} catch (error) {
|
|
2347
|
+
if (isFatalLlmProviderError(error)) {
|
|
2348
|
+
throw createFatalLlmRunError(error, {
|
|
2349
|
+
domain: "chat",
|
|
2350
|
+
candidate: detailResult.candidate
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
llmResult = createFailedLlmResult(error);
|
|
2354
|
+
}
|
|
1755
2355
|
}
|
|
1756
2356
|
}
|
|
1757
|
-
} else {
|
|
1758
|
-
source = "missing_capture_node";
|
|
1759
|
-
|
|
2357
|
+
} else {
|
|
2358
|
+
source = "missing_capture_node";
|
|
2359
|
+
imageEvidence = createRequiredImageEvidenceFailure({
|
|
2360
|
+
code: "IMAGE_CAPTURE_TARGET_UNAVAILABLE",
|
|
2361
|
+
message: "Chat CV capture target was unavailable after the network fallback missed",
|
|
2362
|
+
metadata: {
|
|
2363
|
+
domain: "chat",
|
|
2364
|
+
candidate_key: candidateKey,
|
|
2365
|
+
capture_target_wait: captureTargetWait || null
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
fullCvEvidence = summarizeChatFullCvEvidence({
|
|
1760
2369
|
detailResult,
|
|
1761
2370
|
contentWait,
|
|
1762
2371
|
imageEvidence
|
|
@@ -1801,10 +2410,16 @@ export async function runChatWorkflow({
|
|
|
1801
2410
|
});
|
|
1802
2411
|
}
|
|
1803
2412
|
|
|
1804
|
-
if (useLlmScreening && !llmResult) {
|
|
1805
|
-
if (!fullCvEvidence
|
|
1806
|
-
|
|
1807
|
-
|
|
2413
|
+
if (useLlmScreening && !llmResult) {
|
|
2414
|
+
if (!hasScreenableChatFullCvEvidence(fullCvEvidence, imageEvidence)) {
|
|
2415
|
+
if (isIncompleteImageEvidence(imageEvidence)) {
|
|
2416
|
+
detailUnavailableReason = `image_capture_failed:${String(
|
|
2417
|
+
imageEvidence?.error_code || "coverage_incomplete"
|
|
2418
|
+
).toLowerCase()}`;
|
|
2419
|
+
} else {
|
|
2420
|
+
detailUnavailableReason = "full_cv_not_acquired";
|
|
2421
|
+
}
|
|
2422
|
+
} else {
|
|
1808
2423
|
detailStep = "llm_screening";
|
|
1809
2424
|
if (!llmConfig) {
|
|
1810
2425
|
llmResult = createMissingLlmConfigResult();
|
|
@@ -1813,24 +2428,24 @@ export async function runChatWorkflow({
|
|
|
1813
2428
|
const llmTimingKey = imageEvidence?.file_paths?.length
|
|
1814
2429
|
? "vision_model_ms"
|
|
1815
2430
|
: "text_model_ms";
|
|
1816
|
-
llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
|
|
1817
|
-
candidate: detailResult.candidate,
|
|
1818
|
-
criteria,
|
|
1819
|
-
config: llmConfig,
|
|
2431
|
+
llmResult = await measureTiming(timings, llmTimingKey, () => callScreeningLlm({
|
|
2432
|
+
candidate: detailResult.candidate,
|
|
2433
|
+
criteria,
|
|
2434
|
+
config: llmConfig,
|
|
1820
2435
|
timeoutMs: llmTimeoutMs,
|
|
1821
2436
|
imageEvidence,
|
|
1822
2437
|
maxImages: llmImageLimit,
|
|
1823
2438
|
imageDetail: llmImageDetail
|
|
1824
2439
|
}));
|
|
1825
|
-
} catch (error) {
|
|
1826
|
-
if (isFatalLlmProviderError(error)) {
|
|
1827
|
-
throw createFatalLlmRunError(error, {
|
|
1828
|
-
domain: "chat",
|
|
1829
|
-
candidate: detailResult.candidate
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1832
|
-
llmResult = createFailedLlmResult(error);
|
|
1833
|
-
}
|
|
2440
|
+
} catch (error) {
|
|
2441
|
+
if (isFatalLlmProviderError(error)) {
|
|
2442
|
+
throw createFatalLlmRunError(error, {
|
|
2443
|
+
domain: "chat",
|
|
2444
|
+
candidate: detailResult.candidate
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
llmResult = createFailedLlmResult(error);
|
|
2448
|
+
}
|
|
1834
2449
|
}
|
|
1835
2450
|
}
|
|
1836
2451
|
}
|
|
@@ -1891,24 +2506,36 @@ export async function runChatWorkflow({
|
|
|
1891
2506
|
event: "detail_error",
|
|
1892
2507
|
error: compactChatRuntimeError(error)
|
|
1893
2508
|
});
|
|
1894
|
-
if (isForbiddenChatResumeNavigationError(error)) {
|
|
2509
|
+
if (isForbiddenChatResumeNavigationError(error)) {
|
|
1895
2510
|
detailUnavailableReason = "forbidden_top_level_resume_navigation";
|
|
1896
2511
|
const recovery = await recoverAndReapplyChatContext(detailUnavailableReason, error);
|
|
1897
|
-
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
1898
|
-
detailResult.cv_acquisition.recovery = recovery;
|
|
1899
|
-
} else if (isChatResumeModalCloseFailureError(error)) {
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2512
|
+
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
2513
|
+
detailResult.cv_acquisition.recovery = recovery;
|
|
2514
|
+
} else if (isChatResumeModalCloseFailureError(error)) {
|
|
2515
|
+
const retryReservation = consumeChatDetailRecoveryBudget(
|
|
2516
|
+
candidateRecoveryCounts,
|
|
2517
|
+
candidateKey
|
|
2518
|
+
);
|
|
2519
|
+
if (retryReservation.allowed) {
|
|
2520
|
+
const recoveryReason = `resume_modal_close_failed:${detailStep}`;
|
|
2521
|
+
const recovery = await recoverAndReapplyChatContext(recoveryReason, error, { forceRefresh: true });
|
|
2522
|
+
checkpointInProgressCandidate({
|
|
2523
|
+
event: "retry_after_modal_recovery",
|
|
2524
|
+
recovery,
|
|
2525
|
+
retry: retryReservation
|
|
2526
|
+
});
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
detailUnavailableReason = "resume_modal_close_failed";
|
|
2530
|
+
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
2531
|
+
detailResult.cv_acquisition.recovery_attempted = true;
|
|
2532
|
+
detailResult.cv_acquisition.recovery_attempt_count = retryReservation.count;
|
|
2533
|
+
detailResult.cv_acquisition.recovery_retry_limit = retryReservation.retry_limit;
|
|
2534
|
+
} else if (isChatCandidateSelectionMismatchError(error)) {
|
|
2535
|
+
const retryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
2536
|
+
if (retryCount < 1) {
|
|
2537
|
+
candidateRecoveryCounts.set(candidateKey, retryCount + 1);
|
|
2538
|
+
const recovery = await recoverAndReapplyChatContext(
|
|
1912
2539
|
"active_candidate_mismatch",
|
|
1913
2540
|
error,
|
|
1914
2541
|
{ forceRefresh: true }
|
|
@@ -1916,42 +2543,55 @@ export async function runChatWorkflow({
|
|
|
1916
2543
|
checkpointInProgressCandidate({
|
|
1917
2544
|
event: "retry_after_active_candidate_mismatch_recovery",
|
|
1918
2545
|
recovery
|
|
1919
|
-
});
|
|
1920
|
-
continue;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
detailResult
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
detailResult =
|
|
1950
|
-
|
|
1951
|
-
|
|
2546
|
+
});
|
|
2547
|
+
continue;
|
|
2548
|
+
}
|
|
2549
|
+
error.retryable = false;
|
|
2550
|
+
checkpointInProgressCandidate({
|
|
2551
|
+
event: "active_candidate_mismatch_exhausted",
|
|
2552
|
+
selection_ready_state: compactChatCandidateSelectionReadyState(error.selection_ready_state),
|
|
2553
|
+
recovery_attempted: true,
|
|
2554
|
+
recovery_attempt_count: retryCount
|
|
2555
|
+
});
|
|
2556
|
+
throw error;
|
|
2557
|
+
} else if (isChatOnlineResumeModalOpenFailureError(error)) {
|
|
2558
|
+
const retryCount = candidateRecoveryCounts.get(candidateKey) || 0;
|
|
2559
|
+
if (retryCount < 1) {
|
|
2560
|
+
candidateRecoveryCounts.set(candidateKey, retryCount + 1);
|
|
2561
|
+
const recovery = await recoverAndReapplyChatContext(
|
|
2562
|
+
"online_resume_modal_did_not_open",
|
|
2563
|
+
error,
|
|
2564
|
+
{ forceRefresh: true }
|
|
2565
|
+
);
|
|
2566
|
+
checkpointInProgressCandidate({
|
|
2567
|
+
event: "retry_after_online_resume_modal_open_failure",
|
|
2568
|
+
recovery
|
|
2569
|
+
});
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
detailUnavailableReason = "online_resume_modal_did_not_open";
|
|
2573
|
+
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
2574
|
+
detailResult.cv_acquisition.attempts = error.attempts || null;
|
|
2575
|
+
detailResult.cv_acquisition.recovery_attempted = true;
|
|
2576
|
+
detailResult.cv_acquisition.recovery_attempt_count = retryCount;
|
|
2577
|
+
} else if (isUnsafeChatOnlineResumeLinkError(error)) {
|
|
2578
|
+
detailUnavailableReason = "unsafe_online_resume_navigation_link";
|
|
2579
|
+
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
2580
|
+
detailResult.cv_acquisition.blocked_pre_click = true;
|
|
2581
|
+
detailResult.cv_acquisition.button_href = error.href || null;
|
|
1952
2582
|
detailResult.cv_acquisition.button_selector = error.button_selector || null;
|
|
1953
2583
|
detailResult.cv_acquisition.attempts = error.attempts || null;
|
|
1954
|
-
} else {
|
|
2584
|
+
} else if (isRecoverableChatImageCaptureError(error)) {
|
|
2585
|
+
detailUnavailableReason = `image_capture_failed:${String(error?.code || "unknown").toLowerCase()}`;
|
|
2586
|
+
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
2587
|
+
detailResult.cv_acquisition.recovery_attempted = true;
|
|
2588
|
+
detailResult.cv_acquisition.recovery_attempt_count = imageCaptureWorkflowRetries.count(candidateKey);
|
|
2589
|
+
detailResult.image_evidence = createRequiredImageEvidenceFailure({
|
|
2590
|
+
code: error?.code || "IMAGE_CAPTURE_FAILED",
|
|
2591
|
+
message: error?.message || "Chat CV image capture failed",
|
|
2592
|
+
metadata: { domain: "chat", candidate_key: candidateKey }
|
|
2593
|
+
});
|
|
2594
|
+
} else {
|
|
1955
2595
|
if (!isRecoverableCdpNodeError(error)) throw error;
|
|
1956
2596
|
detailUnavailableReason = `recoverable_cdp_node_stale:${detailStep}`;
|
|
1957
2597
|
detailResult = createSkippedDetailResult(cardCandidate, detailUnavailableReason, error);
|
|
@@ -1959,62 +2599,437 @@ export async function runChatWorkflow({
|
|
|
1959
2599
|
await closeChatBlockingPanels(client, { attemptsLimit: 2 });
|
|
1960
2600
|
}
|
|
1961
2601
|
}
|
|
1962
|
-
screeningCandidate = detailResult?.candidate || cardCandidate;
|
|
1963
|
-
}
|
|
2602
|
+
screeningCandidate = detailResult?.candidate || cardCandidate;
|
|
2603
|
+
}
|
|
1964
2604
|
|
|
1965
2605
|
await runControl.waitIfPaused();
|
|
1966
2606
|
runControl.throwIfCanceled();
|
|
1967
2607
|
runControl.setPhase("chat:screening");
|
|
1968
2608
|
let cardOnlyLlmResult = null;
|
|
1969
|
-
if (useLlmScreening && !detailUnavailableReason && !detailResult?.llm_result) {
|
|
1970
|
-
detailUnavailableReason = detailResult
|
|
1971
|
-
? "full_cv_not_acquired"
|
|
1972
|
-
: "detail_not_opened_full_cv_required";
|
|
1973
|
-
}
|
|
1974
|
-
const effectiveLlmResult = detailResult?.llm_result || cardOnlyLlmResult;
|
|
1975
|
-
|
|
1976
|
-
? createCvCollectionScreening(screeningCandidate, {
|
|
1977
|
-
detailResult,
|
|
1978
|
-
detailUnavailableReason,
|
|
1979
|
-
preActionState
|
|
1980
|
-
})
|
|
1981
|
-
: detailUnavailableReason
|
|
1982
|
-
? {
|
|
1983
|
-
status: "skip",
|
|
1984
|
-
passed: false,
|
|
1985
|
-
score: 0,
|
|
1986
|
-
reasons: [detailUnavailableReason],
|
|
1987
|
-
candidate: screeningCandidate
|
|
1988
|
-
}
|
|
1989
|
-
: useLlmScreening
|
|
1990
|
-
? llmToScreening(effectiveLlmResult, screeningCandidate)
|
|
1991
|
-
: screenCandidate(screeningCandidate, { criteria });
|
|
1992
|
-
let postAction =
|
|
1993
|
-
if (requestResumeForPassed && screening.passed) {
|
|
1994
|
-
await maybeHumanActionCooldown("before_post_action", timings);
|
|
1995
|
-
postAction = await measureTiming(timings, "post_action_ms", () =>
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2609
|
+
if (useLlmScreening && !detailUnavailableReason && !detailResult?.llm_result) {
|
|
2610
|
+
detailUnavailableReason = detailResult
|
|
2611
|
+
? "full_cv_not_acquired"
|
|
2612
|
+
: "detail_not_opened_full_cv_required";
|
|
2613
|
+
}
|
|
2614
|
+
const effectiveLlmResult = detailResult?.llm_result || cardOnlyLlmResult;
|
|
2615
|
+
let screening = collectCvOnly
|
|
2616
|
+
? createCvCollectionScreening(screeningCandidate, {
|
|
2617
|
+
detailResult,
|
|
2618
|
+
detailUnavailableReason,
|
|
2619
|
+
preActionState
|
|
2620
|
+
})
|
|
2621
|
+
: detailUnavailableReason
|
|
2622
|
+
? {
|
|
2623
|
+
status: "skip",
|
|
2624
|
+
passed: false,
|
|
2625
|
+
score: 0,
|
|
2626
|
+
reasons: [detailUnavailableReason],
|
|
2627
|
+
candidate: screeningCandidate
|
|
2628
|
+
}
|
|
2629
|
+
: useLlmScreening
|
|
2630
|
+
? llmToScreening(effectiveLlmResult, screeningCandidate)
|
|
2631
|
+
: screenCandidate(screeningCandidate, { criteria });
|
|
2632
|
+
let postAction = preScreeningActionReconciliation;
|
|
2633
|
+
if (requestResumeForPassed && screening.passed) {
|
|
2634
|
+
await maybeHumanActionCooldown("before_post_action", timings);
|
|
2635
|
+
postAction = await measureTiming(timings, "post_action_ms", async () => {
|
|
2636
|
+
if (dryRunRequestCv) {
|
|
2637
|
+
return requestChatResumeForPassedCandidate(client, {
|
|
2638
|
+
greetingText,
|
|
2639
|
+
dryRun: true
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
const candidateId = normalizeText(screeningCandidate.id || cardCandidate.id || "");
|
|
2644
|
+
if (!candidateId) {
|
|
2645
|
+
const error = new Error("CHAT_ACTION_CANDIDATE_ID_REQUIRED: refusing outbound action without a stable Boss candidate ID");
|
|
2646
|
+
error.code = "CHAT_ACTION_CANDIDATE_ID_REQUIRED";
|
|
2647
|
+
error.retryable = false;
|
|
2648
|
+
throw error;
|
|
2649
|
+
}
|
|
2650
|
+
if (!effectiveActionJournal) {
|
|
2651
|
+
const error = new Error("CHAT_ACTION_JOURNAL_REQUIRED: durable outbound action journal is unavailable");
|
|
2652
|
+
error.code = "CHAT_ACTION_JOURNAL_REQUIRED";
|
|
2653
|
+
error.retryable = false;
|
|
2654
|
+
throw error;
|
|
2655
|
+
}
|
|
2656
|
+
if (typeof runControl.checkpointCritical !== "function") {
|
|
2657
|
+
const error = new Error("CHAT_CRITICAL_CHECKPOINT_UNAVAILABLE: refusing outbound action without required checkpoint persistence");
|
|
2658
|
+
error.code = "CHAT_CRITICAL_CHECKPOINT_UNAVAILABLE";
|
|
2659
|
+
error.retryable = false;
|
|
2660
|
+
throw error;
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
const activeState = await readChatActiveCandidateState(client);
|
|
2664
|
+
const activeCandidateId = normalizeText(activeState?.active_candidate?.candidate_id || "");
|
|
2665
|
+
if (!activeCandidateId || activeCandidateId !== candidateId) {
|
|
2666
|
+
const error = new Error(`CHAT_ACTIVE_CANDIDATE_MISMATCH: expected=${candidateId}; active=${activeCandidateId || "unknown"}`);
|
|
2667
|
+
error.code = "CHAT_ACTIVE_CANDIDATE_MISMATCH";
|
|
2668
|
+
error.retryable = false;
|
|
2669
|
+
throw error;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
let journalRecord = effectiveActionJournal.read({
|
|
2673
|
+
scope: actionJournalScope,
|
|
2674
|
+
candidateId
|
|
2675
|
+
});
|
|
2676
|
+
const transitionAction = async (state, evidence = {}) => {
|
|
2677
|
+
const transitioned = effectiveActionJournal.transition({
|
|
2678
|
+
scope: actionJournalScope,
|
|
2679
|
+
candidateId,
|
|
2680
|
+
state,
|
|
2681
|
+
runId: runControl.runId,
|
|
2682
|
+
greeting: greetingText,
|
|
2683
|
+
evidence: {
|
|
2684
|
+
...evidence,
|
|
2685
|
+
active_candidate_id: activeCandidateId
|
|
2686
|
+
}
|
|
2687
|
+
});
|
|
2688
|
+
journalRecord = transitioned.record;
|
|
2689
|
+
runControl.checkpointCritical({
|
|
2690
|
+
action_transaction: compactChatActionJournalRecord(
|
|
2691
|
+
transitioned.record,
|
|
2692
|
+
transitioned.file_path
|
|
2693
|
+
)
|
|
2694
|
+
});
|
|
2695
|
+
return transitioned.record;
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
const currentReadyState = await readChatConversationReadyState(client);
|
|
2699
|
+
if (!journalRecord && (
|
|
2700
|
+
currentReadyState.attachment_resume_enabled
|
|
2701
|
+
|| currentReadyState.already_requested_resume
|
|
2702
|
+
)) {
|
|
2703
|
+
return {
|
|
2704
|
+
requested: false,
|
|
2705
|
+
skipped: true,
|
|
2706
|
+
reason: currentReadyState.attachment_resume_enabled
|
|
2707
|
+
? "attachment_resume_already_available"
|
|
2708
|
+
: "resume_request_already_pending",
|
|
2709
|
+
initial_state: currentReadyState,
|
|
2710
|
+
journal_bypassed_existing_state: true
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
if (!journalRecord) {
|
|
2715
|
+
journalRecord = await transitionAction("pre_action", {
|
|
2716
|
+
active_candidate_id: activeCandidateId
|
|
2717
|
+
});
|
|
2718
|
+
const existingGreeting = await getChatGreetingMessageState(client, greetingText);
|
|
2719
|
+
if (existingGreeting.ok && existingGreeting.exact_count > 0) {
|
|
2720
|
+
journalRecord = await transitionAction("greeting_send_in_flight", {
|
|
2721
|
+
greeting_baseline_count: Math.max(0, existingGreeting.exact_count - 1),
|
|
2722
|
+
greeting_evidence_readable: true,
|
|
2723
|
+
reason: "preexisting_exact_greeting_detected"
|
|
2724
|
+
});
|
|
2725
|
+
journalRecord = await transitionAction("greeting_confirmed", {
|
|
2726
|
+
greeting_baseline_count: Math.max(0, existingGreeting.exact_count - 1),
|
|
2727
|
+
greeting_evidence_readable: true,
|
|
2728
|
+
reason: "preexisting_exact_greeting_detected"
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
if (journalRecord.state === "request_confirmed") {
|
|
2734
|
+
return {
|
|
2735
|
+
requested: false,
|
|
2736
|
+
skipped: true,
|
|
2737
|
+
satisfied: true,
|
|
2738
|
+
reason: "request_confirmed_by_durable_journal",
|
|
2739
|
+
action_transaction: compactChatActionJournalRecord(journalRecord)
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
if (
|
|
2744
|
+
journalRecord.state === "greeting_confirmed"
|
|
2745
|
+
&& (currentReadyState.attachment_resume_enabled || currentReadyState.already_requested_resume)
|
|
2746
|
+
) {
|
|
2747
|
+
await transitionAction("request_in_flight", {
|
|
2748
|
+
request_baseline_count: 0,
|
|
2749
|
+
resume_attachment_baseline_count: 0,
|
|
2750
|
+
reason: "existing_request_state_before_input"
|
|
2751
|
+
});
|
|
2752
|
+
journalRecord = await transitionAction("request_confirmed", {
|
|
2753
|
+
reason: currentReadyState.attachment_resume_enabled
|
|
2754
|
+
? "attachment_resume_already_available"
|
|
2755
|
+
: "resume_request_already_pending"
|
|
2756
|
+
});
|
|
2757
|
+
return {
|
|
2758
|
+
requested: false,
|
|
2759
|
+
skipped: true,
|
|
2760
|
+
satisfied: true,
|
|
2761
|
+
reason: "request_confirmed_from_existing_state",
|
|
2762
|
+
action_transaction: compactChatActionJournalRecord(journalRecord)
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
const unknownOrigin = chatActionUnknownOrigin(journalRecord);
|
|
2767
|
+
if (
|
|
2768
|
+
journalRecord.state === "request_in_flight"
|
|
2769
|
+
|| (journalRecord.state === "outcome_unknown" && unknownOrigin === "request_in_flight")
|
|
2770
|
+
) {
|
|
2771
|
+
const {
|
|
2772
|
+
ready_state: readyEvidence,
|
|
2773
|
+
message_state: messageEvidence
|
|
2774
|
+
} = await readChatRequestVerificationEvidence(client);
|
|
2775
|
+
const baselineCount = Number(journalRecord.evidence?.request_baseline_count) || 0;
|
|
2776
|
+
const baselineAttachmentCount = Number(journalRecord.evidence?.resume_attachment_baseline_count) || 0;
|
|
2777
|
+
const requestConfirmed = Boolean(
|
|
2778
|
+
readyEvidence.attachment_resume_enabled
|
|
2779
|
+
|| readyEvidence.already_requested_resume
|
|
2780
|
+
|| messageEvidence.count > baselineCount
|
|
2781
|
+
|| messageEvidence.resume_attachment_count > baselineAttachmentCount
|
|
2782
|
+
);
|
|
2783
|
+
if (requestConfirmed) {
|
|
2784
|
+
journalRecord = await transitionAction("request_confirmed", {
|
|
2785
|
+
reason: "recovered_from_exact_request_evidence",
|
|
2786
|
+
message_observed: messageEvidence.count > baselineCount,
|
|
2787
|
+
request_after_count: messageEvidence.count,
|
|
2788
|
+
resume_attachment_after_count: messageEvidence.resume_attachment_count
|
|
2789
|
+
});
|
|
2790
|
+
return {
|
|
2791
|
+
requested: false,
|
|
2792
|
+
skipped: true,
|
|
2793
|
+
satisfied: true,
|
|
2794
|
+
reason: "request_confirmed_after_in_flight_recovery",
|
|
2795
|
+
action_transaction: compactChatActionJournalRecord(journalRecord)
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
const preservedUnknown = preserveChatRequestOutcomeUnknownWithoutReplay({
|
|
2799
|
+
actionJournalRecord: journalRecord,
|
|
2800
|
+
candidateId,
|
|
2801
|
+
activeCandidateId,
|
|
2802
|
+
readyEvidence,
|
|
2803
|
+
messageEvidence
|
|
2804
|
+
});
|
|
2805
|
+
if (preservedUnknown) return preservedUnknown;
|
|
2806
|
+
if (journalRecord.state !== "outcome_unknown") {
|
|
2807
|
+
journalRecord = await transitionAction("outcome_unknown", {
|
|
2808
|
+
action: "request_resume",
|
|
2809
|
+
reason: "in_flight_request_not_provable"
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
throw makeChatPostActionOutcomeUnknownError(candidateId, journalRecord, {
|
|
2813
|
+
ready_state_readable: Boolean(readyEvidence),
|
|
2814
|
+
message_state_readable: Boolean(messageEvidence.ok)
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
let skipGreeting = journalRecord.state === "greeting_confirmed";
|
|
2819
|
+
if (
|
|
2820
|
+
journalRecord.state === "greeting_send_in_flight"
|
|
2821
|
+
|| (journalRecord.state === "outcome_unknown" && unknownOrigin === "greeting_send_in_flight")
|
|
2822
|
+
) {
|
|
2823
|
+
const greetingEvidence = await getChatGreetingMessageState(client, greetingText);
|
|
2824
|
+
const baselineCount = Number(journalRecord.evidence?.greeting_baseline_count) || 0;
|
|
2825
|
+
if (greetingEvidence.ok && greetingEvidence.exact_count > baselineCount) {
|
|
2826
|
+
journalRecord = await transitionAction("greeting_confirmed", {
|
|
2827
|
+
reason: "recovered_from_exact_greeting_delta",
|
|
2828
|
+
greeting_baseline_count: baselineCount,
|
|
2829
|
+
greeting_evidence_readable: true
|
|
2830
|
+
});
|
|
2831
|
+
skipGreeting = true;
|
|
2832
|
+
} else {
|
|
2833
|
+
if (journalRecord.state !== "outcome_unknown") {
|
|
2834
|
+
journalRecord = await transitionAction("outcome_unknown", {
|
|
2835
|
+
action: "greeting_send",
|
|
2836
|
+
reason: greetingEvidence.ok
|
|
2837
|
+
? "in_flight_greeting_delta_absent"
|
|
2838
|
+
: "in_flight_greeting_evidence_unreadable",
|
|
2839
|
+
greeting_baseline_count: baselineCount,
|
|
2840
|
+
greeting_evidence_readable: greetingEvidence.ok
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
throw makeChatPostActionOutcomeUnknownError(candidateId, journalRecord, {
|
|
2844
|
+
greeting_evidence_readable: greetingEvidence.ok,
|
|
2845
|
+
greeting_exact_count: greetingEvidence.exact_count,
|
|
2846
|
+
greeting_baseline_count: baselineCount
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
if (journalRecord.state === "outcome_unknown") {
|
|
2852
|
+
throw makeChatPostActionOutcomeUnknownError(candidateId, journalRecord);
|
|
2853
|
+
}
|
|
2854
|
+
let editorPreActionRetryCount = 0;
|
|
2855
|
+
while (true) {
|
|
2856
|
+
try {
|
|
2857
|
+
const result = await requestChatResumeForPassedCandidate(client, {
|
|
2858
|
+
greetingText,
|
|
2859
|
+
dryRun: false,
|
|
2860
|
+
skipGreeting,
|
|
2861
|
+
actionTransition: transitionAction
|
|
2862
|
+
});
|
|
2863
|
+
if (editorPreActionRetryCount > 0) {
|
|
2864
|
+
return {
|
|
2865
|
+
...result,
|
|
2866
|
+
editor_pre_action_recovery: {
|
|
2867
|
+
recovered: true,
|
|
2868
|
+
retry_count: editorPreActionRetryCount,
|
|
2869
|
+
settle_ms: CHAT_EDITOR_PRE_ACTION_RETRY_SETTLE_MS
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
return result;
|
|
2874
|
+
} catch (error) {
|
|
2875
|
+
if (!shouldRetryChatEditorPreAction(
|
|
2876
|
+
error,
|
|
2877
|
+
journalRecord,
|
|
2878
|
+
editorPreActionRetryCount
|
|
2879
|
+
)) {
|
|
2880
|
+
throw error;
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
editorPreActionRetryCount += 1;
|
|
2884
|
+
runControl.checkpoint({
|
|
2885
|
+
chat_editor_pre_action_recovery: {
|
|
2886
|
+
candidate_id: candidateId,
|
|
2887
|
+
retry_count: editorPreActionRetryCount,
|
|
2888
|
+
settle_ms: CHAT_EDITOR_PRE_ACTION_RETRY_SETTLE_MS,
|
|
2889
|
+
reason: error?.code || error?.message || "CHAT_EDITOR_MESSAGE_MISMATCH",
|
|
2890
|
+
action_state: journalRecord.state,
|
|
2891
|
+
attempts: Array.isArray(error?.attempts) ? error.attempts : []
|
|
2892
|
+
}
|
|
2893
|
+
});
|
|
2894
|
+
await runControl.sleep(CHAT_EDITOR_PRE_ACTION_RETRY_SETTLE_MS);
|
|
2895
|
+
runControl.throwIfCanceled();
|
|
2896
|
+
|
|
2897
|
+
const topLevelState = await getChatTopLevelState(client);
|
|
2898
|
+
if (topLevelState.is_security_verification) {
|
|
2899
|
+
throw makeBossSecurityVerificationRequiredError(
|
|
2900
|
+
topLevelState,
|
|
2901
|
+
"chat_editor_pre_action_recovery"
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
const latestJournalRecord = effectiveActionJournal.read({
|
|
2905
|
+
scope: actionJournalScope,
|
|
2906
|
+
candidateId
|
|
2907
|
+
});
|
|
2908
|
+
if (latestJournalRecord?.state !== "pre_action") {
|
|
2909
|
+
throw error;
|
|
2910
|
+
}
|
|
2911
|
+
journalRecord = latestJournalRecord;
|
|
2912
|
+
|
|
2913
|
+
const retryActiveState = await readChatActiveCandidateState(client);
|
|
2914
|
+
const retryActiveCandidateId = normalizeText(
|
|
2915
|
+
retryActiveState?.active_candidate?.candidate_id || ""
|
|
2916
|
+
);
|
|
2917
|
+
if (!retryActiveCandidateId || retryActiveCandidateId !== candidateId) {
|
|
2918
|
+
const mismatchError = new Error(
|
|
2919
|
+
`CHAT_ACTIVE_CANDIDATE_MISMATCH: expected=${candidateId}; active=${retryActiveCandidateId || "unknown"}`
|
|
2920
|
+
);
|
|
2921
|
+
mismatchError.code = "CHAT_ACTIVE_CANDIDATE_MISMATCH";
|
|
2922
|
+
mismatchError.retryable = false;
|
|
2923
|
+
throw mismatchError;
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
const retryReadyState = await readChatConversationReadyState(client);
|
|
2927
|
+
if (
|
|
2928
|
+
retryReadyState.attachment_resume_enabled
|
|
2929
|
+
|| retryReadyState.already_requested_resume
|
|
2930
|
+
) {
|
|
2931
|
+
return {
|
|
2932
|
+
requested: false,
|
|
2933
|
+
skipped: true,
|
|
2934
|
+
reason: retryReadyState.attachment_resume_enabled
|
|
2935
|
+
? "attachment_resume_already_available"
|
|
2936
|
+
: "resume_request_already_pending",
|
|
2937
|
+
initial_state: retryReadyState,
|
|
2938
|
+
editor_pre_action_recovery: {
|
|
2939
|
+
recovered: true,
|
|
2940
|
+
retry_count: editorPreActionRetryCount,
|
|
2941
|
+
settle_ms: CHAT_EDITOR_PRE_ACTION_RETRY_SETTLE_MS,
|
|
2942
|
+
converged_to_existing_state: true
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
screening = applyChatProtectedOutcomeSkip(screening, postAction, screeningCandidate);
|
|
2950
|
+
if (postAction?.requested) requestSatisfiedCount += 1;
|
|
2951
|
+
if (postAction?.satisfied && !postAction?.requested) requestSatisfiedCount += 1;
|
|
2952
|
+
if (postAction?.skipped) requestSkippedCount += 1;
|
|
2953
|
+
if (postAction?.requested && !postAction?.skipped) requestedCount += 1;
|
|
2954
|
+
if (!postAction?.requested && !postAction?.skipped && !dryRunRequestCv) {
|
|
2955
|
+
const verificationError = new Error(
|
|
2956
|
+
`REQUEST_CV_NOT_VERIFIED:${postAction?.reason || "unknown"}`
|
|
2957
|
+
);
|
|
2958
|
+
verificationError.code = "REQUEST_CV_NOT_VERIFIED";
|
|
2959
|
+
verificationError.retryable = false;
|
|
2960
|
+
verificationError.attempts = Array.isArray(postAction?.attempts)
|
|
2961
|
+
? postAction.attempts
|
|
2962
|
+
: [];
|
|
2963
|
+
throw verificationError;
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
// Every committed collect-CV outcome converges here: existing attachment/online CV,
|
|
2967
|
+
// an already-pending request, or a newly requested CV. Human-rest and delayMs remain later layers.
|
|
2968
|
+
let processingFloor = null;
|
|
2969
|
+
if (collectCvProcessingFloorEnabled) {
|
|
2970
|
+
const elapsedBeforeFloor = Math.max(0, Date.now() - candidateProcessingStarted);
|
|
2971
|
+
const floorCurrent = {
|
|
2972
|
+
candidate_id: screeningCandidate.id || null,
|
|
2973
|
+
candidate_key: candidateKey,
|
|
2974
|
+
target_ms: candidateProcessingFloorTargetMs,
|
|
2975
|
+
elapsed_before_ms: elapsedBeforeFloor,
|
|
2976
|
+
remaining_ms: Math.max(0, candidateProcessingFloorTargetMs - elapsedBeforeFloor)
|
|
2977
|
+
};
|
|
2978
|
+
runControl.setPhase("chat:collect_cv_processing_floor");
|
|
2979
|
+
runControl.updateProgress({
|
|
2980
|
+
collect_cv_processing_floor_current: floorCurrent
|
|
2981
|
+
});
|
|
2982
|
+
runControl.checkpoint({
|
|
2983
|
+
collect_cv_processing_floor_current: floorCurrent
|
|
2984
|
+
});
|
|
2985
|
+
processingFloor = await enforceChatCollectCvProcessingFloor({
|
|
2986
|
+
candidateStartedAt: candidateProcessingStarted,
|
|
2987
|
+
targetMs: candidateProcessingFloorTargetMs,
|
|
2988
|
+
sleepFn: (ms) => runControl.sleep(ms)
|
|
2989
|
+
});
|
|
2990
|
+
collectCvProcessingFloorState.count += 1;
|
|
2991
|
+
collectCvProcessingFloorState.total_delay_requested_ms += processingFloor.delay_requested_ms;
|
|
2992
|
+
collectCvProcessingFloorState.total_delay_elapsed_ms += processingFloor.delay_elapsed_ms;
|
|
2993
|
+
collectCvProcessingFloorState.last = {
|
|
2994
|
+
candidate_id: screeningCandidate.id || null,
|
|
2995
|
+
candidate_key: candidateKey,
|
|
2996
|
+
...processingFloor
|
|
2997
|
+
};
|
|
2998
|
+
timings.collect_cv_processing_target_ms = processingFloor.target_ms;
|
|
2999
|
+
timings.collect_cv_processing_elapsed_before_floor_ms = processingFloor.elapsed_before_ms;
|
|
3000
|
+
timings.collect_cv_processing_floor_delay_requested_ms = processingFloor.delay_requested_ms;
|
|
3001
|
+
timings.collect_cv_processing_floor_delay_ms = processingFloor.delay_elapsed_ms;
|
|
3002
|
+
timings.collect_cv_processing_elapsed_after_floor_ms = processingFloor.elapsed_after_ms;
|
|
3003
|
+
runControl.updateProgress({
|
|
3004
|
+
collect_cv_processing_floor_count: collectCvProcessingFloorState.count,
|
|
3005
|
+
collect_cv_processing_floor_delay_requested_ms: collectCvProcessingFloorState.total_delay_requested_ms,
|
|
3006
|
+
collect_cv_processing_floor_delay_ms: collectCvProcessingFloorState.total_delay_elapsed_ms,
|
|
3007
|
+
collect_cv_processing_floor_last: collectCvProcessingFloorState.last,
|
|
3008
|
+
collect_cv_processing_floor_current: null
|
|
3009
|
+
});
|
|
3010
|
+
runControl.checkpoint({
|
|
3011
|
+
collect_cv_processing_floor_current: null,
|
|
3012
|
+
collect_cv_processing_floor_last: collectCvProcessingFloorState.last,
|
|
3013
|
+
collect_cv_processing_floor: collectCvProcessingFloorSnapshot()
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
3016
|
+
timings.total_ms = Date.now() - candidateStarted;
|
|
3017
|
+
if (index < detailCountLimit && preActionState) {
|
|
3018
|
+
requireExactChatCandidateSelection({ ready: selectionReadyState }, cardCandidate);
|
|
3019
|
+
}
|
|
3020
|
+
const compactResult = {
|
|
2008
3021
|
index,
|
|
2009
3022
|
candidate_key: candidateKey,
|
|
2010
|
-
card_node_id: effectiveCardNodeId,
|
|
2011
|
-
candidate: compactCandidate(screeningCandidate),
|
|
2012
|
-
|
|
3023
|
+
card_node_id: effectiveCardNodeId,
|
|
3024
|
+
candidate: compactCandidate(screeningCandidate),
|
|
3025
|
+
selection_ready_state: compactChatCandidateSelectionReadyState(selectionReadyState),
|
|
3026
|
+
detail: compactDetail(detailResult),
|
|
2013
3027
|
llm_screening: detailResult ? null : compactLlmResult(cardOnlyLlmResult),
|
|
2014
|
-
screening: compactScreening(screening),
|
|
2015
|
-
post_action: postAction,
|
|
2016
|
-
pre_action_state: preActionState,
|
|
2017
|
-
|
|
3028
|
+
screening: compactScreening(screening),
|
|
3029
|
+
post_action: postAction,
|
|
3030
|
+
pre_action_state: preActionState,
|
|
3031
|
+
collect_cv_processing_floor: processingFloor,
|
|
3032
|
+
timings
|
|
2018
3033
|
};
|
|
2019
3034
|
results.push(compactResult);
|
|
2020
3035
|
markInfiniteListCandidateProcessed(listState, candidateKey, {
|
|
@@ -2046,14 +3061,22 @@ export async function runChatWorkflow({
|
|
|
2046
3061
|
viewport_checks: viewportGuard.getStats().checks,
|
|
2047
3062
|
viewport_recoveries: viewportGuard.getStats().recoveries,
|
|
2048
3063
|
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
2049
|
-
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
2050
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
2051
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
2052
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
2053
|
-
human_rest_per_candidate_min_ms:
|
|
2054
|
-
human_rest_per_candidate_max_ms:
|
|
2055
|
-
|
|
2056
|
-
|
|
3064
|
+
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
3065
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
3066
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
3067
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
3068
|
+
human_rest_per_candidate_min_ms: null,
|
|
3069
|
+
human_rest_per_candidate_max_ms: null,
|
|
3070
|
+
collect_cv_processing_floor_enabled: collectCvProcessingFloorEnabled,
|
|
3071
|
+
collect_cv_processing_floor_min_ms: collectCvProcessingFloorState.min_ms,
|
|
3072
|
+
collect_cv_processing_floor_max_ms: collectCvProcessingFloorState.max_ms,
|
|
3073
|
+
collect_cv_processing_floor_count: collectCvProcessingFloorState.count,
|
|
3074
|
+
collect_cv_processing_floor_delay_requested_ms: collectCvProcessingFloorState.total_delay_requested_ms,
|
|
3075
|
+
collect_cv_processing_floor_delay_ms: collectCvProcessingFloorState.total_delay_elapsed_ms,
|
|
3076
|
+
collect_cv_processing_floor_last: collectCvProcessingFloorState.last,
|
|
3077
|
+
collect_cv_processing_floor_current: null,
|
|
3078
|
+
human_rest_count: humanRestController.getState().rest_count,
|
|
3079
|
+
human_rest_ms: humanRestController.getState().total_rest_ms,
|
|
2057
3080
|
last_human_event: lastHumanEvent,
|
|
2058
3081
|
last_candidate_id: screeningCandidate.id || null,
|
|
2059
3082
|
last_candidate_key: candidateKey,
|
|
@@ -2093,11 +3116,11 @@ export async function runChatWorkflow({
|
|
|
2093
3116
|
addTiming(compactResult.timings, "human_rest_ms", restElapsed);
|
|
2094
3117
|
compactResult.timings.total_ms = Date.now() - candidateStarted;
|
|
2095
3118
|
runControl.updateProgress({
|
|
2096
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
2097
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
2098
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
2099
|
-
human_rest_count: humanRestController.getState().rest_count,
|
|
2100
|
-
human_rest_ms: humanRestController.getState().total_rest_ms,
|
|
3119
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
3120
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
3121
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
3122
|
+
human_rest_count: humanRestController.getState().rest_count,
|
|
3123
|
+
human_rest_ms: humanRestController.getState().total_rest_ms,
|
|
2101
3124
|
human_rest_last: restResult,
|
|
2102
3125
|
context_recoveries: contextRecoveryAttempts,
|
|
2103
3126
|
last_human_event: lastHumanEvent
|
|
@@ -2125,15 +3148,16 @@ export async function runChatWorkflow({
|
|
|
2125
3148
|
stats: viewportGuard.getStats(),
|
|
2126
3149
|
events: viewportGuard.getEvents()
|
|
2127
3150
|
},
|
|
2128
|
-
human_behavior: effectiveHumanBehavior,
|
|
2129
|
-
human_rest: humanRestController.getState(),
|
|
2130
|
-
|
|
3151
|
+
human_behavior: effectiveHumanBehavior,
|
|
3152
|
+
human_rest: humanRestController.getState(),
|
|
3153
|
+
collect_cv_processing_floor: collectCvProcessingFloorSnapshot(),
|
|
3154
|
+
last_human_event: lastHumanEvent,
|
|
2131
3155
|
list_end_reason: listEndReason || null,
|
|
2132
3156
|
target_pass_count: passTarget,
|
|
2133
|
-
process_until_list_end: Boolean(processUntilListEnd),
|
|
2134
|
-
processed_limit: processedLimit,
|
|
2135
|
-
detail_source: normalizedDetailSource,
|
|
2136
|
-
processed: finalCounters.processed,
|
|
3157
|
+
process_until_list_end: Boolean(processUntilListEnd),
|
|
3158
|
+
processed_limit: processedLimit,
|
|
3159
|
+
detail_source: normalizedDetailSource,
|
|
3160
|
+
processed: finalCounters.processed,
|
|
2137
3161
|
screened: finalCounters.screened,
|
|
2138
3162
|
detail_opened: finalCounters.detail_opened,
|
|
2139
3163
|
llm_screened: finalCounters.llm_screened,
|
|
@@ -2147,13 +3171,15 @@ export async function runChatWorkflow({
|
|
|
2147
3171
|
};
|
|
2148
3172
|
}
|
|
2149
3173
|
|
|
2150
|
-
export function createChatRunService({
|
|
2151
|
-
lifecycle,
|
|
2152
|
-
idPrefix = "chat",
|
|
2153
|
-
workflow = runChatWorkflow,
|
|
2154
|
-
onSnapshot = null
|
|
2155
|
-
|
|
2156
|
-
|
|
3174
|
+
export function createChatRunService({
|
|
3175
|
+
lifecycle,
|
|
3176
|
+
idPrefix = "chat",
|
|
3177
|
+
workflow = runChatWorkflow,
|
|
3178
|
+
onSnapshot = null,
|
|
3179
|
+
actionJournal = null
|
|
3180
|
+
} = {}) {
|
|
3181
|
+
const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
|
|
3182
|
+
const configuredActionJournal = actionJournal || null;
|
|
2157
3183
|
|
|
2158
3184
|
function startChatRun({
|
|
2159
3185
|
runId = "",
|
|
@@ -2191,21 +3217,23 @@ export function createChatRunService({
|
|
|
2191
3217
|
listSettleMs = 2200,
|
|
2192
3218
|
listFallbackPoint = null,
|
|
2193
3219
|
imageOutputDir = "",
|
|
2194
|
-
humanRestEnabled = false,
|
|
2195
|
-
humanBehavior = null,
|
|
2196
|
-
|
|
3220
|
+
humanRestEnabled = false,
|
|
3221
|
+
humanBehavior = null,
|
|
3222
|
+
actionJournalScope = "boss-chat:default",
|
|
3223
|
+
name = "chat-domain-run"
|
|
2197
3224
|
} = {}) {
|
|
2198
3225
|
if (!client) throw new Error("startChatRun requires a guarded CDP client");
|
|
2199
|
-
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
2200
|
-
const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
|
|
2201
|
-
const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
|
|
2202
|
-
const processedLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
2203
|
-
const normalizedDetailLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
|
|
2204
|
-
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
2205
|
-
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
2206
|
-
});
|
|
2207
|
-
const
|
|
2208
|
-
const
|
|
3226
|
+
const normalizedDetailSource = normalizeDetailSource(detailSource);
|
|
3227
|
+
const normalizedScreeningMode = normalizeText(criteria) ? normalizeScreeningMode(screeningMode) : "collect_cv";
|
|
3228
|
+
const collectCvOnly = normalizedScreeningMode === "collect_cv" || !normalizeText(criteria);
|
|
3229
|
+
const processedLimit = Math.max(1, Number(maxCandidates) || 1);
|
|
3230
|
+
const normalizedDetailLimit = detailLimit == null ? processedLimit : Math.max(0, Number(detailLimit) || 0);
|
|
3231
|
+
const effectiveHumanBehavior = normalizeHumanBehaviorOptions(humanBehavior, {
|
|
3232
|
+
legacyEnabled: humanRestEnabled === true || llmConfig?.humanRestEnabled === true
|
|
3233
|
+
});
|
|
3234
|
+
const collectCvProcessingFloorEnabled = collectCvOnly;
|
|
3235
|
+
const collectCvPerCandidateRestEnabled = false;
|
|
3236
|
+
const effectiveHumanRestEnabled = effectiveHumanBehavior.restEnabled;
|
|
2209
3237
|
return manager.startRun({
|
|
2210
3238
|
runId,
|
|
2211
3239
|
name,
|
|
@@ -2221,20 +3249,21 @@ export function createChatRunService({
|
|
|
2221
3249
|
detail_limit: normalizedDetailLimit,
|
|
2222
3250
|
detail_source: normalizedDetailSource,
|
|
2223
3251
|
close_resume: closeResume,
|
|
2224
|
-
request_resume_for_passed: Boolean(requestResumeForPassed),
|
|
3252
|
+
request_resume_for_passed: Boolean(requestResumeForPassed),
|
|
3253
|
+
action_journal_enabled: Boolean(requestResumeForPassed),
|
|
2225
3254
|
dry_run_request_cv: Boolean(dryRunRequestCv),
|
|
2226
3255
|
greeting_text: greetingText,
|
|
2227
3256
|
cv_acquisition_mode: cvAcquisitionMode,
|
|
2228
|
-
call_llm_on_image: Boolean(callLlmOnImage),
|
|
2229
|
-
screening_mode: normalizedScreeningMode,
|
|
2230
|
-
cv_collection_mode: normalizedScreeningMode === "collect_cv",
|
|
2231
|
-
llm_configured: Boolean(llmConfig),
|
|
3257
|
+
call_llm_on_image: Boolean(callLlmOnImage),
|
|
3258
|
+
screening_mode: normalizedScreeningMode,
|
|
3259
|
+
cv_collection_mode: normalizedScreeningMode === "collect_cv",
|
|
3260
|
+
llm_configured: Boolean(llmConfig),
|
|
2232
3261
|
llm_timeout_ms: llmTimeoutMs,
|
|
2233
3262
|
llm_image_limit: llmImageLimit,
|
|
2234
|
-
llm_image_detail: llmImageDetail,
|
|
2235
|
-
max_image_pages: maxImagePages,
|
|
2236
|
-
image_wheel_delta_y: imageWheelDeltaY,
|
|
2237
|
-
list_max_scrolls: listMaxScrolls,
|
|
3263
|
+
llm_image_detail: llmImageDetail,
|
|
3264
|
+
max_image_pages: maxImagePages,
|
|
3265
|
+
image_wheel_delta_y: imageWheelDeltaY,
|
|
3266
|
+
list_max_scrolls: listMaxScrolls,
|
|
2238
3267
|
list_stable_signature_limit: listStableSignatureLimit,
|
|
2239
3268
|
list_wheel_delta_y: listWheelDeltaY,
|
|
2240
3269
|
list_settle_ms: listSettleMs,
|
|
@@ -2243,13 +3272,16 @@ export function createChatRunService({
|
|
|
2243
3272
|
image_output_dir: imageOutputDir || "",
|
|
2244
3273
|
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
2245
3274
|
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
2246
|
-
human_behavior: effectiveHumanBehavior,
|
|
2247
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
2248
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
2249
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
2250
|
-
human_rest_per_candidate_min_ms:
|
|
2251
|
-
human_rest_per_candidate_max_ms:
|
|
2252
|
-
|
|
3275
|
+
human_behavior: effectiveHumanBehavior,
|
|
3276
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
3277
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
3278
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
3279
|
+
human_rest_per_candidate_min_ms: null,
|
|
3280
|
+
human_rest_per_candidate_max_ms: null,
|
|
3281
|
+
collect_cv_processing_floor_enabled: collectCvProcessingFloorEnabled,
|
|
3282
|
+
collect_cv_processing_floor_min_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS : null,
|
|
3283
|
+
collect_cv_processing_floor_max_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS : null
|
|
3284
|
+
},
|
|
2253
3285
|
progress: {
|
|
2254
3286
|
card_count: 0,
|
|
2255
3287
|
target_count: targetPassCount || (processUntilListEnd ? "all" : processedLimit),
|
|
@@ -2266,74 +3298,83 @@ export function createChatRunService({
|
|
|
2266
3298
|
request_skipped: 0,
|
|
2267
3299
|
context_recoveries: 0,
|
|
2268
3300
|
human_behavior_enabled: effectiveHumanBehavior.enabled,
|
|
2269
|
-
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
2270
|
-
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
2271
|
-
human_rest_enabled: effectiveHumanRestEnabled,
|
|
2272
|
-
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
2273
|
-
human_rest_per_candidate_min_ms:
|
|
2274
|
-
human_rest_per_candidate_max_ms:
|
|
2275
|
-
|
|
3301
|
+
human_behavior_profile: effectiveHumanBehavior.profile,
|
|
3302
|
+
human_rest_level: effectiveHumanBehavior.restLevel,
|
|
3303
|
+
human_rest_enabled: effectiveHumanRestEnabled,
|
|
3304
|
+
human_rest_per_candidate_enabled: collectCvPerCandidateRestEnabled,
|
|
3305
|
+
human_rest_per_candidate_min_ms: null,
|
|
3306
|
+
human_rest_per_candidate_max_ms: null,
|
|
3307
|
+
collect_cv_processing_floor_enabled: collectCvProcessingFloorEnabled,
|
|
3308
|
+
collect_cv_processing_floor_min_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MIN_MS : null,
|
|
3309
|
+
collect_cv_processing_floor_max_ms: collectCvProcessingFloorEnabled ? CHAT_COLLECT_CV_PROCESSING_FLOOR_MAX_MS : null,
|
|
3310
|
+
collect_cv_processing_floor_count: 0,
|
|
3311
|
+
collect_cv_processing_floor_delay_requested_ms: 0,
|
|
3312
|
+
collect_cv_processing_floor_delay_ms: 0,
|
|
3313
|
+
collect_cv_processing_floor_current: null,
|
|
3314
|
+
human_rest_count: 0,
|
|
2276
3315
|
human_rest_ms: 0,
|
|
2277
3316
|
last_human_event: null
|
|
2278
3317
|
},
|
|
2279
|
-
checkpoint: {},
|
|
2280
|
-
task: async (runControl) => {
|
|
2281
|
-
try {
|
|
2282
|
-
return await workflow({
|
|
2283
|
-
client,
|
|
2284
|
-
targetUrl,
|
|
2285
|
-
job,
|
|
2286
|
-
startFrom,
|
|
2287
|
-
criteria,
|
|
2288
|
-
maxCandidates,
|
|
2289
|
-
targetPassCount,
|
|
2290
|
-
processUntilListEnd,
|
|
2291
|
-
detailLimit: normalizedDetailLimit,
|
|
2292
|
-
detailSource: normalizedDetailSource,
|
|
2293
|
-
closeResume,
|
|
2294
|
-
requestResumeForPassed,
|
|
2295
|
-
dryRunRequestCv,
|
|
2296
|
-
greetingText,
|
|
2297
|
-
delayMs,
|
|
2298
|
-
cardTimeoutMs,
|
|
2299
|
-
readyTimeoutMs,
|
|
2300
|
-
onlineResumeButtonTimeoutMs,
|
|
2301
|
-
resumeDomTimeoutMs,
|
|
2302
|
-
maxImagePages,
|
|
2303
|
-
imageWheelDeltaY,
|
|
2304
|
-
cvAcquisitionMode,
|
|
2305
|
-
callLlmOnImage,
|
|
2306
|
-
llmConfig,
|
|
2307
|
-
llmTimeoutMs,
|
|
2308
|
-
llmImageLimit,
|
|
2309
|
-
llmImageDetail,
|
|
2310
|
-
screeningMode: normalizedScreeningMode,
|
|
2311
|
-
listMaxScrolls,
|
|
2312
|
-
listStableSignatureLimit,
|
|
2313
|
-
listWheelDeltaY,
|
|
2314
|
-
listSettleMs,
|
|
2315
|
-
listFallbackPoint,
|
|
2316
|
-
imageOutputDir,
|
|
2317
|
-
humanRestEnabled: effectiveHumanRestEnabled,
|
|
2318
|
-
humanBehavior: effectiveHumanBehavior
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
3318
|
+
checkpoint: {},
|
|
3319
|
+
task: async (runControl) => {
|
|
3320
|
+
try {
|
|
3321
|
+
return await workflow({
|
|
3322
|
+
client,
|
|
3323
|
+
targetUrl,
|
|
3324
|
+
job,
|
|
3325
|
+
startFrom,
|
|
3326
|
+
criteria,
|
|
3327
|
+
maxCandidates,
|
|
3328
|
+
targetPassCount,
|
|
3329
|
+
processUntilListEnd,
|
|
3330
|
+
detailLimit: normalizedDetailLimit,
|
|
3331
|
+
detailSource: normalizedDetailSource,
|
|
3332
|
+
closeResume,
|
|
3333
|
+
requestResumeForPassed,
|
|
3334
|
+
dryRunRequestCv,
|
|
3335
|
+
greetingText,
|
|
3336
|
+
delayMs,
|
|
3337
|
+
cardTimeoutMs,
|
|
3338
|
+
readyTimeoutMs,
|
|
3339
|
+
onlineResumeButtonTimeoutMs,
|
|
3340
|
+
resumeDomTimeoutMs,
|
|
3341
|
+
maxImagePages,
|
|
3342
|
+
imageWheelDeltaY,
|
|
3343
|
+
cvAcquisitionMode,
|
|
3344
|
+
callLlmOnImage,
|
|
3345
|
+
llmConfig,
|
|
3346
|
+
llmTimeoutMs,
|
|
3347
|
+
llmImageLimit,
|
|
3348
|
+
llmImageDetail,
|
|
3349
|
+
screeningMode: normalizedScreeningMode,
|
|
3350
|
+
listMaxScrolls,
|
|
3351
|
+
listStableSignatureLimit,
|
|
3352
|
+
listWheelDeltaY,
|
|
3353
|
+
listSettleMs,
|
|
3354
|
+
listFallbackPoint,
|
|
3355
|
+
imageOutputDir,
|
|
3356
|
+
humanRestEnabled: effectiveHumanRestEnabled,
|
|
3357
|
+
humanBehavior: effectiveHumanBehavior,
|
|
3358
|
+
actionJournal: configuredActionJournal || createChatActionJournal(),
|
|
3359
|
+
actionJournalScope
|
|
3360
|
+
}, runControl);
|
|
3361
|
+
} catch (error) {
|
|
3362
|
+
if (error instanceof RunCanceledError) throw error;
|
|
3363
|
+
const finalFailureArtifact = await captureChatFinalFailureArtifact(client, {
|
|
3364
|
+
runControl,
|
|
3365
|
+
imageOutputDir,
|
|
3366
|
+
error
|
|
3367
|
+
});
|
|
3368
|
+
if (finalFailureArtifact) {
|
|
3369
|
+
runControl.checkpoint({
|
|
3370
|
+
final_failure_artifact: finalFailureArtifact
|
|
3371
|
+
});
|
|
3372
|
+
}
|
|
3373
|
+
throw error;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
});
|
|
3377
|
+
}
|
|
2337
3378
|
|
|
2338
3379
|
return {
|
|
2339
3380
|
startChatRun,
|