@reconcrap/boss-recommend-mcp 2.1.24 → 2.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/chat-runtime-config.js +7 -5
- package/src/core/browser/index.js +16 -9
- package/src/core/run/index.js +5 -4
- package/src/core/screening/index.js +291 -26
- package/src/domains/chat/action-journal.js +97 -4
- package/src/domains/recommend/actions.js +740 -36
- package/src/domains/recommend/colleague-contact.js +836 -90
- package/src/domains/recommend/detail.js +4029 -128
- package/src/domains/recommend/filters.js +46 -28
- package/src/domains/recommend/jobs.js +32 -3
- package/src/domains/recommend/location.js +93 -46
- package/src/domains/recommend/refresh.js +354 -69
- package/src/domains/recommend/run-service.js +2499 -484
- package/src/recommend-mcp.js +514 -92
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
clickNodeCenter,
|
|
3
|
+
describeNode,
|
|
4
|
+
getFrameDocumentNodeId,
|
|
3
5
|
getNodeBox,
|
|
4
6
|
getOuterHTML,
|
|
5
7
|
querySelectorAll,
|
|
@@ -8,12 +10,10 @@ import {
|
|
|
8
10
|
import { htmlToText } from "../../core/screening/index.js";
|
|
9
11
|
|
|
10
12
|
const COLLEAGUE_SECTION_SELECTOR = ".colleague-collaboration";
|
|
11
|
-
const COLLEAGUE_TAB_SELECTOR = ".colleague-collaboration .tab-hd";
|
|
12
|
-
const SELECTED_TAB_SELECTOR = ".colleague-collaboration .tab-hd .selected";
|
|
13
13
|
const SECTION_SELECTED_TAB_SELECTOR = ".tab-hd .selected";
|
|
14
|
-
const TAB_CANDIDATE_SELECTOR = ".tab-hd span, .tab-hd div, .tab-hd
|
|
15
|
-
const
|
|
16
|
-
const
|
|
14
|
+
const TAB_CANDIDATE_SELECTOR = ".tab-hd > span, .tab-hd > div, .tab-hd > button, .tab-hd > li";
|
|
15
|
+
const SECTION_ROW_SELECTOR = ".record-item.mate-log-item";
|
|
16
|
+
const SECTION_ROW_CONTENT_SELECTOR = ".record-item.mate-log-item .content";
|
|
17
17
|
const DETAIL_PANE_SELECTOR = ".resume-item-detail";
|
|
18
18
|
|
|
19
19
|
function normalizeText(value) {
|
|
@@ -60,6 +60,7 @@ export function parseColleagueContactDate(text, {
|
|
|
60
60
|
const raw = normalizeText(text);
|
|
61
61
|
if (!raw) return null;
|
|
62
62
|
const today = dateOnly(referenceDate) || dateOnly(new Date());
|
|
63
|
+
if (/(?:刚刚|刚才)/.test(raw)) return today;
|
|
63
64
|
const relativeDays = raw.match(/(\d+)\s*天前/);
|
|
64
65
|
if (relativeDays) {
|
|
65
66
|
const days = Number.parseInt(relativeDays[1], 10);
|
|
@@ -127,7 +128,7 @@ async function queryAcrossRoots(client, roots, selector) {
|
|
|
127
128
|
const matches = [];
|
|
128
129
|
for (const root of roots || []) {
|
|
129
130
|
if (!root?.nodeId) continue;
|
|
130
|
-
const nodeIds = await querySelectorAll(client, root.nodeId, selector)
|
|
131
|
+
const nodeIds = await querySelectorAll(client, root.nodeId, selector);
|
|
131
132
|
for (const nodeId of nodeIds) {
|
|
132
133
|
matches.push({
|
|
133
134
|
root: root.name,
|
|
@@ -141,167 +142,794 @@ async function queryAcrossRoots(client, roots, selector) {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
function tabIsColleague(text) {
|
|
144
|
-
return
|
|
145
|
+
return normalizeText(text) === "同事沟通进度";
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
function isUsableVisibleRowBox(box) {
|
|
149
|
+
return Number(box?.rect?.width || 0) > 2 && Number(box?.rect?.height || 0) > 2;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function readVisibleNodeEvidence(client, nodeId) {
|
|
153
|
+
let box = null;
|
|
154
|
+
try {
|
|
155
|
+
box = await getNodeBox(client, nodeId);
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
if (!isUsableVisibleRowBox(box)) return null;
|
|
160
|
+
const node = await describeNode(client, nodeId, { depth: 0, pierce: true });
|
|
161
|
+
if (!Number.isInteger(node?.backendNodeId) || node.backendNodeId <= 0) return null;
|
|
162
|
+
return {
|
|
163
|
+
node_id: nodeId,
|
|
164
|
+
backend_node_id: node.backendNodeId,
|
|
165
|
+
box: {
|
|
166
|
+
x: box.rect.x,
|
|
167
|
+
y: box.rect.y,
|
|
168
|
+
width: box.rect.width,
|
|
169
|
+
height: box.rect.height
|
|
157
170
|
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function readSelectedColleagueTab(client, sectionNodeId) {
|
|
175
|
+
const selectedIds = await querySelectorAll(client, sectionNodeId, SECTION_SELECTED_TAB_SELECTOR);
|
|
176
|
+
const visibleSelected = [];
|
|
177
|
+
for (const nodeId of selectedIds) {
|
|
178
|
+
const evidence = await readVisibleNodeEvidence(client, nodeId);
|
|
179
|
+
if (!evidence) continue;
|
|
180
|
+
visibleSelected.push({
|
|
181
|
+
...evidence,
|
|
182
|
+
text: normalizeText(await textForNode(client, nodeId))
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const selected = visibleSelected.length === 1 && tabIsColleague(visibleSelected[0].text);
|
|
186
|
+
return {
|
|
187
|
+
selected,
|
|
188
|
+
selected_text: visibleSelected[0]?.text || "",
|
|
189
|
+
selected_tab_count: visibleSelected.length,
|
|
190
|
+
node_id: visibleSelected[0]?.node_id || null,
|
|
191
|
+
backend_node_id: visibleSelected[0]?.backend_node_id || null,
|
|
192
|
+
box: visibleSelected[0]?.box || null
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function ensureColleagueTabSelected(client, sectionNodeId) {
|
|
197
|
+
const before = await readSelectedColleagueTab(client, sectionNodeId);
|
|
198
|
+
if (before.selected) {
|
|
199
|
+
return {
|
|
200
|
+
...before,
|
|
201
|
+
changed: false
|
|
202
|
+
};
|
|
158
203
|
}
|
|
159
204
|
|
|
160
|
-
const candidateIds = await querySelectorAll(client, sectionNodeId, TAB_CANDIDATE_SELECTOR)
|
|
205
|
+
const candidateIds = await querySelectorAll(client, sectionNodeId, TAB_CANDIDATE_SELECTOR);
|
|
161
206
|
for (const nodeId of candidateIds) {
|
|
162
|
-
const
|
|
207
|
+
const evidence = await readVisibleNodeEvidence(client, nodeId);
|
|
208
|
+
if (!evidence) continue;
|
|
209
|
+
const text = normalizeText(await textForNode(client, nodeId));
|
|
163
210
|
if (!tabIsColleague(text)) continue;
|
|
164
211
|
const box = await clickNodeCenter(client, nodeId, { scrollIntoView: true });
|
|
165
212
|
await sleep(500);
|
|
213
|
+
const after = await readSelectedColleagueTab(client, sectionNodeId);
|
|
166
214
|
return {
|
|
167
|
-
|
|
215
|
+
...after,
|
|
168
216
|
changed: true,
|
|
169
|
-
selected_text: text,
|
|
170
217
|
click_box: {
|
|
171
218
|
rect: box.rect,
|
|
172
219
|
center: box.center
|
|
173
|
-
}
|
|
220
|
+
},
|
|
221
|
+
reason: after.selected ? null : "colleague_tab_selection_not_verified"
|
|
174
222
|
};
|
|
175
223
|
}
|
|
176
224
|
|
|
177
225
|
return {
|
|
178
|
-
|
|
226
|
+
...before,
|
|
179
227
|
changed: false,
|
|
180
|
-
|
|
181
|
-
? normalizeText(await textForNode(client, selectedIds[0]).catch(() => ""))
|
|
182
|
-
: ""
|
|
228
|
+
reason: "colleague_tab_candidate_not_visible"
|
|
183
229
|
};
|
|
184
230
|
}
|
|
185
231
|
|
|
186
|
-
async function readContactRows(client,
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
232
|
+
async function readContactRows(client, sectionNodeId, sectionBackendNodeId, sectionRoot = "") {
|
|
233
|
+
const contentNodeIds = await querySelectorAll(client, sectionNodeId, SECTION_ROW_CONTENT_SELECTOR);
|
|
234
|
+
const fallbackNodeIds = contentNodeIds.length
|
|
235
|
+
? []
|
|
236
|
+
: await querySelectorAll(client, sectionNodeId, SECTION_ROW_SELECTOR);
|
|
237
|
+
const nodeIds = contentNodeIds.length ? contentNodeIds : fallbackNodeIds;
|
|
190
238
|
const rows = [];
|
|
239
|
+
const unreadableRows = [];
|
|
191
240
|
const seen = new Set();
|
|
192
|
-
for (const
|
|
193
|
-
|
|
194
|
-
|
|
241
|
+
for (const nodeId of nodeIds) {
|
|
242
|
+
let box = null;
|
|
243
|
+
try {
|
|
244
|
+
box = await getNodeBox(client, nodeId);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
unreadableRows.push({
|
|
247
|
+
node_id: nodeId,
|
|
248
|
+
stage: "box",
|
|
249
|
+
error: error?.message || String(error)
|
|
250
|
+
});
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (!isUsableVisibleRowBox(box)) {
|
|
254
|
+
unreadableRows.push({
|
|
255
|
+
node_id: nodeId,
|
|
256
|
+
stage: "visible_box"
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const node = await describeNode(client, nodeId, { depth: 0, pierce: true });
|
|
261
|
+
if (!Number.isInteger(node?.backendNodeId) || node.backendNodeId <= 0) {
|
|
262
|
+
unreadableRows.push({
|
|
263
|
+
node_id: nodeId,
|
|
264
|
+
stage: "backend_identity"
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const text = normalizeText(await textForNode(client, nodeId));
|
|
269
|
+
if (!text) {
|
|
270
|
+
unreadableRows.push({
|
|
271
|
+
node_id: nodeId,
|
|
272
|
+
backend_node_id: node.backendNodeId,
|
|
273
|
+
stage: "text"
|
|
274
|
+
});
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (seen.has(text)) continue;
|
|
195
278
|
seen.add(text);
|
|
196
279
|
rows.push({
|
|
197
280
|
text,
|
|
198
|
-
root:
|
|
199
|
-
selector:
|
|
200
|
-
node_id:
|
|
281
|
+
root: sectionRoot,
|
|
282
|
+
selector: contentNodeIds.length ? SECTION_ROW_CONTENT_SELECTOR : SECTION_ROW_SELECTOR,
|
|
283
|
+
node_id: nodeId,
|
|
284
|
+
backend_node_id: node.backendNodeId,
|
|
285
|
+
section_node_id: sectionNodeId,
|
|
286
|
+
section_backend_node_id: sectionBackendNodeId,
|
|
287
|
+
visible: true,
|
|
288
|
+
box: {
|
|
289
|
+
x: box.rect.x,
|
|
290
|
+
y: box.rect.y,
|
|
291
|
+
width: box.rect.width,
|
|
292
|
+
height: box.rect.height
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
rows,
|
|
298
|
+
unreadable_rows: unreadableRows
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function paneBindingMatches(sectionEvidence, selectedEvidence, section, tab) {
|
|
303
|
+
return Boolean(
|
|
304
|
+
sectionEvidence
|
|
305
|
+
&& sectionEvidence.backend_node_id === section.backend_node_id
|
|
306
|
+
&& selectedEvidence?.selected === true
|
|
307
|
+
&& selectedEvidence.selected_tab_count === 1
|
|
308
|
+
&& selectedEvidence.node_id === tab.node_id
|
|
309
|
+
&& selectedEvidence.backend_node_id === tab.backend_node_id
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function readPaneBindingAtPosition(client, section, tab) {
|
|
314
|
+
const sectionEvidence = await readVisibleNodeEvidence(client, section.node_id);
|
|
315
|
+
const selectedEvidence = await readSelectedColleagueTab(client, section.node_id);
|
|
316
|
+
return {
|
|
317
|
+
verified: paneBindingMatches(sectionEvidence, selectedEvidence, section, tab),
|
|
318
|
+
section_visible: Boolean(sectionEvidence),
|
|
319
|
+
section_node_id: section.node_id,
|
|
320
|
+
section_backend_node_id: sectionEvidence?.backend_node_id || null,
|
|
321
|
+
selected_tab_node_id: selectedEvidence.node_id || null,
|
|
322
|
+
selected_tab_backend_node_id: selectedEvidence.backend_node_id || null,
|
|
323
|
+
selected_tab_text: selectedEvidence.selected_text,
|
|
324
|
+
selected_tab_count: selectedEvidence.selected_tab_count
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function resolveBoundScrollTarget(client, scopes, section) {
|
|
329
|
+
const sectionScope = (scopes || []).find((scope) => (
|
|
330
|
+
scope?.name === section.root
|
|
331
|
+
&& scope?.nodeId === section.root_node_id
|
|
332
|
+
));
|
|
333
|
+
const detailPanes = sectionScope
|
|
334
|
+
? await queryAcrossRoots(client, [sectionScope], DETAIL_PANE_SELECTOR)
|
|
335
|
+
: [];
|
|
336
|
+
const visiblePanes = [];
|
|
337
|
+
for (const pane of detailPanes) {
|
|
338
|
+
const evidence = await readVisibleNodeEvidence(client, pane.node_id);
|
|
339
|
+
if (!evidence) continue;
|
|
340
|
+
visiblePanes.push({
|
|
341
|
+
...pane,
|
|
342
|
+
...evidence
|
|
201
343
|
});
|
|
202
344
|
}
|
|
203
|
-
|
|
345
|
+
if (visiblePanes.length > 1) {
|
|
346
|
+
return {
|
|
347
|
+
ok: false,
|
|
348
|
+
reason: "scroll_target_ambiguous",
|
|
349
|
+
visible_target_count: visiblePanes.length
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (visiblePanes.length === 1) {
|
|
353
|
+
return {
|
|
354
|
+
ok: true,
|
|
355
|
+
node_id: visiblePanes[0].node_id,
|
|
356
|
+
backend_node_id: visiblePanes[0].backend_node_id,
|
|
357
|
+
selector: visiblePanes[0].selector,
|
|
358
|
+
box: visiblePanes[0].box
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const sectionEvidence = await readVisibleNodeEvidence(client, section.node_id);
|
|
362
|
+
if (!sectionEvidence || sectionEvidence.backend_node_id !== section.backend_node_id) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
reason: "scroll_target_box_unavailable",
|
|
366
|
+
visible_target_count: 0
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
ok: true,
|
|
371
|
+
node_id: section.node_id,
|
|
372
|
+
backend_node_id: section.backend_node_id,
|
|
373
|
+
selector: COLLEAGUE_SECTION_SELECTOR,
|
|
374
|
+
box: sectionEvidence.box
|
|
375
|
+
};
|
|
204
376
|
}
|
|
205
377
|
|
|
206
|
-
|
|
207
|
-
|
|
378
|
+
function addRowsFromPosition(byText, positionRows, positionIndex) {
|
|
379
|
+
for (const row of positionRows) {
|
|
380
|
+
const existing = byText.get(row.text);
|
|
381
|
+
if (existing) {
|
|
382
|
+
if (!existing.observed_at_positions.includes(positionIndex)) {
|
|
383
|
+
existing.observed_at_positions.push(positionIndex);
|
|
384
|
+
}
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
byText.set(row.text, {
|
|
388
|
+
...row,
|
|
389
|
+
observed_at_positions: [positionIndex]
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function scanContactRowsAcrossScrollPositions(client, scopes, section, tab, {
|
|
395
|
+
enabled = true,
|
|
396
|
+
maxScrolls = 24,
|
|
208
397
|
settleMs = 350
|
|
209
398
|
} = {}) {
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
399
|
+
const stableEndSamplesRequired = 2;
|
|
400
|
+
const boundedMaxScrolls = enabled
|
|
401
|
+
? Math.max(
|
|
402
|
+
stableEndSamplesRequired,
|
|
403
|
+
Math.min(48, Number.isFinite(Number(maxScrolls)) ? Math.floor(Number(maxScrolls)) : 24)
|
|
404
|
+
)
|
|
405
|
+
: 0;
|
|
406
|
+
const scrollTarget = enabled
|
|
407
|
+
? await resolveBoundScrollTarget(client, scopes, section)
|
|
408
|
+
: null;
|
|
409
|
+
if (enabled && !scrollTarget?.ok) {
|
|
410
|
+
return {
|
|
411
|
+
completed: false,
|
|
412
|
+
coverage_verified: false,
|
|
413
|
+
reason: scrollTarget?.reason || "scroll_target_box_unavailable",
|
|
414
|
+
scrolls_requested: boundedMaxScrolls,
|
|
415
|
+
scrolls_completed: 0,
|
|
416
|
+
position_count: 0,
|
|
417
|
+
positions: [],
|
|
418
|
+
rows: []
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const rowsByText = new Map();
|
|
423
|
+
const positions = [];
|
|
424
|
+
let scrollsCompleted = 0;
|
|
425
|
+
let previousRowSignature = null;
|
|
426
|
+
let previousRowLayoutSignature = null;
|
|
427
|
+
let stableSignatureCount = 0;
|
|
428
|
+
let effectiveScrollCount = 0;
|
|
429
|
+
let endProof = null;
|
|
430
|
+
const targetHeight = Number(scrollTarget?.box?.height || 0);
|
|
431
|
+
const stepDeltaY = enabled
|
|
432
|
+
? Math.max(1, Math.min(480, Math.floor(targetHeight * 0.65)))
|
|
433
|
+
: 0;
|
|
434
|
+
|
|
435
|
+
for (let positionIndex = 0; positionIndex <= boundedMaxScrolls; positionIndex += 1) {
|
|
436
|
+
const bindingBefore = await readPaneBindingAtPosition(client, section, tab);
|
|
437
|
+
if (!bindingBefore.verified) {
|
|
438
|
+
return {
|
|
439
|
+
completed: false,
|
|
440
|
+
coverage_verified: false,
|
|
441
|
+
reason: "colleague_binding_lost",
|
|
442
|
+
scrolls_requested: boundedMaxScrolls,
|
|
443
|
+
scrolls_completed: scrollsCompleted,
|
|
444
|
+
position_count: positions.length,
|
|
445
|
+
positions,
|
|
446
|
+
rows: [],
|
|
447
|
+
failed_position: positionIndex,
|
|
448
|
+
failed_binding_phase: "before_rows",
|
|
449
|
+
binding: bindingBefore
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const positionRead = await readContactRows(
|
|
454
|
+
client,
|
|
455
|
+
section.node_id,
|
|
456
|
+
section.backend_node_id,
|
|
457
|
+
section.root
|
|
458
|
+
);
|
|
459
|
+
const positionRows = positionRead.rows;
|
|
460
|
+
const bindingAfter = await readPaneBindingAtPosition(client, section, tab);
|
|
461
|
+
const rowIdentityKeys = positionRows
|
|
462
|
+
.map((row) => `${row.backend_node_id}:${row.text}`)
|
|
463
|
+
.sort();
|
|
464
|
+
const rowTexts = positionRows.map((row) => row.text).sort();
|
|
465
|
+
const rowSignature = JSON.stringify(rowIdentityKeys);
|
|
466
|
+
const orderedRows = positionRows
|
|
467
|
+
.map((row) => ({
|
|
468
|
+
backend_node_id: row.backend_node_id,
|
|
469
|
+
text: row.text,
|
|
470
|
+
x: Math.round(Number(row?.box?.x || 0)),
|
|
471
|
+
y: Math.round(Number(row?.box?.y || 0)),
|
|
472
|
+
width: Math.round(Number(row?.box?.width || 0)),
|
|
473
|
+
height: Math.round(Number(row?.box?.height || 0))
|
|
474
|
+
}))
|
|
475
|
+
.sort((left, right) => (
|
|
476
|
+
left.y - right.y
|
|
477
|
+
|| left.x - right.x
|
|
478
|
+
|| left.backend_node_id - right.backend_node_id
|
|
479
|
+
));
|
|
480
|
+
const rowLayoutKeys = orderedRows.map((row) => (
|
|
481
|
+
`${row.text}:${row.x}:${row.y}:${row.width}:${row.height}`
|
|
482
|
+
));
|
|
483
|
+
const rowLayoutSignature = JSON.stringify(rowLayoutKeys);
|
|
484
|
+
const newRowTexts = rowTexts.filter((text) => !rowsByText.has(text));
|
|
485
|
+
const scrollEffectObserved = Boolean(
|
|
486
|
+
scrollsCompleted > 0
|
|
487
|
+
&& rowLayoutSignature !== previousRowLayoutSignature
|
|
488
|
+
);
|
|
489
|
+
if (scrollEffectObserved) effectiveScrollCount += 1;
|
|
490
|
+
if (
|
|
491
|
+
scrollsCompleted > 0
|
|
492
|
+
&& rowSignature === previousRowSignature
|
|
493
|
+
&& newRowTexts.length === 0
|
|
494
|
+
) {
|
|
495
|
+
stableSignatureCount += 1;
|
|
496
|
+
} else {
|
|
497
|
+
stableSignatureCount = 0;
|
|
498
|
+
}
|
|
499
|
+
const positionEvidence = {
|
|
500
|
+
position_index: positionIndex,
|
|
501
|
+
sampled_after_scroll_count: scrollsCompleted,
|
|
502
|
+
row_count: positionRows.length,
|
|
503
|
+
unreadable_row_count: positionRead.unreadable_rows.length,
|
|
504
|
+
row_backend_node_ids: positionRows.map((row) => row.backend_node_id),
|
|
505
|
+
row_texts: rowTexts,
|
|
506
|
+
row_identity_keys: rowIdentityKeys,
|
|
507
|
+
row_signature: rowSignature,
|
|
508
|
+
ordered_row_layout: orderedRows,
|
|
509
|
+
ordered_row_layout_keys: rowLayoutKeys,
|
|
510
|
+
row_layout_signature: rowLayoutSignature,
|
|
511
|
+
scroll_effect_observed: scrollEffectObserved,
|
|
512
|
+
cumulative_effective_scroll_count: effectiveScrollCount,
|
|
513
|
+
new_row_count: newRowTexts.length,
|
|
514
|
+
new_row_texts: newRowTexts,
|
|
515
|
+
stable_signature_count: stableSignatureCount,
|
|
516
|
+
binding_before_verified: bindingBefore.verified,
|
|
517
|
+
binding_after_verified: bindingAfter.verified
|
|
518
|
+
};
|
|
519
|
+
positions.push(positionEvidence);
|
|
520
|
+
if (!bindingAfter.verified) {
|
|
521
|
+
return {
|
|
522
|
+
completed: false,
|
|
523
|
+
coverage_verified: false,
|
|
524
|
+
reason: "colleague_binding_lost",
|
|
525
|
+
scrolls_requested: boundedMaxScrolls,
|
|
526
|
+
scrolls_completed: scrollsCompleted,
|
|
527
|
+
position_count: positions.length,
|
|
528
|
+
positions,
|
|
529
|
+
rows: [],
|
|
530
|
+
failed_position: positionIndex,
|
|
531
|
+
failed_binding_phase: "after_rows",
|
|
532
|
+
binding: bindingAfter
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
if (positionRead.unreadable_rows.length) {
|
|
536
|
+
return {
|
|
537
|
+
completed: false,
|
|
538
|
+
coverage_verified: false,
|
|
539
|
+
reason: "colleague_row_evidence_unavailable",
|
|
540
|
+
scrolls_requested: boundedMaxScrolls,
|
|
541
|
+
scrolls_completed: scrollsCompleted,
|
|
542
|
+
position_count: positions.length,
|
|
543
|
+
positions,
|
|
544
|
+
rows: [],
|
|
545
|
+
failed_position: positionIndex,
|
|
546
|
+
unreadable_rows: positionRead.unreadable_rows
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
addRowsFromPosition(rowsByText, positionRows, positionIndex);
|
|
550
|
+
previousRowSignature = rowSignature;
|
|
551
|
+
previousRowLayoutSignature = rowLayoutSignature;
|
|
552
|
+
|
|
553
|
+
if (
|
|
554
|
+
enabled
|
|
555
|
+
&& stableSignatureCount >= stableEndSamplesRequired
|
|
556
|
+
&& effectiveScrollCount > 0
|
|
557
|
+
) {
|
|
558
|
+
endProof = {
|
|
559
|
+
verified: true,
|
|
560
|
+
method: "effective_scroll_then_repeated_identical_rows",
|
|
561
|
+
stable_samples_required: stableEndSamplesRequired,
|
|
562
|
+
stable_samples_observed: stableSignatureCount,
|
|
563
|
+
effective_scroll_observed: true,
|
|
564
|
+
effective_scroll_count: effectiveScrollCount,
|
|
565
|
+
end_position_index: positionIndex,
|
|
566
|
+
end_scroll_count: scrollsCompleted,
|
|
567
|
+
row_signature: rowSignature,
|
|
568
|
+
additional_wheel_attempts_without_change: stableSignatureCount
|
|
569
|
+
};
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (positionIndex === boundedMaxScrolls) break;
|
|
574
|
+
|
|
575
|
+
const freshTarget = await readVisibleNodeEvidence(client, scrollTarget.node_id);
|
|
576
|
+
if (!freshTarget || freshTarget.backend_node_id !== scrollTarget.backend_node_id) {
|
|
577
|
+
return {
|
|
578
|
+
completed: false,
|
|
579
|
+
coverage_verified: false,
|
|
580
|
+
reason: "scroll_target_binding_lost",
|
|
581
|
+
scrolls_requested: boundedMaxScrolls,
|
|
582
|
+
scrolls_completed: scrollsCompleted,
|
|
583
|
+
position_count: positions.length,
|
|
584
|
+
positions,
|
|
585
|
+
rows: [],
|
|
586
|
+
failed_position: positionIndex,
|
|
587
|
+
target_selector: scrollTarget.selector
|
|
588
|
+
};
|
|
589
|
+
}
|
|
216
590
|
try {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
591
|
+
await client.Input.dispatchMouseEvent({
|
|
592
|
+
type: "mouseWheel",
|
|
593
|
+
x: freshTarget.box.x + (freshTarget.box.width / 2),
|
|
594
|
+
y: freshTarget.box.y + (freshTarget.box.height / 2),
|
|
595
|
+
deltaY: stepDeltaY,
|
|
596
|
+
deltaX: 0
|
|
597
|
+
});
|
|
598
|
+
} catch (error) {
|
|
599
|
+
return {
|
|
600
|
+
completed: false,
|
|
601
|
+
coverage_verified: false,
|
|
602
|
+
reason: "scroll_dispatch_failed",
|
|
603
|
+
error: error?.message || String(error),
|
|
604
|
+
scrolls_requested: boundedMaxScrolls,
|
|
605
|
+
scrolls_completed: scrollsCompleted,
|
|
606
|
+
position_count: positions.length,
|
|
607
|
+
positions,
|
|
608
|
+
rows: [],
|
|
609
|
+
failed_position: positionIndex,
|
|
610
|
+
target_selector: scrollTarget.selector
|
|
611
|
+
};
|
|
220
612
|
}
|
|
613
|
+
scrollsCompleted += 1;
|
|
614
|
+
await sleep(Math.max(0, Number(settleMs) || 0));
|
|
221
615
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
616
|
+
|
|
617
|
+
const allPositionEvidenceVerified = positions.every((position) => (
|
|
618
|
+
position.binding_before_verified === true
|
|
619
|
+
&& position.binding_after_verified === true
|
|
620
|
+
));
|
|
621
|
+
const completed = enabled
|
|
622
|
+
? Boolean(endProof?.verified && allPositionEvidenceVerified)
|
|
623
|
+
: Boolean(positions.length === 1 && scrollsCompleted === 0 && allPositionEvidenceVerified);
|
|
624
|
+
const coverageVerified = Boolean(
|
|
625
|
+
enabled
|
|
626
|
+
&& completed
|
|
627
|
+
&& endProof?.verified === true
|
|
628
|
+
&& positions.every((position) => position.row_count > 0)
|
|
629
|
+
);
|
|
630
|
+
const capReachedWithoutEnd = Boolean(
|
|
631
|
+
enabled
|
|
632
|
+
&& scrollsCompleted >= boundedMaxScrolls
|
|
633
|
+
&& endProof?.verified !== true
|
|
634
|
+
);
|
|
234
635
|
return {
|
|
235
|
-
|
|
236
|
-
|
|
636
|
+
completed,
|
|
637
|
+
coverage_verified: coverageVerified,
|
|
638
|
+
reason: completed
|
|
639
|
+
? !enabled
|
|
640
|
+
? "scroll_scan_disabled"
|
|
641
|
+
: coverageVerified
|
|
642
|
+
? null
|
|
643
|
+
: "scroll_position_rows_missing"
|
|
644
|
+
: capReachedWithoutEnd
|
|
645
|
+
? "scroll_end_not_verified_before_cap"
|
|
646
|
+
: "scroll_scan_incomplete",
|
|
647
|
+
scrolls_requested: boundedMaxScrolls,
|
|
648
|
+
scrolls_completed: scrollsCompleted,
|
|
649
|
+
position_count: positions.length,
|
|
650
|
+
positions,
|
|
651
|
+
target_selector: scrollTarget?.selector || null,
|
|
652
|
+
target_node_id: scrollTarget?.node_id || null,
|
|
653
|
+
target_backend_node_id: scrollTarget?.backend_node_id || null,
|
|
654
|
+
step_delta_y: stepDeltaY,
|
|
655
|
+
overlap_ratio: 0.35,
|
|
656
|
+
effective_scroll_count: effectiveScrollCount,
|
|
657
|
+
end_proof: endProof || {
|
|
658
|
+
verified: false,
|
|
659
|
+
method: "effective_scroll_then_repeated_identical_rows",
|
|
660
|
+
stable_samples_required: stableEndSamplesRequired,
|
|
661
|
+
stable_samples_observed: stableSignatureCount,
|
|
662
|
+
effective_scroll_observed: effectiveScrollCount > 0,
|
|
663
|
+
effective_scroll_count: effectiveScrollCount,
|
|
664
|
+
end_position_index: null,
|
|
665
|
+
end_scroll_count: null,
|
|
666
|
+
row_signature: null,
|
|
667
|
+
additional_wheel_attempts_without_change: stableSignatureCount
|
|
668
|
+
},
|
|
669
|
+
cap_reached_without_end: capReachedWithoutEnd,
|
|
670
|
+
coverage_gap_positions: positions
|
|
671
|
+
.filter((position) => position.row_count <= 0)
|
|
672
|
+
.map((position) => position.position_index),
|
|
673
|
+
rows: Array.from(rowsByText.values())
|
|
237
674
|
};
|
|
238
675
|
}
|
|
239
676
|
|
|
240
|
-
async function
|
|
677
|
+
async function resolveColleagueDetailScopes(client, detailState) {
|
|
678
|
+
const scopes = [];
|
|
679
|
+
if (Number.isInteger(detailState?.popup?.node_id) && detailState.popup.node_id > 0) {
|
|
680
|
+
const popupEvidence = await readVisibleNodeEvidence(client, detailState.popup.node_id);
|
|
681
|
+
if (popupEvidence) {
|
|
682
|
+
scopes.push({
|
|
683
|
+
name: "popup",
|
|
684
|
+
nodeId: detailState.popup.node_id,
|
|
685
|
+
backendNodeId: popupEvidence.backend_node_id
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (Number.isInteger(detailState?.resumeIframe?.node_id) && detailState.resumeIframe.node_id > 0) {
|
|
690
|
+
try {
|
|
691
|
+
const documentNodeId = await getFrameDocumentNodeId(client, detailState.resumeIframe.node_id);
|
|
692
|
+
scopes.push({
|
|
693
|
+
name: "resume_iframe",
|
|
694
|
+
nodeId: documentNodeId,
|
|
695
|
+
backendNodeId: null
|
|
696
|
+
});
|
|
697
|
+
} catch {
|
|
698
|
+
// A stale or inaccessible iframe cannot be used as colleague-contact evidence.
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return scopes;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
async function waitForColleagueSections(client, scopes, {
|
|
241
705
|
timeoutMs = 1000,
|
|
242
706
|
intervalMs = 150
|
|
243
707
|
} = {}) {
|
|
244
708
|
const started = Date.now();
|
|
245
709
|
let sections = [];
|
|
710
|
+
let pollCount = 0;
|
|
711
|
+
let stableScopeCount = 0;
|
|
712
|
+
let scopeBindingLost = false;
|
|
246
713
|
do {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
714
|
+
pollCount += 1;
|
|
715
|
+
stableScopeCount = 0;
|
|
716
|
+
for (const scope of scopes) {
|
|
717
|
+
if (!Number.isInteger(scope?.backendNodeId) || scope.backendNodeId <= 0) continue;
|
|
718
|
+
const evidence = await readVisibleNodeEvidence(client, scope.nodeId);
|
|
719
|
+
if (!evidence || evidence.backend_node_id !== scope.backendNodeId) {
|
|
720
|
+
scopeBindingLost = true;
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
stableScopeCount += 1;
|
|
724
|
+
}
|
|
725
|
+
if (scopeBindingLost) break;
|
|
726
|
+
const matches = await queryAcrossRoots(client, scopes, COLLEAGUE_SECTION_SELECTOR);
|
|
727
|
+
sections = [];
|
|
728
|
+
for (const match of matches) {
|
|
729
|
+
const evidence = await readVisibleNodeEvidence(client, match.node_id);
|
|
730
|
+
if (!evidence) continue;
|
|
731
|
+
sections.push({
|
|
732
|
+
...match,
|
|
733
|
+
backend_node_id: evidence.backend_node_id,
|
|
734
|
+
visible: true,
|
|
735
|
+
box: evidence.box
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
if (sections.length) {
|
|
739
|
+
return {
|
|
740
|
+
sections,
|
|
741
|
+
absence_probe: {
|
|
742
|
+
verified: false,
|
|
743
|
+
selector: COLLEAGUE_SECTION_SELECTOR,
|
|
744
|
+
scope_count: scopes.length,
|
|
745
|
+
stable_scope_count: stableScopeCount,
|
|
746
|
+
poll_count: pollCount,
|
|
747
|
+
elapsed_ms: Date.now() - started,
|
|
748
|
+
timeout_ms: Math.max(0, Number(timeoutMs) || 0),
|
|
749
|
+
full_window_elapsed: false,
|
|
750
|
+
query_error_count: 0,
|
|
751
|
+
scope_binding_lost: false,
|
|
752
|
+
scope_backend_node_ids: scopes
|
|
753
|
+
.map((scope) => Number(scope?.backendNodeId))
|
|
754
|
+
.filter((backendNodeId) => Number.isInteger(backendNodeId) && backendNodeId > 0)
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
const elapsedMs = Date.now() - started;
|
|
759
|
+
const normalizedTimeoutMs = Math.max(0, Number(timeoutMs) || 0);
|
|
760
|
+
if (
|
|
761
|
+
elapsedMs >= normalizedTimeoutMs
|
|
762
|
+
&& (normalizedTimeoutMs === 0 || pollCount >= 2)
|
|
763
|
+
) break;
|
|
764
|
+
await sleep(Math.max(1, Number(intervalMs) || 0));
|
|
765
|
+
} while (true);
|
|
766
|
+
const elapsedMs = Date.now() - started;
|
|
767
|
+
const normalizedTimeoutMs = Math.max(0, Number(timeoutMs) || 0);
|
|
768
|
+
const stableBackendNodeIds = scopes
|
|
769
|
+
.map((scope) => Number(scope?.backendNodeId))
|
|
770
|
+
.filter((backendNodeId) => Number.isInteger(backendNodeId) && backendNodeId > 0);
|
|
771
|
+
return {
|
|
772
|
+
sections,
|
|
773
|
+
absence_probe: {
|
|
774
|
+
verified: Boolean(
|
|
775
|
+
!scopeBindingLost
|
|
776
|
+
&& scopes.length > 0
|
|
777
|
+
&& stableScopeCount > 0
|
|
778
|
+
&& stableBackendNodeIds.length === stableScopeCount
|
|
779
|
+
&& pollCount >= 2
|
|
780
|
+
&& elapsedMs >= normalizedTimeoutMs
|
|
781
|
+
),
|
|
782
|
+
selector: COLLEAGUE_SECTION_SELECTOR,
|
|
783
|
+
scope_count: scopes.length,
|
|
784
|
+
stable_scope_count: stableScopeCount,
|
|
785
|
+
poll_count: pollCount,
|
|
786
|
+
elapsed_ms: elapsedMs,
|
|
787
|
+
timeout_ms: normalizedTimeoutMs,
|
|
788
|
+
full_window_elapsed: elapsedMs >= normalizedTimeoutMs,
|
|
789
|
+
query_error_count: 0,
|
|
790
|
+
scope_binding_lost: scopeBindingLost,
|
|
791
|
+
scope_backend_node_ids: stableBackendNodeIds
|
|
792
|
+
}
|
|
793
|
+
};
|
|
253
794
|
}
|
|
254
795
|
|
|
255
796
|
export async function inspectRecentColleagueContact(client, detailState, {
|
|
256
797
|
referenceDate = new Date(),
|
|
257
798
|
windowDays = 14,
|
|
258
799
|
scroll = true,
|
|
800
|
+
scrollMaxSteps = 24,
|
|
801
|
+
scrollSettleMs = 350,
|
|
259
802
|
sectionWaitMs = 1000,
|
|
260
803
|
sectionPollMs = 150
|
|
261
804
|
} = {}) {
|
|
262
|
-
const
|
|
263
|
-
|
|
805
|
+
const scopes = await resolveColleagueDetailScopes(client, detailState);
|
|
806
|
+
if (!scopes.length) {
|
|
807
|
+
return {
|
|
808
|
+
checked: false,
|
|
809
|
+
panel_found: false,
|
|
810
|
+
recent: null,
|
|
811
|
+
indeterminate: true,
|
|
812
|
+
reason: "detail_scope_unavailable",
|
|
813
|
+
window_days: windowDays,
|
|
814
|
+
rows: []
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
const sectionWait = await waitForColleagueSections(client, scopes, {
|
|
264
818
|
timeoutMs: sectionWaitMs,
|
|
265
819
|
intervalMs: sectionPollMs
|
|
266
820
|
});
|
|
821
|
+
const sections = sectionWait.sections;
|
|
267
822
|
if (!sections.length) {
|
|
823
|
+
const absenceVerified = sectionWait.absence_probe?.verified === true;
|
|
268
824
|
return {
|
|
269
|
-
checked:
|
|
825
|
+
checked: absenceVerified,
|
|
270
826
|
panel_found: false,
|
|
271
|
-
recent: false,
|
|
827
|
+
recent: absenceVerified ? false : null,
|
|
828
|
+
indeterminate: !absenceVerified,
|
|
272
829
|
reason: "panel_missing",
|
|
273
830
|
window_days: windowDays,
|
|
831
|
+
absence_probe: sectionWait.absence_probe,
|
|
832
|
+
rows: []
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
if (sections.length !== 1) {
|
|
836
|
+
return {
|
|
837
|
+
checked: false,
|
|
838
|
+
panel_found: true,
|
|
839
|
+
recent: null,
|
|
840
|
+
indeterminate: true,
|
|
841
|
+
reason: "panel_ambiguous",
|
|
842
|
+
window_days: windowDays,
|
|
843
|
+
visible_section_count: sections.length,
|
|
274
844
|
rows: []
|
|
275
845
|
};
|
|
276
846
|
}
|
|
277
847
|
|
|
278
848
|
const section = sections[0];
|
|
279
|
-
const
|
|
849
|
+
const tabHeaderIds = await querySelectorAll(client, section.node_id, ".tab-hd");
|
|
850
|
+
let visibleTabHeaderCount = 0;
|
|
851
|
+
for (const nodeId of tabHeaderIds) {
|
|
852
|
+
if (await readVisibleNodeEvidence(client, nodeId)) visibleTabHeaderCount += 1;
|
|
853
|
+
}
|
|
280
854
|
const tab = await ensureColleagueTabSelected(client, section.node_id);
|
|
281
855
|
if (!tab.selected) {
|
|
282
856
|
return {
|
|
283
|
-
checked:
|
|
857
|
+
checked: false,
|
|
284
858
|
panel_found: true,
|
|
285
|
-
recent:
|
|
859
|
+
recent: null,
|
|
860
|
+
indeterminate: true,
|
|
286
861
|
reason: "colleague_tab_unavailable",
|
|
287
862
|
window_days: windowDays,
|
|
288
863
|
section_root: section.root,
|
|
289
|
-
|
|
864
|
+
section_node_id: section.node_id,
|
|
865
|
+
section_backend_node_id: section.backend_node_id,
|
|
866
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
290
867
|
selected_tab_text: tab.selected_text,
|
|
868
|
+
selected_tab_count: tab.selected_tab_count,
|
|
869
|
+
pane_binding_verified: false,
|
|
291
870
|
rows: []
|
|
292
871
|
};
|
|
293
872
|
}
|
|
294
873
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
874
|
+
const scrollScan = await scanContactRowsAcrossScrollPositions(
|
|
875
|
+
client,
|
|
876
|
+
scopes,
|
|
877
|
+
section,
|
|
878
|
+
tab,
|
|
879
|
+
{
|
|
880
|
+
enabled: scroll,
|
|
881
|
+
maxScrolls: scrollMaxSteps,
|
|
882
|
+
settleMs: scrollSettleMs
|
|
303
883
|
}
|
|
304
|
-
|
|
884
|
+
);
|
|
885
|
+
const {
|
|
886
|
+
rows = [],
|
|
887
|
+
...scroll_probe
|
|
888
|
+
} = scrollScan;
|
|
889
|
+
|
|
890
|
+
const sectionAfter = await readVisibleNodeEvidence(client, section.node_id);
|
|
891
|
+
const selectedAfter = await readSelectedColleagueTab(client, section.node_id);
|
|
892
|
+
const binding = {
|
|
893
|
+
verified: Boolean(
|
|
894
|
+
sectionAfter
|
|
895
|
+
&& sectionAfter.backend_node_id === section.backend_node_id
|
|
896
|
+
&& tab.selected === true
|
|
897
|
+
&& selectedAfter.selected === true
|
|
898
|
+
&& selectedAfter.backend_node_id === tab.backend_node_id
|
|
899
|
+
&& selectedAfter.selected_tab_count === 1
|
|
900
|
+
),
|
|
901
|
+
detail_root_node_id: scopes[0]?.nodeId || null,
|
|
902
|
+
section_node_id: section.node_id,
|
|
903
|
+
section_backend_node_id: section.backend_node_id,
|
|
904
|
+
section_visible: Boolean(sectionAfter),
|
|
905
|
+
selected_tab_node_id: tab.node_id || null,
|
|
906
|
+
selected_tab_backend_node_id: tab.backend_node_id || null,
|
|
907
|
+
selected_tab_text: tab.selected_text,
|
|
908
|
+
selected_tab_visible: tab.selected === true,
|
|
909
|
+
selected_tab_count: tab.selected_tab_count,
|
|
910
|
+
selection_reverified_after_rows: selectedAfter.selected === true,
|
|
911
|
+
selected_tab_backend_node_id_after_rows: selectedAfter.backend_node_id || null,
|
|
912
|
+
row_scope: "selected_section_descendants"
|
|
913
|
+
};
|
|
914
|
+
if (!binding.verified) {
|
|
915
|
+
return {
|
|
916
|
+
checked: false,
|
|
917
|
+
panel_found: true,
|
|
918
|
+
recent: null,
|
|
919
|
+
indeterminate: true,
|
|
920
|
+
reason: "colleague_binding_lost",
|
|
921
|
+
window_days: windowDays,
|
|
922
|
+
section_root: section.root,
|
|
923
|
+
section_node_id: section.node_id,
|
|
924
|
+
section_backend_node_id: section.backend_node_id,
|
|
925
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
926
|
+
selected_tab_text: selectedAfter.selected_text || tab.selected_text,
|
|
927
|
+
selected_tab_count: selectedAfter.selected_tab_count,
|
|
928
|
+
pane_binding_verified: false,
|
|
929
|
+
binding,
|
|
930
|
+
rows: [],
|
|
931
|
+
scroll_probe
|
|
932
|
+
};
|
|
305
933
|
}
|
|
306
934
|
|
|
307
935
|
const parsedRows = rows.map((row) => {
|
|
@@ -315,17 +943,135 @@ export async function inspectRecentColleagueContact(client, detailState, {
|
|
|
315
943
|
};
|
|
316
944
|
});
|
|
317
945
|
const matched = parsedRows.find((row) => row.within_window) || null;
|
|
946
|
+
if (matched) {
|
|
947
|
+
return {
|
|
948
|
+
checked: true,
|
|
949
|
+
panel_found: true,
|
|
950
|
+
recent: true,
|
|
951
|
+
indeterminate: false,
|
|
952
|
+
reason: "recent_colleague_contact_found",
|
|
953
|
+
window_days: windowDays,
|
|
954
|
+
section_root: section.root,
|
|
955
|
+
section_node_id: section.node_id,
|
|
956
|
+
section_backend_node_id: section.backend_node_id,
|
|
957
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
958
|
+
selected_tab_text: tab.selected_text,
|
|
959
|
+
selected_tab_count: tab.selected_tab_count,
|
|
960
|
+
pane_binding_verified: true,
|
|
961
|
+
binding,
|
|
962
|
+
tab_changed: tab.changed,
|
|
963
|
+
matched_row: matched,
|
|
964
|
+
row_count: parsedRows.length,
|
|
965
|
+
rows: parsedRows,
|
|
966
|
+
scroll_probe
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
if (!scroll_probe.completed) {
|
|
970
|
+
return {
|
|
971
|
+
checked: false,
|
|
972
|
+
panel_found: true,
|
|
973
|
+
recent: null,
|
|
974
|
+
indeterminate: true,
|
|
975
|
+
reason: scroll_probe.reason || "colleague_scroll_scan_incomplete",
|
|
976
|
+
window_days: windowDays,
|
|
977
|
+
section_root: section.root,
|
|
978
|
+
section_node_id: section.node_id,
|
|
979
|
+
section_backend_node_id: section.backend_node_id,
|
|
980
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
981
|
+
selected_tab_text: tab.selected_text,
|
|
982
|
+
selected_tab_count: tab.selected_tab_count,
|
|
983
|
+
pane_binding_verified: true,
|
|
984
|
+
binding,
|
|
985
|
+
rows: parsedRows,
|
|
986
|
+
scroll_probe
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (!parsedRows.length) {
|
|
990
|
+
return {
|
|
991
|
+
checked: false,
|
|
992
|
+
panel_found: true,
|
|
993
|
+
recent: null,
|
|
994
|
+
indeterminate: true,
|
|
995
|
+
reason: "contact_rows_missing",
|
|
996
|
+
window_days: windowDays,
|
|
997
|
+
section_root: section.root,
|
|
998
|
+
section_node_id: section.node_id,
|
|
999
|
+
section_backend_node_id: section.backend_node_id,
|
|
1000
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
1001
|
+
selected_tab_text: tab.selected_text,
|
|
1002
|
+
selected_tab_count: tab.selected_tab_count,
|
|
1003
|
+
pane_binding_verified: true,
|
|
1004
|
+
binding,
|
|
1005
|
+
tab_changed: tab.changed,
|
|
1006
|
+
row_count: 0,
|
|
1007
|
+
rows: [],
|
|
1008
|
+
scroll_probe
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
const unparsedRows = parsedRows.filter((row) => !row.parsed_date);
|
|
1012
|
+
if (unparsedRows.length) {
|
|
1013
|
+
return {
|
|
1014
|
+
checked: false,
|
|
1015
|
+
panel_found: true,
|
|
1016
|
+
recent: null,
|
|
1017
|
+
indeterminate: true,
|
|
1018
|
+
reason: "contact_date_unparseable",
|
|
1019
|
+
window_days: windowDays,
|
|
1020
|
+
section_root: section.root,
|
|
1021
|
+
section_node_id: section.node_id,
|
|
1022
|
+
section_backend_node_id: section.backend_node_id,
|
|
1023
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
1024
|
+
selected_tab_text: tab.selected_text,
|
|
1025
|
+
selected_tab_count: tab.selected_tab_count,
|
|
1026
|
+
pane_binding_verified: true,
|
|
1027
|
+
binding,
|
|
1028
|
+
tab_changed: tab.changed,
|
|
1029
|
+
row_count: parsedRows.length,
|
|
1030
|
+
unparsed_row_count: unparsedRows.length,
|
|
1031
|
+
unparsed_rows: unparsedRows,
|
|
1032
|
+
rows: parsedRows,
|
|
1033
|
+
scroll_probe
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
if (!scroll_probe.coverage_verified) {
|
|
1037
|
+
return {
|
|
1038
|
+
checked: false,
|
|
1039
|
+
panel_found: true,
|
|
1040
|
+
recent: null,
|
|
1041
|
+
indeterminate: true,
|
|
1042
|
+
reason: scroll_probe.reason || "colleague_scroll_scan_incomplete",
|
|
1043
|
+
window_days: windowDays,
|
|
1044
|
+
section_root: section.root,
|
|
1045
|
+
section_node_id: section.node_id,
|
|
1046
|
+
section_backend_node_id: section.backend_node_id,
|
|
1047
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
1048
|
+
selected_tab_text: tab.selected_text,
|
|
1049
|
+
selected_tab_count: tab.selected_tab_count,
|
|
1050
|
+
pane_binding_verified: true,
|
|
1051
|
+
binding,
|
|
1052
|
+
tab_changed: tab.changed,
|
|
1053
|
+
row_count: parsedRows.length,
|
|
1054
|
+
rows: parsedRows,
|
|
1055
|
+
scroll_probe
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
318
1058
|
return {
|
|
319
1059
|
checked: true,
|
|
320
1060
|
panel_found: true,
|
|
321
|
-
recent:
|
|
322
|
-
|
|
1061
|
+
recent: false,
|
|
1062
|
+
indeterminate: false,
|
|
1063
|
+
reason: "no_recent_colleague_contact",
|
|
323
1064
|
window_days: windowDays,
|
|
324
1065
|
section_root: section.root,
|
|
325
|
-
|
|
1066
|
+
section_node_id: section.node_id,
|
|
1067
|
+
section_backend_node_id: section.backend_node_id,
|
|
1068
|
+
tab_header_found: visibleTabHeaderCount === 1,
|
|
326
1069
|
selected_tab_text: tab.selected_text,
|
|
1070
|
+
selected_tab_count: tab.selected_tab_count,
|
|
1071
|
+
pane_binding_verified: true,
|
|
1072
|
+
binding,
|
|
327
1073
|
tab_changed: tab.changed,
|
|
328
|
-
matched_row:
|
|
1074
|
+
matched_row: null,
|
|
329
1075
|
row_count: parsedRows.length,
|
|
330
1076
|
rows: parsedRows,
|
|
331
1077
|
scroll_probe
|