@reconcrap/boss-recommend-mcp 2.0.4 → 2.0.6

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.
@@ -18,6 +18,7 @@ import {
18
18
  markInfiniteListCandidateProcessed,
19
19
  resetInfiniteListForRefreshRound
20
20
  } from "../../core/infinite-list/index.js";
21
+ import { createViewportRunGuard } from "../../core/self-heal/index.js";
21
22
  import { screenCandidate } from "../../core/screening/index.js";
22
23
  import {
23
24
  closeRecruitDetail,
@@ -140,6 +141,18 @@ export async function runRecruitWorkflow({
140
141
  domain: "recruit",
141
142
  listName: "search-results"
142
143
  });
144
+ const viewportGuard = createViewportRunGuard({
145
+ client,
146
+ domain: "recruit",
147
+ root: "frame",
148
+ frameOwnerRoot: "frameOwner",
149
+ runControl,
150
+ getRoots: getRecruitRoots
151
+ });
152
+ async function ensureRecruitViewport(rootState, phase) {
153
+ const result = await viewportGuard.ensure(rootState, { phase });
154
+ return result.rootState || rootState;
155
+ }
143
156
  const results = [];
144
157
  const refreshAttempts = [];
145
158
  let refreshRounds = 0;
@@ -153,6 +166,7 @@ export async function runRecruitWorkflow({
153
166
  runControl.throwIfCanceled();
154
167
  runControl.setPhase("recruit:roots");
155
168
  let rootState = await getRecruitRoots(client);
169
+ rootState = await ensureRecruitViewport(rootState, "roots");
156
170
  runControl.checkpoint({
157
171
  iframe_selector: rootState.iframe.selector,
158
172
  iframe_document_node_id: rootState.iframe.documentNodeId,
@@ -186,11 +200,13 @@ export async function runRecruitWorkflow({
186
200
  }
187
201
  });
188
202
  rootState = await getRecruitRoots(client);
203
+ rootState = await ensureRecruitViewport(rootState, "search");
189
204
  }
190
205
 
191
206
  await runControl.waitIfPaused();
192
207
  runControl.throwIfCanceled();
193
208
  runControl.setPhase("recruit:cards");
209
+ rootState = await ensureRecruitViewport(rootState, "cards");
194
210
  cardNodeIds = await waitForRecruitCardNodeIds(client, rootState.iframe.documentNodeId, {
195
211
  timeoutMs: cardTimeoutMs,
196
212
  intervalMs: 300
@@ -209,13 +225,16 @@ export async function runRecruitWorkflow({
209
225
  unique_seen: compactInfiniteListState(listState).seen_count,
210
226
  scroll_count: 0,
211
227
  refresh_rounds: 0,
212
- refresh_attempts: 0
228
+ refresh_attempts: 0,
229
+ viewport_checks: viewportGuard.getStats().checks,
230
+ viewport_recoveries: viewportGuard.getStats().recoveries
213
231
  });
214
232
 
215
233
  while (results.length < limit) {
216
234
  await runControl.waitIfPaused();
217
235
  runControl.throwIfCanceled();
218
236
  runControl.setPhase("recruit:candidate");
237
+ rootState = await ensureRecruitViewport(rootState, "candidate_loop");
219
238
 
220
239
  const nextCandidateResult = await getNextInfiniteListCandidate({
221
240
  client,
@@ -226,7 +245,9 @@ export async function runRecruitWorkflow({
226
245
  settleMs: listSettleMs,
227
246
  fallbackPoint: listFallbackPoint,
228
247
  findNodeIds: async () => {
229
- const currentRootState = await getRecruitRoots(client);
248
+ let currentRootState = await getRecruitRoots(client);
249
+ currentRootState = await ensureRecruitViewport(currentRootState, "candidate_find_nodes");
250
+ rootState = currentRootState;
230
251
  const currentCardNodeIds = await waitForRecruitCardNodeIds(client, currentRootState.iframe.documentNodeId, {
231
252
  timeoutMs: Math.min(cardTimeoutMs, 5000),
232
253
  intervalMs: 300
@@ -283,10 +304,13 @@ export async function runRecruitWorkflow({
283
304
  refresh_attempts: refreshAttempts.length,
284
305
  refresh_method: refreshResult.method || null,
285
306
  refresh_forced_recent_viewed: true,
286
- list_end_reason: listEndReason
307
+ list_end_reason: listEndReason,
308
+ viewport_checks: viewportGuard.getStats().checks,
309
+ viewport_recoveries: viewportGuard.getStats().recoveries
287
310
  });
288
311
  if (refreshResult.ok) {
289
312
  rootState = await getRecruitRoots(client);
313
+ rootState = await ensureRecruitViewport(rootState, "refresh_after");
290
314
  cardNodeIds = await waitForRecruitCardNodeIds(client, rootState.iframe.documentNodeId, {
291
315
  timeoutMs: cardTimeoutMs,
292
316
  intervalMs: 300
@@ -318,6 +342,7 @@ export async function runRecruitWorkflow({
318
342
  await runControl.waitIfPaused();
319
343
  runControl.throwIfCanceled();
320
344
  runControl.setPhase("recruit:detail");
345
+ rootState = await ensureRecruitViewport(rootState, "detail");
321
346
  networkRecorder.clear();
322
347
  const openedDetail = await openRecruitCardDetail(client, cardNodeId);
323
348
  const waitPlan = getCvNetworkWaitPlan(cvAcquisitionState);
@@ -430,6 +455,8 @@ export async function runRecruitWorkflow({
430
455
  refresh_rounds: refreshRounds,
431
456
  refresh_attempts: refreshAttempts.length,
432
457
  list_end_reason: listEndReason || null,
458
+ viewport_checks: viewportGuard.getStats().checks,
459
+ viewport_recoveries: viewportGuard.getStats().recoveries,
433
460
  last_candidate_id: screeningCandidate.id || null,
434
461
  last_candidate_key: candidateKey,
435
462
  last_score: screening.score
@@ -459,6 +486,10 @@ export async function runRecruitWorkflow({
459
486
  search_params: normalizedSearchParams,
460
487
  card_count: cardNodeIds.length,
461
488
  candidate_list: compactInfiniteListState(listState),
489
+ viewport_health: {
490
+ stats: viewportGuard.getStats(),
491
+ events: viewportGuard.getEvents()
492
+ },
462
493
  list_end_reason: listEndReason || null,
463
494
  refresh_rounds: refreshRounds,
464
495
  refresh_attempts: refreshAttempts,
package/src/index.js CHANGED
@@ -1911,7 +1911,8 @@ async function handleRunRecommendSelfHealTool({ workspaceRoot, args }) {
1911
1911
  domain: "recommend",
1912
1912
  roots: rootState?.roots || {},
1913
1913
  selectorProbes: config.selectorProbes,
1914
- accessibilityProbes: config.accessibilityProbes
1914
+ accessibilityProbes: config.accessibilityProbes,
1915
+ viewportProbes: config.viewportProbes
1915
1916
  });
1916
1917
  assertNoForbiddenCdpCalls(methodLog);
1917
1918
 
@@ -623,7 +623,8 @@ async function waitForHealthyRecommend(client, config, {
623
623
  domain: "recommend",
624
624
  roots: roots.roots,
625
625
  selectorProbes: config.selectorProbes,
626
- accessibilityProbes: config.accessibilityProbes
626
+ accessibilityProbes: config.accessibilityProbes,
627
+ viewportProbes: config.viewportProbes
627
628
  });
628
629
  if (lastCheck.status === HEALTH_STATUS.HEALTHY) return lastCheck;
629
630
  await sleep(intervalMs);