@iblai/iblai-js 2.7.3 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-layer/playwright/agent-skills-helpers.d.ts +8 -6
- package/dist/playwright/index.cjs +13 -8
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +8 -6
- package/dist/playwright/index.esm.js +13 -8
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/agent-skills-helpers.d.ts +8 -6
- package/dist/security/playwright/agent-skills-helpers.d.ts +8 -6
- package/dist/web-containers/playwright/agent-skills-helpers.d.ts +8 -6
- package/dist/web-containers/source/index.esm.js +56 -16
- package/dist/web-containers/source/next/index.esm.js +57 -17
- package/dist/web-utils/playwright/agent-skills-helpers.d.ts +8 -6
- package/package.json +3 -3
|
@@ -55,15 +55,17 @@ export declare function addSkillToAgent(page: Page, skillName: string): Promise<
|
|
|
55
55
|
*/
|
|
56
56
|
export declare function verifySkillAdded(page: Page, skillName: string): Promise<void>;
|
|
57
57
|
/**
|
|
58
|
-
* Open the three-dots actions menu for a skill row on
|
|
59
|
-
* Agent-tab rows
|
|
60
|
-
*
|
|
61
|
-
* document.body, so the returned locator is page-level by design.
|
|
58
|
+
* Open the three-dots actions menu for a CATALOG skill row (Edit/Delete on
|
|
59
|
+
* editable skills). Agent-tab rows have no menu — detaching is a direct
|
|
60
|
+
* "Remove" button there (see `removeSkillFromAgent`). The menu content
|
|
61
|
+
* portals to document.body, so the returned locator is page-level by design.
|
|
62
62
|
*/
|
|
63
63
|
export declare function openSkillActionsMenu(page: Page, skillName: string): Promise<Locator>;
|
|
64
64
|
/**
|
|
65
|
-
* Detach a skill from the agent via its row
|
|
66
|
-
* "Agent Skills" sub-tab
|
|
65
|
+
* Detach a skill from the agent via its row's direct "Remove" button on the
|
|
66
|
+
* "Agent Skills" sub-tab (the visible text is just "Remove"; the button's
|
|
67
|
+
* accessible name carries the skill, mirroring the catalog's Add button).
|
|
68
|
+
* Waits for the toast confirming the detach.
|
|
67
69
|
*/
|
|
68
70
|
export declare function removeSkillFromAgent(page: Page, skillName: string): Promise<void>;
|
|
69
71
|
/**
|
|
@@ -55,15 +55,17 @@ export declare function addSkillToAgent(page: Page, skillName: string): Promise<
|
|
|
55
55
|
*/
|
|
56
56
|
export declare function verifySkillAdded(page: Page, skillName: string): Promise<void>;
|
|
57
57
|
/**
|
|
58
|
-
* Open the three-dots actions menu for a skill row on
|
|
59
|
-
* Agent-tab rows
|
|
60
|
-
*
|
|
61
|
-
* document.body, so the returned locator is page-level by design.
|
|
58
|
+
* Open the three-dots actions menu for a CATALOG skill row (Edit/Delete on
|
|
59
|
+
* editable skills). Agent-tab rows have no menu — detaching is a direct
|
|
60
|
+
* "Remove" button there (see `removeSkillFromAgent`). The menu content
|
|
61
|
+
* portals to document.body, so the returned locator is page-level by design.
|
|
62
62
|
*/
|
|
63
63
|
export declare function openSkillActionsMenu(page: Page, skillName: string): Promise<Locator>;
|
|
64
64
|
/**
|
|
65
|
-
* Detach a skill from the agent via its row
|
|
66
|
-
* "Agent Skills" sub-tab
|
|
65
|
+
* Detach a skill from the agent via its row's direct "Remove" button on the
|
|
66
|
+
* "Agent Skills" sub-tab (the visible text is just "Remove"; the button's
|
|
67
|
+
* accessible name carries the skill, mirroring the catalog's Add button).
|
|
68
|
+
* Waits for the toast confirming the detach.
|
|
67
69
|
*/
|
|
68
70
|
export declare function removeSkillFromAgent(page: Page, skillName: string): Promise<void>;
|
|
69
71
|
/**
|
|
@@ -55,15 +55,17 @@ export declare function addSkillToAgent(page: Page, skillName: string): Promise<
|
|
|
55
55
|
*/
|
|
56
56
|
export declare function verifySkillAdded(page: Page, skillName: string): Promise<void>;
|
|
57
57
|
/**
|
|
58
|
-
* Open the three-dots actions menu for a skill row on
|
|
59
|
-
* Agent-tab rows
|
|
60
|
-
*
|
|
61
|
-
* document.body, so the returned locator is page-level by design.
|
|
58
|
+
* Open the three-dots actions menu for a CATALOG skill row (Edit/Delete on
|
|
59
|
+
* editable skills). Agent-tab rows have no menu — detaching is a direct
|
|
60
|
+
* "Remove" button there (see `removeSkillFromAgent`). The menu content
|
|
61
|
+
* portals to document.body, so the returned locator is page-level by design.
|
|
62
62
|
*/
|
|
63
63
|
export declare function openSkillActionsMenu(page: Page, skillName: string): Promise<Locator>;
|
|
64
64
|
/**
|
|
65
|
-
* Detach a skill from the agent via its row
|
|
66
|
-
* "Agent Skills" sub-tab
|
|
65
|
+
* Detach a skill from the agent via its row's direct "Remove" button on the
|
|
66
|
+
* "Agent Skills" sub-tab (the visible text is just "Remove"; the button's
|
|
67
|
+
* accessible name carries the skill, mirroring the catalog's Add button).
|
|
68
|
+
* Waits for the toast confirming the detach.
|
|
67
69
|
*/
|
|
68
70
|
export declare function removeSkillFromAgent(page: Page, skillName: string): Promise<void>;
|
|
69
71
|
/**
|
|
@@ -3041,7 +3041,8 @@ var clawSandboxAgentSkills$3 = {
|
|
|
3041
3041
|
noEffectiveSkills: "No skills enabled for this agent yet. Add one from the Available Skills tab.",
|
|
3042
3042
|
addToAgent: "Add",
|
|
3043
3043
|
addToAgentLabel: "Add {name} to this agent",
|
|
3044
|
-
removeFromAgent: "Remove
|
|
3044
|
+
removeFromAgent: "Remove",
|
|
3045
|
+
removeFromAgentLabel: "Remove {name} from this agent",
|
|
3045
3046
|
skillAdded: "{name} added",
|
|
3046
3047
|
skillRemoved: "{name} removed",
|
|
3047
3048
|
featuredBadge: "Featured",
|
|
@@ -10204,7 +10205,8 @@ var clawSandboxAgentSkills$2 = {
|
|
|
10204
10205
|
noEffectiveSkills: "Cet agent n'a pas encore de compétences activées. Ajoutez-en une depuis l'onglet Compétences disponibles.",
|
|
10205
10206
|
addToAgent: "Ajouter",
|
|
10206
10207
|
addToAgentLabel: "Ajouter {name} à cet agent",
|
|
10207
|
-
removeFromAgent: "Retirer
|
|
10208
|
+
removeFromAgent: "Retirer",
|
|
10209
|
+
removeFromAgentLabel: "Retirer {name} de cet agent",
|
|
10208
10210
|
skillAdded: "{name} ajoutée",
|
|
10209
10211
|
skillRemoved: "{name} retirée",
|
|
10210
10212
|
featuredBadge: "En vedette",
|
|
@@ -17201,7 +17203,8 @@ var clawSandboxAgentSkills$1 = {
|
|
|
17201
17203
|
noEffectiveSkills: "Este agente aún no tiene habilidades habilitadas. Añade una desde la pestaña Habilidades disponibles.",
|
|
17202
17204
|
addToAgent: "Añadir",
|
|
17203
17205
|
addToAgentLabel: "Añadir {name} a este agente",
|
|
17204
|
-
removeFromAgent: "Quitar
|
|
17206
|
+
removeFromAgent: "Quitar",
|
|
17207
|
+
removeFromAgentLabel: "Quitar {name} de este agente",
|
|
17205
17208
|
skillAdded: "{name} añadida",
|
|
17206
17209
|
skillRemoved: "{name} eliminada",
|
|
17207
17210
|
featuredBadge: "Destacada",
|
|
@@ -24198,7 +24201,8 @@ var clawSandboxAgentSkills = {
|
|
|
24198
24201
|
noEffectiveSkills: "此智能体尚未启用任何技能。请从“可用技能”标签页添加。",
|
|
24199
24202
|
addToAgent: "添加",
|
|
24200
24203
|
addToAgentLabel: "将 {name} 添加到此智能体",
|
|
24201
|
-
removeFromAgent: "
|
|
24204
|
+
removeFromAgent: "移除",
|
|
24205
|
+
removeFromAgentLabel: "将 {name} 从此智能体中移除",
|
|
24202
24206
|
skillAdded: "已添加 {name}",
|
|
24203
24207
|
skillRemoved: "已移除 {name}",
|
|
24204
24208
|
featuredBadge: "精选",
|
|
@@ -236140,12 +236144,31 @@ const EMPTY_SKILL_FORM = {
|
|
|
236140
236144
|
};
|
|
236141
236145
|
/** Page size for both skill lists. */
|
|
236142
236146
|
const SKILLS_PAGE_SIZE = 10;
|
|
236143
|
-
function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
236147
|
+
function AgentSkills({ platformKey, mentorUniqueId, mentorDbId }) {
|
|
236144
236148
|
var _a, _b, _c, _d, _e, _f;
|
|
236145
236149
|
const t = useT();
|
|
236146
236150
|
// Skills are managed independently of the sandbox connection — org-level
|
|
236147
236151
|
// skills and the mentor's assignments both resolve without a wired Claw
|
|
236148
236152
|
// instance, so we always fetch and let admins add/assign skills freely.
|
|
236153
|
+
// Assignment reads/writes are RBAC-gated when the host passes `mentorDbId`:
|
|
236154
|
+
// each action needs its grant from the mentor's permission check (the
|
|
236155
|
+
// endpoints 403 without it). Without a `mentorDbId` there is nothing to
|
|
236156
|
+
// check against, so the gates fail open — previous behavior.
|
|
236157
|
+
const rbacPermissions = useSelector(selectRbacPermissions);
|
|
236158
|
+
const mentorRbacKey = mentorDbId != null ? `/mentors/${mentorDbId}/` : null;
|
|
236159
|
+
const hasMentorRbacData = mentorRbacKey ? mentorRbacKey in rbacPermissions : false;
|
|
236160
|
+
const hasAssignmentGrant = (permission) => mentorRbacKey
|
|
236161
|
+
? hasMentorRbacData && checkRbacPermission(rbacPermissions, `${mentorRbacKey}#${permission}`)
|
|
236162
|
+
: true;
|
|
236163
|
+
const canViewAssignments = hasAssignmentGrant('view_skill_assignments');
|
|
236164
|
+
const canCreateAssignment = hasAssignmentGrant('create_skill_assignment');
|
|
236165
|
+
const canWriteAssignment = hasAssignmentGrant('write_skill_assignment');
|
|
236166
|
+
const canDeleteAssignment = hasAssignmentGrant('delete_skill_assignment');
|
|
236167
|
+
// The Available Skills catalog is a platform-level resource without its own
|
|
236168
|
+
// mentor-scoped grant; its only purpose here is attaching skills, so the
|
|
236169
|
+
// whole tab (fetch included — it can 403 too) plus skill authoring rides on
|
|
236170
|
+
// `create_skill_assignment` until the backend exposes catalog grants.
|
|
236171
|
+
const canBrowseCatalog = canCreateAssignment;
|
|
236149
236172
|
// The "Agent Skills" tab pages the assignment list server-side.
|
|
236150
236173
|
const [agentPage, setAgentPage] = useState(1);
|
|
236151
236174
|
const { data: assignmentsPage, isLoading: isLoadingAssignments } = useGetMentorSkillAssignmentsQuery({
|
|
@@ -236153,7 +236176,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236153
236176
|
mentorUniqueId,
|
|
236154
236177
|
limit: SKILLS_PAGE_SIZE,
|
|
236155
236178
|
offset: (agentPage - 1) * SKILLS_PAGE_SIZE,
|
|
236156
|
-
});
|
|
236179
|
+
}, { skip: !canViewAssignments });
|
|
236157
236180
|
const agentAssignments = (_a = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.results) !== null && _a !== void 0 ? _a : [];
|
|
236158
236181
|
const agentTotalCount = (_b = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.count) !== null && _b !== void 0 ? _b : 0;
|
|
236159
236182
|
const agentTotalPages = Math.max(1, Math.ceil(agentTotalCount / SKILLS_PAGE_SIZE));
|
|
@@ -236167,7 +236190,9 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236167
236190
|
const { data: assignmentCoverage } = useGetMentorSkillAssignmentsQuery({
|
|
236168
236191
|
org: platformKey,
|
|
236169
236192
|
mentorUniqueId,
|
|
236170
|
-
}
|
|
236193
|
+
},
|
|
236194
|
+
// Only powers the catalog tab's "Added" chips — pointless without it.
|
|
236195
|
+
{ skip: !canViewAssignments || !canBrowseCatalog });
|
|
236171
236196
|
const assignedSkillIds = new Set(((_c = assignmentCoverage === null || assignmentCoverage === void 0 ? void 0 : assignmentCoverage.results) !== null && _c !== void 0 ? _c : []).map((a) => a.skill));
|
|
236172
236197
|
// The "Available skills" tab pages the catalog server-side. Deliberately
|
|
236173
236198
|
// unfiltered: disabled skills stay listed (switch off) so admins can
|
|
@@ -236177,7 +236202,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236177
236202
|
org: platformKey,
|
|
236178
236203
|
limit: SKILLS_PAGE_SIZE,
|
|
236179
236204
|
offset: (catalogPage - 1) * SKILLS_PAGE_SIZE,
|
|
236180
|
-
});
|
|
236205
|
+
}, { skip: !canBrowseCatalog });
|
|
236181
236206
|
const catalogSkills = (_d = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _d !== void 0 ? _d : [];
|
|
236182
236207
|
const catalogTotalCount = (_e = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.count) !== null && _e !== void 0 ? _e : 0;
|
|
236183
236208
|
const catalogTotalPages = Math.max(1, Math.ceil(catalogTotalCount / SKILLS_PAGE_SIZE));
|
|
@@ -236308,7 +236333,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236308
236333
|
}).unwrap();
|
|
236309
236334
|
// A private skill only takes effect once assigned, and the Agent Skills
|
|
236310
236335
|
// tab lists assignments — so attach it to this agent right away.
|
|
236311
|
-
if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id)) {
|
|
236336
|
+
if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id) && canCreateAssignment) {
|
|
236312
236337
|
await createAssignment({
|
|
236313
236338
|
org: platformKey,
|
|
236314
236339
|
mentorUniqueId,
|
|
@@ -236348,7 +236373,10 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236348
236373
|
}).unwrap();
|
|
236349
236374
|
// Making a skill private to this agent implies it should be usable by
|
|
236350
236375
|
// it — attach it if it isn't assigned yet.
|
|
236351
|
-
if (!privateLocked &&
|
|
236376
|
+
if (!privateLocked &&
|
|
236377
|
+
form.privateToAgent &&
|
|
236378
|
+
canCreateAssignment &&
|
|
236379
|
+
!assignedSkillIds.has(editingSkill.unique_id)) {
|
|
236352
236380
|
await createAssignment({
|
|
236353
236381
|
org: platformKey,
|
|
236354
236382
|
mentorUniqueId,
|
|
@@ -236376,17 +236404,21 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236376
236404
|
}
|
|
236377
236405
|
};
|
|
236378
236406
|
const renderBadges = (skill) => (jsxs("span", { className: "flex flex-wrap items-center gap-1", children: [skill.category && (jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-[0.625rem] text-gray-500", children: skill.category })), skill.skill_type === 'native' && (jsx("span", { className: "rounded-full bg-purple-50 px-2 py-0.5 text-[0.625rem] text-purple-600", children: t('clawSandboxAgentSkills.nativeBadge') })), skill.is_featured && (jsx("span", { className: "rounded-full bg-amber-50 px-2 py-0.5 text-[0.625rem] text-amber-600", children: t('clawSandboxAgentSkills.featuredBadge') })), skill.mentor === mentorUniqueId && (jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[0.625rem] text-blue-600", children: t('clawSandboxAgentSkills.privateBadge') }))] }));
|
|
236379
|
-
// Agent-tab rows are assignments: their only
|
|
236380
|
-
|
|
236407
|
+
// Agent-tab rows are assignments: detaching is their only action, so it's
|
|
236408
|
+
// a direct button (mirroring the catalog's "Add to Agent") rather than a
|
|
236409
|
+
// one-item three-dots menu — hidden entirely without the delete grant.
|
|
236410
|
+
const renderAssignmentRemoveButton = (assignment) => !canDeleteAssignment ? null : (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleDetach(assignment), "aria-label": t('clawSandboxAgentSkills.removeFromAgentLabel', {
|
|
236411
|
+
name: assignment.skill_name,
|
|
236412
|
+
}), children: [jsx(CircleMinus$1, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.removeFromAgent')] }));
|
|
236381
236413
|
// Catalog rows carry the full skill object: edit/delete where allowed.
|
|
236382
236414
|
const renderSkillMenu = (skill) => {
|
|
236383
236415
|
if (!isSkillEditable(skill))
|
|
236384
236416
|
return null;
|
|
236385
236417
|
return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: skill.name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openEditSkillDialog(skill), children: [jsx(Pen, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.edit')] }), jsxs(DropdownMenuItem, { variant: "destructive", className: "cursor-pointer", onClick: () => setDeletingSkill(skill), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.delete')] })] })] }));
|
|
236386
236418
|
};
|
|
236387
|
-
return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] })] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') })] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
|
|
236419
|
+
return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), canBrowseCatalog && (jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] }))] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), canBrowseCatalog && (jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') }))] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill || !canWriteAssignment, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
|
|
236388
236420
|
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
236389
|
-
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }),
|
|
236421
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }), renderAssignmentRemoveButton(assignment)] })] }, assignment.id))), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), canBrowseCatalog && (jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : isOtherAgentsPrivate(skill) ? (
|
|
236390
236422
|
// Another agent's private skill: Add stays visible but
|
|
236391
236423
|
// disabled, with the reason on hover/focus.
|
|
236392
236424
|
jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { tabIndex: 0, children: jsxs(Button$1, { variant: "outline", size: "sm", disabled: true, "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
|
|
@@ -236395,7 +236427,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
236395
236427
|
name: skill.name,
|
|
236396
236428
|
}), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] })), isSkillEditable(skill) && (jsx(Switch, { checked: skill.enabled, onCheckedChange: (checked) => handleToggleSkill(skill, checked), disabled: isUpdatingSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(skill.enabled
|
|
236397
236429
|
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
236398
|
-
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) })] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
|
|
236430
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) }))] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
|
|
236399
236431
|
? t('clawSandboxAgentSkills.creating')
|
|
236400
236432
|
: t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm, privateLocked: !!editingSkill && isOtherAgentsPrivate(editingSkill) }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
|
|
236401
236433
|
? t('clawSandboxAgentSkills.saving')
|
|
@@ -238996,6 +239028,14 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
|
|
|
238996
239028
|
? checkRbacPermission(rbacPermissions, `/mentors/${mentorDbId}/#chat`)
|
|
238997
239029
|
: true; // Default to true if mentor ID not available or RBAC data not loaded
|
|
238998
239030
|
const isChatDisabledByRbac = !hasChatPermission;
|
|
239031
|
+
// Listing an agent's skills is a separate, stricter grant than chatting:
|
|
239032
|
+
// the per-agent skills endpoint 403s for users without
|
|
239033
|
+
// `view_skill_assignments`, so the `/` picker's fetch stays off until the
|
|
239034
|
+
// mentor's permission check has explicitly granted it (no permission data
|
|
239035
|
+
// loaded means no fetch — chatting never requires this endpoint).
|
|
239036
|
+
const hasSkillAssignmentsPermission = mentorDbId && hasMentorRbacData
|
|
239037
|
+
? checkRbacPermission(rbacPermissions, `/mentors/${mentorDbId}/#view_skill_assignments`)
|
|
239038
|
+
: false;
|
|
238999
239039
|
const { FreeTrialDialog, closeModal: closeFreeTrialModal, isModalOpen: isFreeTrialModalOpen, executeWithTrialCheck, } = useShowFreeTrialDialog();
|
|
239000
239040
|
const embedMode = useEmbedMode();
|
|
239001
239041
|
const inputValue = useSelector(chatInputSliceSelectors.selectTextareaInput);
|
|
@@ -239038,7 +239078,7 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
|
|
|
239038
239078
|
const fetchedSlashSkills = useSlashSkills({
|
|
239039
239079
|
org: tenantKey,
|
|
239040
239080
|
mentorUniqueId,
|
|
239041
|
-
enabled: slashSkillsEnabled && !slashSkills,
|
|
239081
|
+
enabled: slashSkillsEnabled && !slashSkills && hasSkillAssignmentsPermission,
|
|
239042
239082
|
active: slashTokenActive,
|
|
239043
239083
|
});
|
|
239044
239084
|
const effectiveSlashSkills = slashSkills !== null && slashSkills !== void 0 ? slashSkills : (slashSkillsEnabled ? fetchedSlashSkills.skills : []);
|
|
@@ -98586,7 +98586,8 @@ var clawSandboxAgentSkills$3 = {
|
|
|
98586
98586
|
noEffectiveSkills: "No skills enabled for this agent yet. Add one from the Available Skills tab.",
|
|
98587
98587
|
addToAgent: "Add",
|
|
98588
98588
|
addToAgentLabel: "Add {name} to this agent",
|
|
98589
|
-
removeFromAgent: "Remove
|
|
98589
|
+
removeFromAgent: "Remove",
|
|
98590
|
+
removeFromAgentLabel: "Remove {name} from this agent",
|
|
98590
98591
|
skillAdded: "{name} added",
|
|
98591
98592
|
skillRemoved: "{name} removed",
|
|
98592
98593
|
featuredBadge: "Featured",
|
|
@@ -105749,7 +105750,8 @@ var clawSandboxAgentSkills$2 = {
|
|
|
105749
105750
|
noEffectiveSkills: "Cet agent n'a pas encore de compétences activées. Ajoutez-en une depuis l'onglet Compétences disponibles.",
|
|
105750
105751
|
addToAgent: "Ajouter",
|
|
105751
105752
|
addToAgentLabel: "Ajouter {name} à cet agent",
|
|
105752
|
-
removeFromAgent: "Retirer
|
|
105753
|
+
removeFromAgent: "Retirer",
|
|
105754
|
+
removeFromAgentLabel: "Retirer {name} de cet agent",
|
|
105753
105755
|
skillAdded: "{name} ajoutée",
|
|
105754
105756
|
skillRemoved: "{name} retirée",
|
|
105755
105757
|
featuredBadge: "En vedette",
|
|
@@ -112746,7 +112748,8 @@ var clawSandboxAgentSkills$1 = {
|
|
|
112746
112748
|
noEffectiveSkills: "Este agente aún no tiene habilidades habilitadas. Añade una desde la pestaña Habilidades disponibles.",
|
|
112747
112749
|
addToAgent: "Añadir",
|
|
112748
112750
|
addToAgentLabel: "Añadir {name} a este agente",
|
|
112749
|
-
removeFromAgent: "Quitar
|
|
112751
|
+
removeFromAgent: "Quitar",
|
|
112752
|
+
removeFromAgentLabel: "Quitar {name} de este agente",
|
|
112750
112753
|
skillAdded: "{name} añadida",
|
|
112751
112754
|
skillRemoved: "{name} eliminada",
|
|
112752
112755
|
featuredBadge: "Destacada",
|
|
@@ -119743,7 +119746,8 @@ var clawSandboxAgentSkills = {
|
|
|
119743
119746
|
noEffectiveSkills: "此智能体尚未启用任何技能。请从“可用技能”标签页添加。",
|
|
119744
119747
|
addToAgent: "添加",
|
|
119745
119748
|
addToAgentLabel: "将 {name} 添加到此智能体",
|
|
119746
|
-
removeFromAgent: "
|
|
119749
|
+
removeFromAgent: "移除",
|
|
119750
|
+
removeFromAgentLabel: "将 {name} 从此智能体中移除",
|
|
119747
119751
|
skillAdded: "已添加 {name}",
|
|
119748
119752
|
skillRemoved: "已移除 {name}",
|
|
119749
119753
|
featuredBadge: "精选",
|
|
@@ -259087,12 +259091,31 @@ const EMPTY_SKILL_FORM = {
|
|
|
259087
259091
|
};
|
|
259088
259092
|
/** Page size for both skill lists. */
|
|
259089
259093
|
const SKILLS_PAGE_SIZE = 10;
|
|
259090
|
-
function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
259094
|
+
function AgentSkills({ platformKey, mentorUniqueId, mentorDbId }) {
|
|
259091
259095
|
var _a, _b, _c, _d, _e, _f;
|
|
259092
259096
|
const t = useT();
|
|
259093
259097
|
// Skills are managed independently of the sandbox connection — org-level
|
|
259094
259098
|
// skills and the mentor's assignments both resolve without a wired Claw
|
|
259095
259099
|
// instance, so we always fetch and let admins add/assign skills freely.
|
|
259100
|
+
// Assignment reads/writes are RBAC-gated when the host passes `mentorDbId`:
|
|
259101
|
+
// each action needs its grant from the mentor's permission check (the
|
|
259102
|
+
// endpoints 403 without it). Without a `mentorDbId` there is nothing to
|
|
259103
|
+
// check against, so the gates fail open — previous behavior.
|
|
259104
|
+
const rbacPermissions = useSelector(selectRbacPermissions);
|
|
259105
|
+
const mentorRbacKey = mentorDbId != null ? `/mentors/${mentorDbId}/` : null;
|
|
259106
|
+
const hasMentorRbacData = mentorRbacKey ? mentorRbacKey in rbacPermissions : false;
|
|
259107
|
+
const hasAssignmentGrant = (permission) => mentorRbacKey
|
|
259108
|
+
? hasMentorRbacData && checkRbacPermission(rbacPermissions, `${mentorRbacKey}#${permission}`)
|
|
259109
|
+
: true;
|
|
259110
|
+
const canViewAssignments = hasAssignmentGrant('view_skill_assignments');
|
|
259111
|
+
const canCreateAssignment = hasAssignmentGrant('create_skill_assignment');
|
|
259112
|
+
const canWriteAssignment = hasAssignmentGrant('write_skill_assignment');
|
|
259113
|
+
const canDeleteAssignment = hasAssignmentGrant('delete_skill_assignment');
|
|
259114
|
+
// The Available Skills catalog is a platform-level resource without its own
|
|
259115
|
+
// mentor-scoped grant; its only purpose here is attaching skills, so the
|
|
259116
|
+
// whole tab (fetch included — it can 403 too) plus skill authoring rides on
|
|
259117
|
+
// `create_skill_assignment` until the backend exposes catalog grants.
|
|
259118
|
+
const canBrowseCatalog = canCreateAssignment;
|
|
259096
259119
|
// The "Agent Skills" tab pages the assignment list server-side.
|
|
259097
259120
|
const [agentPage, setAgentPage] = useState(1);
|
|
259098
259121
|
const { data: assignmentsPage, isLoading: isLoadingAssignments } = useGetMentorSkillAssignmentsQuery({
|
|
@@ -259100,7 +259123,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259100
259123
|
mentorUniqueId,
|
|
259101
259124
|
limit: SKILLS_PAGE_SIZE,
|
|
259102
259125
|
offset: (agentPage - 1) * SKILLS_PAGE_SIZE,
|
|
259103
|
-
});
|
|
259126
|
+
}, { skip: !canViewAssignments });
|
|
259104
259127
|
const agentAssignments = (_a = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.results) !== null && _a !== void 0 ? _a : [];
|
|
259105
259128
|
const agentTotalCount = (_b = assignmentsPage === null || assignmentsPage === void 0 ? void 0 : assignmentsPage.count) !== null && _b !== void 0 ? _b : 0;
|
|
259106
259129
|
const agentTotalPages = Math.max(1, Math.ceil(agentTotalCount / SKILLS_PAGE_SIZE));
|
|
@@ -259114,7 +259137,9 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259114
259137
|
const { data: assignmentCoverage } = useGetMentorSkillAssignmentsQuery({
|
|
259115
259138
|
org: platformKey,
|
|
259116
259139
|
mentorUniqueId,
|
|
259117
|
-
}
|
|
259140
|
+
},
|
|
259141
|
+
// Only powers the catalog tab's "Added" chips — pointless without it.
|
|
259142
|
+
{ skip: !canViewAssignments || !canBrowseCatalog });
|
|
259118
259143
|
const assignedSkillIds = new Set(((_c = assignmentCoverage === null || assignmentCoverage === void 0 ? void 0 : assignmentCoverage.results) !== null && _c !== void 0 ? _c : []).map((a) => a.skill));
|
|
259119
259144
|
// The "Available skills" tab pages the catalog server-side. Deliberately
|
|
259120
259145
|
// unfiltered: disabled skills stay listed (switch off) so admins can
|
|
@@ -259124,7 +259149,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259124
259149
|
org: platformKey,
|
|
259125
259150
|
limit: SKILLS_PAGE_SIZE,
|
|
259126
259151
|
offset: (catalogPage - 1) * SKILLS_PAGE_SIZE,
|
|
259127
|
-
});
|
|
259152
|
+
}, { skip: !canBrowseCatalog });
|
|
259128
259153
|
const catalogSkills = (_d = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.results) !== null && _d !== void 0 ? _d : [];
|
|
259129
259154
|
const catalogTotalCount = (_e = catalogSkillsPage === null || catalogSkillsPage === void 0 ? void 0 : catalogSkillsPage.count) !== null && _e !== void 0 ? _e : 0;
|
|
259130
259155
|
const catalogTotalPages = Math.max(1, Math.ceil(catalogTotalCount / SKILLS_PAGE_SIZE));
|
|
@@ -259255,7 +259280,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259255
259280
|
}).unwrap();
|
|
259256
259281
|
// A private skill only takes effect once assigned, and the Agent Skills
|
|
259257
259282
|
// tab lists assignments — so attach it to this agent right away.
|
|
259258
|
-
if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id)) {
|
|
259283
|
+
if (form.privateToAgent && (created === null || created === void 0 ? void 0 : created.unique_id) && canCreateAssignment) {
|
|
259259
259284
|
await createAssignment({
|
|
259260
259285
|
org: platformKey,
|
|
259261
259286
|
mentorUniqueId,
|
|
@@ -259295,7 +259320,10 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259295
259320
|
}).unwrap();
|
|
259296
259321
|
// Making a skill private to this agent implies it should be usable by
|
|
259297
259322
|
// it — attach it if it isn't assigned yet.
|
|
259298
|
-
if (!privateLocked &&
|
|
259323
|
+
if (!privateLocked &&
|
|
259324
|
+
form.privateToAgent &&
|
|
259325
|
+
canCreateAssignment &&
|
|
259326
|
+
!assignedSkillIds.has(editingSkill.unique_id)) {
|
|
259299
259327
|
await createAssignment({
|
|
259300
259328
|
org: platformKey,
|
|
259301
259329
|
mentorUniqueId,
|
|
@@ -259323,17 +259351,21 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259323
259351
|
}
|
|
259324
259352
|
};
|
|
259325
259353
|
const renderBadges = (skill) => (jsxs("span", { className: "flex flex-wrap items-center gap-1", children: [skill.category && (jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-[0.625rem] text-gray-500", children: skill.category })), skill.skill_type === 'native' && (jsx("span", { className: "rounded-full bg-purple-50 px-2 py-0.5 text-[0.625rem] text-purple-600", children: t('clawSandboxAgentSkills.nativeBadge') })), skill.is_featured && (jsx("span", { className: "rounded-full bg-amber-50 px-2 py-0.5 text-[0.625rem] text-amber-600", children: t('clawSandboxAgentSkills.featuredBadge') })), skill.mentor === mentorUniqueId && (jsx("span", { className: "rounded-full bg-blue-50 px-2 py-0.5 text-[0.625rem] text-blue-600", children: t('clawSandboxAgentSkills.privateBadge') }))] }));
|
|
259326
|
-
// Agent-tab rows are assignments: their only
|
|
259327
|
-
|
|
259354
|
+
// Agent-tab rows are assignments: detaching is their only action, so it's
|
|
259355
|
+
// a direct button (mirroring the catalog's "Add to Agent") rather than a
|
|
259356
|
+
// one-item three-dots menu — hidden entirely without the delete grant.
|
|
259357
|
+
const renderAssignmentRemoveButton = (assignment) => !canDeleteAssignment ? null : (jsxs(Button$1, { variant: "outline", size: "sm", className: "cursor-pointer", disabled: isMutatingAgentSkill, onClick: () => handleDetach(assignment), "aria-label": t('clawSandboxAgentSkills.removeFromAgentLabel', {
|
|
259358
|
+
name: assignment.skill_name,
|
|
259359
|
+
}), children: [jsx(CircleMinus$1, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.removeFromAgent')] }));
|
|
259328
259360
|
// Catalog rows carry the full skill object: edit/delete where allowed.
|
|
259329
259361
|
const renderSkillMenu = (skill) => {
|
|
259330
259362
|
if (!isSkillEditable(skill))
|
|
259331
259363
|
return null;
|
|
259332
259364
|
return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button$1, { variant: "ghost", size: "sm", "aria-label": t('clawSandboxAgentSkills.skillActions', { name: skill.name }), children: jsx(Ellipsis, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsxs(DropdownMenuItem, { className: "cursor-pointer", onClick: () => openEditSkillDialog(skill), children: [jsx(Pen, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.edit')] }), jsxs(DropdownMenuItem, { variant: "destructive", className: "cursor-pointer", onClick: () => setDeletingSkill(skill), children: [jsx(Trash2, { className: "mr-2 h-4 w-4" }), t('clawSandboxAgentSkills.delete')] })] })] }));
|
|
259333
259365
|
};
|
|
259334
|
-
return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] })] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') })] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
|
|
259366
|
+
return (jsxs("div", { className: "space-y-4", "data-testid": "agent-skills-content", children: [jsxs("div", { className: "flex items-start justify-between gap-4", children: [jsxs("p", { className: "flex items-start gap-2 text-xs text-gray-500", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 shrink-0 text-gray-400" }), jsx("span", { children: t('clawSandboxAgentSkills.sessionNote') })] }), canBrowseCatalog && (jsxs(Button$1, { onClick: openNewSkillDialog, size: "sm", className: "shrink-0 cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.newSkill')] }))] }), jsxs(Tabs, { defaultValue: "agent", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.tabsAriaLabel'), "data-testid": "agent-skills-tabs", children: [jsx(TabsTrigger, { value: "agent", "data-testid": "agent-skills-tab-agent", children: t('clawSandboxAgentSkills.tabAgentSkills') }), canBrowseCatalog && (jsx(TabsTrigger, { value: "available", "data-testid": "agent-skills-tab-available", children: t('clawSandboxAgentSkills.tabAvailableSkills') }))] }), jsx(TabsContent, { value: "agent", className: "space-y-3", children: isLoadingAssignments ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : agentTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noEffectiveSkills') }) })) : (jsxs(Fragment$1, { children: [agentAssignments.map((assignment) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "agent-skill-row", children: [jsx("div", { className: "min-w-0 space-y-1", children: jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: assignment.skill_name }), assignment.skill_slug && (jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["/", assignment.skill_slug] }))] }) }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [jsx(Switch, { checked: assignment.enabled, onCheckedChange: (checked) => handleToggleAssignment(assignment, checked), disabled: isMutatingAgentSkill || !canWriteAssignment, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(assignment.enabled
|
|
259335
259367
|
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
259336
|
-
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }),
|
|
259368
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: assignment.skill_name }) }), renderAssignmentRemoveButton(assignment)] })] }, assignment.id))), jsx(IblPagination, { currentPage: agentPage, totalPages: agentTotalPages, onPageChange: setAgentPage, disabled: isMutatingAgentSkill })] })) }), canBrowseCatalog && (jsx(TabsContent, { value: "available", className: "space-y-3", children: isLoadingCatalog ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.loadingSkills') })] })) : catalogTotalCount === 0 ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: t('clawSandboxAgentSkills.noSkillsAvailable') }) })) : (jsxs(Fragment$1, { children: [catalogSkills.map((skill) => (jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border p-4", "data-testid": "available-skill-row", children: [jsxs("div", { className: "min-w-0 space-y-1", children: [jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-700", children: skill.name }), jsxs("span", { className: "hidden text-xs text-gray-400 sm:inline", children: ["v", skill.version] }), renderBadges(skill)] }), skill.description && (jsx("p", { className: "line-clamp-2 text-xs text-gray-500", children: skill.description }))] }), jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isCoveredByAgent(skill) ? (jsx("span", { className: "rounded-full bg-green-50 px-2 py-0.5 text-xs text-green-700", children: t('clawSandboxAgentSkills.addedBadge') })) : isOtherAgentsPrivate(skill) ? (
|
|
259337
259369
|
// Another agent's private skill: Add stays visible but
|
|
259338
259370
|
// disabled, with the reason on hover/focus.
|
|
259339
259371
|
jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { tabIndex: 0, children: jsxs(Button$1, { variant: "outline", size: "sm", disabled: true, "aria-label": t('clawSandboxAgentSkills.addToAgentLabel', {
|
|
@@ -259342,7 +259374,7 @@ function AgentSkills({ platformKey, mentorUniqueId }) {
|
|
|
259342
259374
|
name: skill.name,
|
|
259343
259375
|
}), children: [jsx(Plus, { className: "h-4 w-4" }), t('clawSandboxAgentSkills.addToAgent')] })), isSkillEditable(skill) && (jsx(Switch, { checked: skill.enabled, onCheckedChange: (checked) => handleToggleSkill(skill, checked), disabled: isUpdatingSkill, className: "cursor-pointer data-[state=checked]:bg-blue-500", "aria-label": t(skill.enabled
|
|
259344
259376
|
? 'clawSandboxAgentSkills.skillEnabledLabel'
|
|
259345
|
-
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) })] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
|
|
259377
|
+
: 'clawSandboxAgentSkills.skillDisabledLabel', { name: skill.name }) })), renderSkillMenu(skill)] })] }, skill.id))), jsx(IblPagination, { currentPage: catalogPage, totalPages: catalogTotalPages, onPageChange: setCatalogPage, disabled: isMutatingAgentSkill })] })) }))] }), jsx(OverlayModal, { open: showNewDialog, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.newSkillDialogTitle'), className: "max-w-2xl", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsxs("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: [jsx(SkillForm, { form: form, onChange: setForm }), jsx("p", { className: "text-xs text-gray-500", children: t('clawSandboxAgentSkills.filesAfterCreateHint') })] }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleCreateSkill, disabled: isCreatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isCreatingSkill
|
|
259346
259378
|
? t('clawSandboxAgentSkills.creating')
|
|
259347
259379
|
: t('clawSandboxAgentSkills.create') })] })] }) }), jsx(OverlayModal, { open: !!editingSkill, onClose: closeAllDialogs, title: t('clawSandboxAgentSkills.editSkillDialogTitle'), className: "max-w-2xl", children: jsxs(Tabs, { defaultValue: "details", className: "w-full", children: [jsxs(TabsList, { "aria-label": t('clawSandboxAgentSkills.editSkillDialogTitle'), "data-testid": "skill-dialog-tabs", children: [jsx(TabsTrigger, { value: "details", "data-testid": "skill-dialog-tab-details", children: t('clawSandboxAgentSkills.detailsTab') }), jsx(TabsTrigger, { value: "files", "data-testid": "skill-dialog-tab-files", children: t('clawSandboxAgentSkills.filesTab') })] }), jsx(TabsContent, { value: "details", children: jsxs("div", { className: "flex h-[min(75dvh,46rem)] flex-col", children: [jsx("div", { className: "flex-1 space-y-4 overflow-y-auto pb-4 pr-1", children: jsx(SkillForm, { form: form, onChange: setForm, privateLocked: !!editingSkill && isOtherAgentsPrivate(editingSkill) }) }), jsxs("div", { className: "flex justify-end gap-2 border-t pt-4", children: [jsx(Button$1, { variant: "outline", onClick: closeAllDialogs, children: t('clawSandboxAgentSkills.cancel') }), jsx(Button$1, { onClick: handleUpdateSkill, disabled: isUpdatingSkill || !form.name || !form.slug, className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isUpdatingSkill
|
|
259348
259380
|
? t('clawSandboxAgentSkills.saving')
|
|
@@ -261773,6 +261805,14 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
|
|
|
261773
261805
|
? checkRbacPermission(rbacPermissions, `/mentors/${mentorDbId}/#chat`)
|
|
261774
261806
|
: true; // Default to true if mentor ID not available or RBAC data not loaded
|
|
261775
261807
|
const isChatDisabledByRbac = !hasChatPermission;
|
|
261808
|
+
// Listing an agent's skills is a separate, stricter grant than chatting:
|
|
261809
|
+
// the per-agent skills endpoint 403s for users without
|
|
261810
|
+
// `view_skill_assignments`, so the `/` picker's fetch stays off until the
|
|
261811
|
+
// mentor's permission check has explicitly granted it (no permission data
|
|
261812
|
+
// loaded means no fetch — chatting never requires this endpoint).
|
|
261813
|
+
const hasSkillAssignmentsPermission = mentorDbId && hasMentorRbacData
|
|
261814
|
+
? checkRbacPermission(rbacPermissions, `/mentors/${mentorDbId}/#view_skill_assignments`)
|
|
261815
|
+
: false;
|
|
261776
261816
|
const { FreeTrialDialog, closeModal: closeFreeTrialModal, isModalOpen: isFreeTrialModalOpen, executeWithTrialCheck, } = useShowFreeTrialDialog();
|
|
261777
261817
|
const embedMode = useEmbedMode();
|
|
261778
261818
|
const inputValue = useSelector(chatInputSliceSelectors.selectTextareaInput);
|
|
@@ -261815,7 +261855,7 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
|
|
|
261815
261855
|
const fetchedSlashSkills = useSlashSkills({
|
|
261816
261856
|
org: tenantKey,
|
|
261817
261857
|
mentorUniqueId,
|
|
261818
|
-
enabled: slashSkillsEnabled && !slashSkills,
|
|
261858
|
+
enabled: slashSkillsEnabled && !slashSkills && hasSkillAssignmentsPermission,
|
|
261819
261859
|
active: slashTokenActive,
|
|
261820
261860
|
});
|
|
261821
261861
|
const effectiveSlashSkills = slashSkills !== null && slashSkills !== void 0 ? slashSkills : (slashSkillsEnabled ? fetchedSlashSkills.skills : []);
|
|
@@ -275151,7 +275191,7 @@ function AgentSkillsTab({ labels: labelsOverride, tenantKey: tenantKeyProp, ment
|
|
|
275151
275191
|
return (jsxs(Fragment$1, { children: [jsx("div", { className: "hidden h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-700", children: labels.header.description })] }) }), jsx("div", { className: "flex-1 space-y-4 p-3", style: {
|
|
275152
275192
|
overflowY: 'auto',
|
|
275153
275193
|
overflowX: 'hidden',
|
|
275154
|
-
}, children: isSettingsLoading || !mentorUniqueId ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: labels.loading })] })) : knownNotBaseAgent ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: labels.notBaseAgent }) })) : (jsx(AgentSkills, { platformKey: tenantKey, mentorUniqueId: mentorUniqueId })) })] }));
|
|
275194
|
+
}, children: isSettingsLoading || !mentorUniqueId ? (jsxs("div", { className: "flex items-center gap-2 p-4", role: "status", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-500", children: labels.loading })] })) : knownNotBaseAgent ? (jsx("div", { className: "flex items-center justify-center rounded-lg border p-6", children: jsx("p", { className: "text-sm text-gray-500", children: labels.notBaseAgent }) })) : (jsx(AgentSkills, { platformKey: tenantKey, mentorUniqueId: mentorUniqueId, mentorDbId: settings === null || settings === void 0 ? void 0 : settings.mentor_id })) })] }));
|
|
275155
275195
|
}
|
|
275156
275196
|
|
|
275157
275197
|
const AGENT_GRADER_TAB_LABELS = {
|
|
@@ -55,15 +55,17 @@ export declare function addSkillToAgent(page: Page, skillName: string): Promise<
|
|
|
55
55
|
*/
|
|
56
56
|
export declare function verifySkillAdded(page: Page, skillName: string): Promise<void>;
|
|
57
57
|
/**
|
|
58
|
-
* Open the three-dots actions menu for a skill row on
|
|
59
|
-
* Agent-tab rows
|
|
60
|
-
*
|
|
61
|
-
* document.body, so the returned locator is page-level by design.
|
|
58
|
+
* Open the three-dots actions menu for a CATALOG skill row (Edit/Delete on
|
|
59
|
+
* editable skills). Agent-tab rows have no menu — detaching is a direct
|
|
60
|
+
* "Remove" button there (see `removeSkillFromAgent`). The menu content
|
|
61
|
+
* portals to document.body, so the returned locator is page-level by design.
|
|
62
62
|
*/
|
|
63
63
|
export declare function openSkillActionsMenu(page: Page, skillName: string): Promise<Locator>;
|
|
64
64
|
/**
|
|
65
|
-
* Detach a skill from the agent via its row
|
|
66
|
-
* "Agent Skills" sub-tab
|
|
65
|
+
* Detach a skill from the agent via its row's direct "Remove" button on the
|
|
66
|
+
* "Agent Skills" sub-tab (the visible text is just "Remove"; the button's
|
|
67
|
+
* accessible name carries the skill, mirroring the catalog's Add button).
|
|
68
|
+
* Waits for the toast confirming the detach.
|
|
67
69
|
*/
|
|
68
70
|
export declare function removeSkillFromAgent(page: Page, skillName: string): Promise<void>;
|
|
69
71
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iblai/iblai-js",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5",
|
|
4
4
|
"description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
"dotenv": "16.6.1",
|
|
78
78
|
"winston": "3.19.0",
|
|
79
79
|
"@iblai/data-layer": "1.13.0",
|
|
80
|
-
"@iblai/mcp": "1.12.
|
|
81
|
-
"@iblai/web-containers": "1.19.
|
|
80
|
+
"@iblai/mcp": "1.12.2",
|
|
81
|
+
"@iblai/web-containers": "1.19.3",
|
|
82
82
|
"@iblai/web-utils": "2.2.2"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|