candidates and board ui agent issue
Some checks failed
CI / check (push) Failing after 4m58s

This commit is contained in:
2026-09-05 10:46:06 +05:30
parent e02a0c23d4
commit 6249e00a3a
78 changed files with 15071 additions and 1998 deletions

View File

@@ -15,6 +15,11 @@ import { SECTION_COMPONENTS } from '@/components/skills/SkillSections';
from the module rather than the package index so a page section never pulls
the assistant panel in behind it. */
import { usePageAction, usePageContext } from '@/components/ai-assistant/PageContext';
/* Which agent is answering on this page. Imported from the module rather than
the package index for the same reason `PageContext` is: a page section must
not pull the assistant panel in behind it. */
import { useActiveAgent } from '@/components/ai-assistant/AgentContext';
import { agentPermitsSkill } from '@/lib/agents/runtime';
/**
* The extension point: one controlled slot a page offers to skills.
@@ -35,23 +40,71 @@ import { usePageAction, usePageContext } from '@/components/ai-assistant/PageCon
*/
/** The skills contributing sections to this page right now. */
function useSkillSections(page, placement) {
/**
* The sections definitions contribute to a page, optionally narrowed to one
* placement.
*
* Exported because the node tree needs the identical reading: a skill's section
* drawn as a child node and the same section drawn by this surface must come
* from one resolution, or the two would disagree about what a page carries.
* Called with no placement it returns every section on the page.
*/
export function useSkillSections(page, placement) {
const preferences = usePreferences();
/**
* Who is answering here.
*
* Read through the same context the panel reads, which is mounted around the
* page as well as around the panel — see `AssistantPanelContext`. Outside a
* provider this returns an inert value, so a surface drawn anywhere else sees
* no agents and no ownership, which is exactly today's behaviour.
*/
const { agent, agents } = useActiveAgent();
const customKey = JSON.stringify(preferences.customSkills || []);
const disabledKey = JSON.stringify(preferences.disabledSkills || []);
/* Stable keys, because these are fresh arrays on every render and a memo
keyed on their identity would recompute forever. */
const ownedKey = JSON.stringify((agents || []).map((a) => a.skills || []));
const mineKey = JSON.stringify(agent?.skills || []);
return useMemo(() => {
const custom = JSON.parse(customKey);
const disabled = JSON.parse(disabledKey);
/**
* Agent ownership, and why it is opt-in.
*
* A skill that an agent claims belongs to that agent: its UI is drawn where
* that agent is answering and nowhere else. A skill that **no** agent claims
* is unowned, and unowned means unchanged — page scope and the account
* switch decide it, exactly as they did before this existed.
*
* That asymmetry is the whole design. Every skill this product ships is
* conversation-only and claimed for conversation; the definitions that draw
* page UI are authored on the account and claimed by nobody. Enforcing
* ownership on all of them would have removed every skill section in the
* product on the day it shipped. Attaching a skill to an agent is therefore
* the act that brings it under an agent's control — a decision an author
* makes in Agent Configure, not one taken on their behalf here.
*
* Three separate ideas meet here and none of them is the others: what the
* *agent* owns, what pages the *skill* declares, and what the *account* has
* switched off. All three must pass.
*/
/* The rule itself lives with the other agent rules, so it can be reasoned
about and tested without rendering a page. */
const scope = { agents: JSON.parse(ownedKey).map((skills) => ({ skills })), agent: { skills: JSON.parse(mineKey) } };
return allSkills(custom)
/* Inactive means registered but not offered — the same rule the assistant
follows, so switching a skill off removes its UI too. */
.filter((skill) => skill.status === 'active' && !disabled.includes(skill.id))
.filter((skill) => agentPermitsSkill(skill.id, scope))
.flatMap((skill) => sectionsForPage(skill, page)
.filter((section) => !placement || section.placement === placement)
.map((section) => ({ skill, section })));
}, [page, placement, customKey, disabledKey]);
}, [page, placement, customKey, disabledKey, ownedKey, mineKey]);
}
/**