fix(ai-roles): harden model override, role-name uniqueness, id validation, list least-privilege

Follow-up fixes on the agent-roles feature:

- ai.service: a cross-driver override to the ollama driver (when the
  workspace driver is not ollama) now fails with an explicit 503 instead
  of silently reusing the workspace base URL, which belongs to a different
  provider. Same-driver ollama and openai/gemini overrides are unchanged.
- migration: add a partial unique index on (workspace_id, name) WHERE
  deleted_at IS NULL so role names are unique per workspace without
  soft-deleted rows blocking re-creation; map Postgres 23505 to a 409
  ConflictException on create/update.
- dto: validate the role id as @IsUUID instead of @IsString.
- roles list: do not expose instructions/modelConfig to non-admin members.
  The list endpoint now returns a picker view (id/name/emoji/description/
  enabled) to members and the full view only to admins (same gate as the
  CRUD endpoints). Client IAiRole fields made optional accordingly.

Adds tests for the cross-driver-ollama throw, the 23505->409 mapping, and
the non-admin picker-view security invariant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
vvzvlad
2026-06-20 18:30:33 +03:00
parent 20a1780977
commit 4b31128e24
8 changed files with 325 additions and 65 deletions

View File

@@ -31,20 +31,24 @@ export interface IAiRoleModelConfig {
}
/**
* An agent role (mirrors the server `AgentRoleView`). A role replaces the
* agent's persona (instructions) and may optionally override the model. The
* safety framework is always still applied server-side.
* An agent role (mirrors the server role views). A role replaces the agent's
* persona (instructions) and may optionally override the model. The safety
* framework is always still applied server-side.
*
* The list endpoint returns the FULL view to admins and a reduced picker view to
* ordinary members, so the admin-only fields (`instructions`, `modelConfig`,
* `createdAt`, `updatedAt`) are optional here — present only for admins.
*/
export interface IAiRole {
id: string;
name: string;
emoji: string | null;
description: string | null;
instructions: string;
modelConfig: IAiRoleModelConfig | null;
instructions?: string;
modelConfig?: IAiRoleModelConfig | null;
enabled: boolean;
createdAt: string;
updatedAt: string;
createdAt?: string;
updatedAt?: string;
}
/** Admin create payload for a role. */