update agents skill design

This commit is contained in:
2026-08-20 18:18:10 +05:30
parent 161b237695
commit b2e6868824
75 changed files with 14586 additions and 98 deletions

View File

@@ -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,