fix: enforce read-only agent resources in build mode (#41220)

This commit is contained in:
Joel authored and GitHub committed 2026-08-25 03:37:02 +00:00
1 parent d4ed92cb0a
commit fcb380044c
6 files changed
+30 -54

No files matched your search

@@ -730,11 +730,11 @@ describe('AgentFiles', () => {
).not.toBeInTheDocument()
})
it('should keep add action available for build drafts', () => {
it('should hide the add action while a build draft is read-only', () => {
renderAgentFiles({ readOnly: true })
expect(
screen.getByRole('button', { name: /agentV2\.agentDetail\.configure\.files\.add/i }),
).toBeInTheDocument()
screen.queryByRole('button', { name: /agentV2\.agentDetail\.configure\.files\.add/i }),
).not.toBeInTheDocument()
})
})
@@ -375,6 +375,7 @@ export function AgentFiles() {
const { t } = useTranslation('agentV2')
const filesTip = t(($) => $['agentDetail.configure.files.tip'])
const filesTreeId = 'agent-configure-files-tree'
const readOnly = useAgentOrchestrateReadOnly()
const [isUploadOpen, setIsUploadOpen] = useState(false)
const promptAddCallbackRef = useRef<AgentOrchestrateAddActionOptions['onAdded']>(undefined)
const apiContext = useAgentConfigApiContext()
@@ -477,10 +478,12 @@ export function AgentFiles() {
rootClassName="border-b border-divider-subtle pt-4"
panelContentClassName="pb-4"
actions={
<ConfigureSectionAddButton
ariaLabel={t(($) => $['agentDetail.configure.files.add'])}
onClick={() => handleOpenUpload()}
/>
!readOnly ? (
<ConfigureSectionAddButton
ariaLabel={t(($) => $['agentDetail.configure.files.add'])}
onClick={() => handleOpenUpload()}
/>
) : undefined
}
>
{visibleFiles.length === 0 ? (
@@ -1516,7 +1516,7 @@ describe('AgentSkills', () => {
})
})
it('should keep delete available for an embedded skill while a build draft is read-only', async () => {
it('should expose only download for an embedded skill while a build draft is read-only', async () => {
const user = userEvent.setup()
renderAgentSkills({
apiContext: { agentId: 'agent-1', draftType: 'debug_build' },
@@ -1530,20 +1530,8 @@ describe('AgentSkills', () => {
)
expect(screen.getByText('common.operation.download')).toBeInTheDocument()
await user.click(screen.getByText('common.operation.delete'))
await waitFor(() => {
expect(mocks.deleteSkillMutationFn.mock.calls[0]?.[0]).toEqual({
params: {
agent_id: 'agent-1',
name: 'Tender Analyzer',
},
query: {
draft_type: 'debug_build',
version_id: undefined,
},
})
})
expect(screen.queryByText('common.operation.delete')).not.toBeInTheDocument()
expect(mocks.deleteSkillMutationFn).not.toHaveBeenCalled()
})
it('should expose only download from an embedded skill row when viewing a version', async () => {
@@ -1853,8 +1841,7 @@ describe('AgentSkills', () => {
expect(mocks.replaceAgentSkillBindingsMutationFn).not.toHaveBeenCalled()
})
it('should keep the add menu available for build draft skills', async () => {
const user = userEvent.setup()
it('should hide the add action while a build draft is read-only', () => {
renderAgentSkills({
apiContext: {
agentId: 'agent-1',
@@ -1867,21 +1854,10 @@ describe('AgentSkills', () => {
readOnly: true,
})
await user.click(
await screen.findByRole('button', {
expect(
screen.queryByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.add/i,
}),
)
expect(
await screen.findByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.workspace\.label/i,
}),
).toBeInTheDocument()
expect(
screen.getByRole('button', {
name: /agentV2\.agentDetail\.configure\.skills\.addMenu\.upload\.label/i,
}),
).toBeInTheDocument()
).not.toBeInTheDocument()
})
})
@@ -49,10 +49,7 @@ import { ConfigureSectionEmpty } from '../common/empty'
import { ConfigureSection } from '../common/section'
import { AgentConfigureTipContent } from '../common/tip-content'
import { useAgentConfigApiContext } from '../config-context'
import {
useAgentOrchestrateReadOnly,
useAgentOrchestrateViewingVersion,
} from '../read-only-context'
import { useAgentOrchestrateReadOnly } from '../read-only-context'
import { AgentSkillItem } from './item'
import { AgentSkillUploadDialog } from './upload-dialog'
@@ -463,7 +460,7 @@ export function AgentSkills() {
const skillsTip = t(($) => $['agentDetail.configure.skills.tip'])
const skillsListId = 'agent-configure-skills-list'
const queryClient = useQueryClient()
const isViewingVersion = useAgentOrchestrateViewingVersion()
const readOnly = useAgentOrchestrateReadOnly()
const [addMenuOpen, setAddMenuOpen] = useState(false)
const [addMenuView, setAddMenuView] = useState<'menu' | 'workspace-selector'>('menu')
const [isUploadOpen, setIsUploadOpen] = useState(false)
@@ -511,7 +508,7 @@ export function AgentSkills() {
const replaceWorkspaceSkillBindings = useCallback(
(skillIds: string[], onSuccess?: () => void) => {
if (isViewingVersion || !hasLoadedAgentSkillBindings) return
if (readOnly || !hasLoadedAgentSkillBindings) return
replaceAgentSkillBindings(
{
@@ -563,8 +560,8 @@ export function AgentSkills() {
apiContext.agentId,
hasLoadedAgentSkillBindings,
invalidateAgentSkillBindings,
isViewingVersion,
queryClient,
readOnly,
replaceAgentSkillBindings,
t,
tSkill,
@@ -709,7 +706,7 @@ export function AgentSkills() {
rootClassName="border-b border-divider-subtle pt-4"
panelContentClassName="flex flex-col gap-1 pb-4"
actions={
!isViewingVersion && (
!readOnly && (
<Popover open={addMenuOpen} onOpenChange={handleAddMenuOpenChange}>
<PopoverTrigger
render={
@@ -775,7 +772,7 @@ export function AgentSkills() {
{workspaceSkills.map((skill) => (
<WorkspaceAgentSkillItem
key={skill.id}
canRemove={!isViewingVersion}
canRemove={!readOnly}
skill={skill}
onRemove={handleRemoveWorkspaceSkill}
/>
@@ -784,7 +781,7 @@ export function AgentSkills() {
<AgentSkillItem
key={skill.id}
apiContext={apiContext}
canRemove={!isViewingVersion}
canRemove={!readOnly}
skill={skill}
onRemove={handleRemoveSkill}
/>
@@ -587,14 +587,14 @@ describe('AgentTools', () => {
).not.toBeInTheDocument()
})
it('should keep add action available for build drafts', () => {
it('should hide the add action while a build draft is read-only', () => {
renderReadonlyAgentTools()
expect(
screen.getByRole('button', {
screen.queryByRole('button', {
name: 'agentV2.agentDetail.configure.tools.add',
}),
).toBeInTheDocument()
).not.toBeInTheDocument()
})
it('should hide CLI tool rows while CLI tools are disabled', () => {
@@ -41,7 +41,7 @@ import { ConfigureSectionAddButton } from '../common/add-button'
import { ConfigureSectionEmpty } from '../common/empty'
import { ConfigureSection } from '../common/section'
import { AgentConfigureTipContent } from '../common/tip-content'
import { useAgentOrchestrateViewingVersion } from '../read-only-context'
import { useAgentOrchestrateReadOnly } from '../read-only-context'
import { CliToolDialog } from './cli-tool/dialog'
import { AgentCliToolItem } from './cli-tool/item'
import {
@@ -380,7 +380,7 @@ function AddToolMenu({
export function AgentTools() {
const { t } = useTranslation('agentV2')
const isViewingVersion = useAgentOrchestrateViewingVersion()
const readOnly = useAgentOrchestrateReadOnly()
const setProviderToolCredential = useSetAtom(setProviderToolCredentialAtom)
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
@@ -530,7 +530,7 @@ export function AgentTools() {
rootClassName="border-b border-divider-subtle pt-4"
panelContentClassName="flex flex-col gap-1 pb-4"
actions={
!isViewingVersion ? (
!readOnly ? (
<AddToolMenu
onAddCliTool={openCliToolDialog}
onAddTools={addTools}