mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 00:03:00 +00:00
feat: deploying an agent draft from review keeps its deployed identity
As for an app: an agent draft carries no identity, so the review page claims the deployed one back, which the backend honours for an admin or wm_deployers member. The draft diff leaves the deployed identity out, since a draft never holds one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b04de3eeda
commit
70824ea11b
@@ -122,6 +122,25 @@ describe('deployDraft preserves on_behalf_of', () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('agent: claims the deployed identity, which its draft never carries', async () => {
|
||||
vi.mocked(ResourceService.getResource).mockResolvedValueOnce({
|
||||
path: 'f/support/triage_agent',
|
||||
resource_type: 'ai_agent',
|
||||
value: { system_prompt: 'deployed', on_behalf_of: 'u/alice' },
|
||||
draft: { path: 'f/support/triage_agent', args: { system_prompt: 'drafted' } }
|
||||
} as any)
|
||||
|
||||
expect(await deployDraft('resource', 'f/support/triage_agent', 'ws')).toEqual({ success: true })
|
||||
expect(ResourceService.updateResource).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
requestBody: expect.objectContaining({
|
||||
value: { system_prompt: 'drafted', on_behalf_of: 'u/alice' },
|
||||
preserve_on_behalf_of: true
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// The resource branch reads the item again when the deploy lands, and falls back to the deployed
|
||||
|
||||
@@ -233,8 +233,14 @@ function canonicalizeDraftDiffValue(kind: DraftKind, raw: any, isDraft: boolean)
|
||||
if (kind === 'resource') {
|
||||
// draft: { args, description, resource_type, labels, wsSpecific }
|
||||
// deployed row: { value, description, resource_type, labels, ws_specific }
|
||||
// An agent's identity is in its deployed value and never in a draft, which is not an edit.
|
||||
let value = (isDraft ? raw.args : raw.value) ?? {}
|
||||
if (raw.resource_type === 'ai_agent') {
|
||||
const { on_behalf_of: _, ...rest } = value
|
||||
value = rest
|
||||
}
|
||||
return {
|
||||
value: (isDraft ? raw.args : raw.value) ?? {},
|
||||
value,
|
||||
description: raw.description ?? '',
|
||||
resource_type: raw.resource_type ?? undefined,
|
||||
labels: raw.labels ?? undefined,
|
||||
@@ -639,12 +645,18 @@ export async function deployDraft(
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// As for an app: an agent draft carries no identity, so the deployed one is kept (the
|
||||
// backend resets it to the deploying user without the flag, gated by
|
||||
// can_preserve_on_behalf_of).
|
||||
const agentObo =
|
||||
deployed.resource_type === 'ai_agent' ? deployed.value?.on_behalf_of : undefined
|
||||
await ResourceService.updateResource({
|
||||
workspace,
|
||||
path,
|
||||
requestBody: {
|
||||
path: d.path ?? path,
|
||||
value: d.args ?? {},
|
||||
value: agentObo ? { ...(d.args ?? {}), on_behalf_of: agentObo } : (d.args ?? {}),
|
||||
preserve_on_behalf_of: agentObo ? true : undefined,
|
||||
description: d.description ?? '',
|
||||
labels: d.labels,
|
||||
ws_specific: d.wsSpecific
|
||||
|
||||
Reference in New Issue
Block a user