update agents skill design
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
|
||||
import { isUnlocked } from '@/lib/provingGround';
|
||||
import { getScoreBand } from '@/lib/talentHome';
|
||||
import { PRIVILEGED_EVENTS, activitySignals } from '@/lib/activitySignals';
|
||||
|
||||
export const pct = (n, d) => (d ? Math.round((n / d) * 100) : 0);
|
||||
|
||||
@@ -26,10 +27,12 @@ const DAY_MS = 1000 * 60 * 60 * 24;
|
||||
|
||||
/**
|
||||
* Events an auditor looks at first: they change who is employed or what is being
|
||||
* hired for. Kept here so the fact sheet, the Activity page's severity column and
|
||||
* the assistant's security answers all agree on what "privileged" means.
|
||||
* hired for. Defined in `lib/activitySignals.js` alongside the detection that
|
||||
* uses it, and re-exported here because the fact sheet, the Activity page's
|
||||
* severity column and the assistant's security answers all import it from this
|
||||
* module and must keep agreeing on what "privileged" means.
|
||||
*/
|
||||
export const PRIVILEGED_EVENTS = ['hire_candidate', 'create_position'];
|
||||
export { PRIVILEGED_EVENTS };
|
||||
|
||||
/** `today` is injected rather than read from the clock so answers are stable. */
|
||||
export function buildFacts({
|
||||
@@ -119,64 +122,15 @@ export function buildFacts({
|
||||
/**
|
||||
* Out-of-pattern activity.
|
||||
*
|
||||
* Detected here rather than in the assistant so the greeting, the answer and
|
||||
* any future page section all count the same things. "Two unusual patterns"
|
||||
* has to mean the same two everywhere it is said.
|
||||
* Computed by `lib/activitySignals.js` rather than here, because it now has
|
||||
* a second reader: the `activity.signals` data source resolves it for a card
|
||||
* or an answer, and `dataResolver.js` cannot import from this directory
|
||||
* without a library reaching up into the component tree.
|
||||
*
|
||||
* Each signal is a deviation from this platform's own baseline, not a verdict:
|
||||
* on a live deployment most of them resolve to an integration or a busy
|
||||
* afternoon, and the copy that renders them says so.
|
||||
* Moved, not changed. "Two unusual patterns" has to mean the same two
|
||||
* everywhere it is said, and one function is the only way to guarantee that.
|
||||
*/
|
||||
const activitySignals = (() => {
|
||||
const privileged = activity.filter((e) => PRIVILEGED_EVENTS.includes(e.event_type));
|
||||
|
||||
const perUser = activity.reduce((acc, e) => {
|
||||
acc[e.user_email] ||= { email: e.user_email, name: e.user_name, count: 0, privileged: 0 };
|
||||
acc[e.user_email].count += 1;
|
||||
if (PRIVILEGED_EVENTS.includes(e.event_type)) acc[e.user_email].privileged += 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const accounts = Object.values(perUser).sort((a, b) => b.count - a.count);
|
||||
const busiest = accounts[0] || null;
|
||||
const busiestShare = busiest ? pct(busiest.count, activity.length) : 0;
|
||||
|
||||
/* A burst is more than three actions from one account inside one hour. */
|
||||
const perAccountHour = activity.reduce((acc, e) => {
|
||||
const key = `${e.user_email}|${String(e.created_date).slice(0, 13)}`;
|
||||
acc[key] = (acc[key] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
const bursts = Object.values(perAccountHour).filter((n) => n > 3).length;
|
||||
|
||||
const offHours = activity.filter((e) => {
|
||||
const hour = new Date(e.created_date).getHours();
|
||||
return hour < 6 || hour >= 22;
|
||||
});
|
||||
|
||||
const privilegedShare = pct(privileged.length, activity.length);
|
||||
|
||||
/* The flags, in the order they are worth reading. A count of these is what
|
||||
the greeting reports, so anything added here changes that number. */
|
||||
const flags = [
|
||||
activity.length > 0 && busiestShare >= 50 && 'concentration',
|
||||
bursts > 0 && 'burst',
|
||||
offHours.length > 0 && 'off-hours',
|
||||
activity.length > 0 && since(1).length === 0 && 'silent',
|
||||
privilegedShare > 30 && 'privileged-share',
|
||||
].filter(Boolean);
|
||||
|
||||
return {
|
||||
accounts,
|
||||
busiest,
|
||||
busiestShare,
|
||||
bursts,
|
||||
offHours,
|
||||
privileged,
|
||||
privilegedShare,
|
||||
flags,
|
||||
};
|
||||
})();
|
||||
const signals = activitySignals(activity, today);
|
||||
|
||||
/* ── KROW Forge ──────────────────────────────────────────────────────────
|
||||
Derived with the same gate (`isUnlocked`) and the same completion record the
|
||||
@@ -386,7 +340,7 @@ export function buildFacts({
|
||||
activity7d: since(7),
|
||||
activity24h: since(1),
|
||||
eventCounts,
|
||||
activitySignals,
|
||||
activitySignals: signals,
|
||||
|
||||
/* Signed-in worker, and the pages built on these records */
|
||||
profile,
|
||||
|
||||
Reference in New Issue
Block a user