diff --git a/server/src/services/heartbeat.ts b/server/src/services/heartbeat.ts index 8313fa79..d6eb9a3e 100644 --- a/server/src/services/heartbeat.ts +++ b/server/src/services/heartbeat.ts @@ -75,7 +75,6 @@ const SESSIONED_LOCAL_ADAPTERS = new Set([ "codex_local", "cursor", "gemini_local", - "hermes_local", "opencode_local", "pi_local", ]); diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 4a83e977..d387ffce 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -9,7 +9,6 @@ import { type AgentPermissionUpdate, } from "../api/agents"; import { companySkillsApi } from "../api/companySkills"; -import { skillGroupsApi, type SkillGroupRow } from "../api/skillGroups"; import { budgetsApi } from "../api/budgets"; import { heartbeatsApi } from "../api/heartbeats"; import { instanceSettingsApi } from "../api/instanceSettings"; @@ -76,17 +75,6 @@ import { import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible"; import { TooltipProvider } from "@/components/ui/tooltip"; import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { Separator } from "@/components/ui/separator"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { GroupBadge } from "../components/GroupBadge"; import { AgentIcon, AgentIconPicker } from "../components/AgentIconPicker"; import { RunTranscriptView, type TranscriptMode } from "../components/transcript/RunTranscriptView"; import { @@ -937,7 +925,7 @@ export function AgentDetail() { {actionError &&

{actionError}

} {isPendingApproval && (

- This agent is pending owner approval and cannot be invoked yet. + This agent is pending board approval and cannot be invoked yet.

)} @@ -1088,28 +1076,10 @@ function LatestRunCard({ runs, agentId }: { runs: HeartbeatRun[]; agentId: strin const isLive = run.status === "running" || run.status === "queued"; const statusInfo = runStatusIcons[run.status] ?? { icon: Clock, color: "text-neutral-400" }; const StatusIcon = statusInfo.icon; - const summaryRaw = run.resultJson + const summary = run.resultJson ? String((run.resultJson as Record).summary ?? (run.resultJson as Record).result ?? "") : run.error ?? ""; - // Extract a clean 2-3 line excerpt: first non-empty, non-header, non-list-mark lines - const summary = useMemo(() => { - if (!summaryRaw) return ""; - const lines = summaryRaw - .replace(/^#{1,6}\s+/gm, "") - .split("\n") - .map((l) => l.trim()) - .filter((l) => l.length > 0 && !l.startsWith("---") && !l.startsWith("|") && !l.startsWith("```") && !/^[-*>]/.test(l) && !/^\d+\./.test(l)); - const excerpt: string[] = []; - let chars = 0; - for (const line of lines) { - if (excerpt.length >= 3 || chars + line.length > 280) break; - excerpt.push(line); - chars += line.length; - } - return excerpt.join(" "); - }, [summaryRaw]); - return (
@@ -1560,11 +1530,11 @@ function ConfigurationTab({ const taskAssignLocked = agent.role === "ceo" || canCreateAgents; const taskAssignHint = taskAssignSource === "ceo_role" - ? `Enabled automatically for ${VOCAB.ceo} agents.` + ? "Enabled automatically for CEO agents." : taskAssignSource === "agent_creator" ? "Enabled automatically while this agent can create new agents." : taskAssignSource === "explicit_grant" - ? `Enabled via explicit ${VOCAB.company.toLowerCase()} permission grant.` + ? "Enabled via explicit company permission grant." : "Disabled unless explicitly granted."; return ( @@ -1808,7 +1778,7 @@ function PromptsTab({ const uploadMarkdownImage = useMutation({ mutationFn: async ({ file, namespace }: { file: File; namespace: string }) => { - if (!selectedCompanyId) throw new Error(`Select a ${VOCAB.company.toLowerCase()} to upload images`); + if (!selectedCompanyId) throw new Error("Select a company to upload images"); return assetsApi.uploadImage(selectedCompanyId, file, namespace); }, }); @@ -2008,7 +1978,7 @@ function PromptsTab({ - {`Managed: ${VOCAB.appName} stores and serves the instructions bundle. External: you provide a path on disk where the instructions live.`} + Managed: Paperclip stores and serves the instructions bundle. External: you provide a path on disk where the instructions live. @@ -2063,7 +2033,7 @@ function PromptsTab({ - {`The absolute directory on disk where the instructions bundle lives. In managed mode this is set by ${VOCAB.appName} automatically.`} + The absolute directory on disk where the instructions bundle lives. In managed mode this is set by Paperclip automatically. @@ -2433,7 +2403,6 @@ function AgentSkillsTab({ const queryClient = useQueryClient(); const [skillDraft, setSkillDraft] = useState([]); const [lastSavedSkills, setLastSavedSkills] = useState([]); - const [unmanagedOpen, setUnmanagedOpen] = useState(false); const lastSavedSkillsRef = useRef([]); const hasHydratedSkillSnapshotRef = useRef(false); const skipNextSkillAutosaveRef = useRef(true); @@ -2463,63 +2432,6 @@ function AgentSkillsTab({ }, }); - // Skill groups queries - const agentGroupsQuery = useQuery({ - queryKey: queryKeys.skillGroups.agentGroups(agent.id), - queryFn: () => skillGroupsApi.listAgentGroups(agent.id), - }); - - const allGroupsQuery = useQuery({ - queryKey: queryKeys.skillGroups.list, - queryFn: () => skillGroupsApi.listGroups(), - }); - - const agentEffectiveSkillsQuery = useQuery({ - queryKey: queryKeys.skillGroups.agentSkills(agent.id), - queryFn: () => skillGroupsApi.listAgentSkills(agent.id), - }); - - // Group dialog state - const [addGroupOpen, setAddGroupOpen] = useState(false); - const [createGroupOpen, setCreateGroupOpen] = useState(false); - const [removeGroupConfirm, setRemoveGroupConfirm] = useState(null); - const [groupSearch, setGroupSearch] = useState(""); - const [newGroupName, setNewGroupName] = useState(""); - const [newGroupDesc, setNewGroupDesc] = useState(""); - const [effectiveOpen, setEffectiveOpen] = useState(false); - - // Group mutations - const assignGroupMut = useMutation({ - mutationFn: ({ groupId }: { groupId: string }) => - skillGroupsApi.assignGroup(agent.id, groupId), - onSuccess: () => { - void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentGroups(agent.id) }); - void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentSkills(agent.id) }); - setAddGroupOpen(false); - }, - }); - - const removeGroupMut = useMutation({ - mutationFn: ({ groupId }: { groupId: string }) => - skillGroupsApi.removeGroup(agent.id, groupId), - onSuccess: () => { - void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentGroups(agent.id) }); - void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentSkills(agent.id) }); - setRemoveGroupConfirm(null); - }, - }); - - const createGroupMut = useMutation({ - mutationFn: () => - skillGroupsApi.createGroup({ name: newGroupName, description: newGroupDesc || undefined }), - onSuccess: () => { - void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.list }); - setCreateGroupOpen(false); - setNewGroupName(""); - setNewGroupDesc(""); - }, - }); - useEffect(() => { setSkillDraft([]); setLastSavedSkills([]); @@ -2651,9 +2563,9 @@ function AgentSkillsTab({ const unsupportedSkillMessage = useMemo(() => { if (skillSnapshot?.mode !== "unsupported") return null; if (agent.adapterType === "openclaw_gateway") { - return `${VOCAB.appName} cannot manage OpenClaw skills here. Visit your OpenClaw instance to manage this agent's skills.`; + return "Paperclip cannot manage OpenClaw skills here. Visit your OpenClaw instance to manage this agent's skills."; } - return `${VOCAB.appName} cannot manage skills for this adapter yet. Manage them in the adapter directly.`; + return "Paperclip cannot manage skills for this adapter yet. Manage them in the adapter directly."; }, [agent.adapterType, skillSnapshot?.mode]); const hasUnsavedChanges = !arraysEqual(skillDraft, lastSavedSkills); const saveStatusLabel = syncSkills.isPending @@ -2693,289 +2605,6 @@ function AgentSkillsTab({
) : null} - {/* ---- Assigned Groups Section ---- */} - - -
-

- Assigned Groups -

- - {agentGroupsQuery.isLoading ? ( -
- - - -
- ) : agentGroupsQuery.data?.length === 0 ? ( -
-

No groups assigned

-

- Add a skill group to install a bundle of skills for this agent. -

-
- ) : ( -
- - {(agentGroupsQuery.data ?? []).map((group) => ( - setRemoveGroupConfirm(group) - } - removing={ - removeGroupMut.isPending && removeGroupConfirm?.id === group.id - } - /> - ))} - -
- )} - - - - {agentGroupsQuery.isError && ( -

Failed to load groups. Try again.

- )} -
- - - - {/* ---- Combined Effective Skills Section ---- */} -
- -
-

- Combined Effective Skills -

- - - -
- - - {agentEffectiveSkillsQuery.isLoading ? ( -
- - - -
- ) : agentEffectiveSkillsQuery.data?.length === 0 ? ( -

- No skills in assigned groups. Add skills to the group definitions first. -

- ) : ( - -
    - {(agentEffectiveSkillsQuery.data ?? []).map((entry) => ( -
  • - {entry.skillId} -
  • - ))} -
-
- )} -
-
-
- - - - {/* ---- Additional Individual Skills ---- */} -

- Additional Individual Skills -

- - {/* ---- Add Group Dialog ---- */} - - - - Add Skill Group - -
- setGroupSearch(e.target.value)} - className="h-8 text-sm" - /> -
- {(() => { - const assignedIds = new Set((agentGroupsQuery.data ?? []).map((g) => g.id)); - const available = (allGroupsQuery.data ?? []).filter( - (g) => - !assignedIds.has(g.id) && - g.name.toLowerCase().includes(groupSearch.toLowerCase()), - ); - if (available.length === 0) { - return ( -

- {groupSearch ? "No groups match your search." : "No groups available to add."} -

- ); - } - return available.map((group) => ( -
-
-

{group.name}

- {group.isBuiltin === 1 && ( -

built-in

- )} - {group.description && ( -

{group.description}

- )} -
- -
- )); - })()} -
- {assignGroupMut.isError && ( -

- Failed to assign group. Check the server logs for details. -

- )} -
- - - - -
-
- - {/* ---- Create Group Dialog ---- */} - - - - Create Skill Group - -
-
- - setNewGroupName(e.target.value)} - className="h-8 text-sm" - /> -
-
- -