Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Luke The Dev
2026-03-23 11:44:25 -05:00
committed by GitHub
parent c2cbdeec44
commit 5e7812c352
30 changed files with 2213 additions and 128 deletions
@@ -0,0 +1,132 @@
"use client";
import { useEffect, useMemo, useRef, useState } from "react";
import type { AgentState } from "@/features/agents/state/store";
import { readGatewayAgentSkillsAllowlist } from "@/lib/gateway/agentConfig";
import type { GatewayClient, GatewayStatus } from "@/lib/gateway/GatewayClient";
import type { OfficeSkillTriggerMovementTarget } from "@/lib/office/places";
import {
buildAgentSkillsAllowlistSet,
deriveAgentSkillsAccessMode,
deriveSkillReadinessState,
} from "@/lib/skills/presentation";
import {
listPackagedSkillTriggerDefinitions,
resolveTriggeredSkillDefinition,
type SkillTriggerDefinition,
} from "@/lib/skills/triggers";
import { loadAgentSkillStatus } from "@/lib/skills/types";
const isSkillEnabledForAgent = (params: {
allowlist: string[] | undefined;
skillName: string;
}): boolean => {
const accessMode = deriveAgentSkillsAccessMode(params.allowlist);
if (accessMode === "all") {
return true;
}
if (accessMode === "none") {
return false;
}
return buildAgentSkillsAllowlistSet(params.allowlist).has(params.skillName.trim());
};
export const useOfficeSkillTriggers = ({
client,
status,
agents,
}: {
client: GatewayClient;
status: GatewayStatus;
agents: AgentState[];
}) => {
const requestIdRef = useRef(0);
const [enabledTriggersByAgentId, setEnabledTriggersByAgentId] = useState<
Record<string, SkillTriggerDefinition[]>
>({});
const packagedTriggers = useMemo(() => listPackagedSkillTriggerDefinitions(), []);
const agentIdsKey = useMemo(
() => agents.map((agent) => agent.agentId).sort().join("|"),
[agents],
);
const stableAgentIds = useMemo(
() => (agentIdsKey ? agentIdsKey.split("|").filter((value) => value.length > 0) : []),
[agentIdsKey],
);
useEffect(() => {
if (status !== "connected" || agents.length === 0 || packagedTriggers.length === 0) {
setEnabledTriggersByAgentId({});
return;
}
let cancelled = false;
const load = async () => {
const requestId = requestIdRef.current + 1;
requestIdRef.current = requestId;
try {
const triggerBySkillKey = new Map(
packagedTriggers.map((trigger) => [trigger.skillKey, trigger]),
);
const results = await Promise.all(
stableAgentIds.map(async (agentId) => {
const [report, allowlist] = await Promise.all([
loadAgentSkillStatus(client, agentId),
readGatewayAgentSkillsAllowlist({ client, agentId }),
]);
const enabledTriggers = report.skills
.filter((skill) => deriveSkillReadinessState(skill) === "ready")
.filter((skill) => isSkillEnabledForAgent({ allowlist, skillName: skill.name }))
.map((skill) => triggerBySkillKey.get(skill.skillKey))
.filter((trigger): trigger is SkillTriggerDefinition => Boolean(trigger));
return [agentId, enabledTriggers] as const;
}),
);
if (cancelled || requestId !== requestIdRef.current) {
return;
}
setEnabledTriggersByAgentId(Object.fromEntries(results));
} catch {
if (cancelled || requestId !== requestIdRef.current) {
return;
}
setEnabledTriggersByAgentId({});
}
};
void load();
const intervalId = window.setInterval(() => {
void load();
}, 30_000);
return () => {
cancelled = true;
window.clearInterval(intervalId);
};
}, [agentIdsKey, client, packagedTriggers, stableAgentIds, status]);
const movementTargetByAgentId = useMemo<Record<string, OfficeSkillTriggerMovementTarget>>(() => {
const next: Record<string, OfficeSkillTriggerMovementTarget> = {};
for (const agent of agents) {
const trigger = resolveTriggeredSkillDefinition({
isAgentRunning: agent.status === "running" || Boolean(agent.runId),
lastUserMessage: agent.lastUserMessage,
transcriptEntries: agent.transcriptEntries,
triggers: enabledTriggersByAgentId[agent.agentId] ?? [],
});
if (trigger) {
next[agent.agentId] = trigger.movementTarget;
}
}
return next;
}, [agents, enabledTriggersByAgentId]);
return {
enabledTriggersByAgentId,
movementTargetByAgentId,
};
};