feat(M4-009,M4-010,M4-011): routing rules CRUD, per-user overrides, agent capabilities (#320)
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #320.
This commit is contained in:
@@ -19,6 +19,53 @@ import { AuthGuard } from '../auth/auth.guard.js';
|
||||
import { CurrentUser } from '../auth/current-user.decorator.js';
|
||||
import { CreateAgentConfigDto, UpdateAgentConfigDto } from './agent-config.dto.js';
|
||||
|
||||
// ─── M4-011 helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
type CapabilityFields = {
|
||||
domains?: string[] | null;
|
||||
preferredModel?: string | null;
|
||||
preferredProvider?: string | null;
|
||||
toolSets?: string[] | null;
|
||||
};
|
||||
|
||||
/** Extract capability shorthand fields from the DTO (undefined if none provided). */
|
||||
function buildCapabilities(dto: CapabilityFields): Record<string, unknown> | undefined {
|
||||
const hasAny =
|
||||
dto.domains !== undefined ||
|
||||
dto.preferredModel !== undefined ||
|
||||
dto.preferredProvider !== undefined ||
|
||||
dto.toolSets !== undefined;
|
||||
|
||||
if (!hasAny) return undefined;
|
||||
|
||||
const cap: Record<string, unknown> = {};
|
||||
if (dto.domains !== undefined) cap['domains'] = dto.domains;
|
||||
if (dto.preferredModel !== undefined) cap['preferredModel'] = dto.preferredModel;
|
||||
if (dto.preferredProvider !== undefined) cap['preferredProvider'] = dto.preferredProvider;
|
||||
if (dto.toolSets !== undefined) cap['toolSets'] = dto.toolSets;
|
||||
return cap;
|
||||
}
|
||||
|
||||
/** Merge capabilities into the config object, preserving other config keys. */
|
||||
function mergeCapabilities(
|
||||
existing: Record<string, unknown> | null | undefined,
|
||||
capabilities: Record<string, unknown> | undefined,
|
||||
): Record<string, unknown> | undefined {
|
||||
if (capabilities === undefined && existing === undefined) return undefined;
|
||||
if (capabilities === undefined) return existing ?? undefined;
|
||||
|
||||
const base = existing ?? {};
|
||||
const existingCap =
|
||||
typeof base['capabilities'] === 'object' && base['capabilities'] !== null
|
||||
? (base['capabilities'] as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
return {
|
||||
...base,
|
||||
capabilities: { ...existingCap, ...capabilities },
|
||||
};
|
||||
}
|
||||
|
||||
@Controller('api/agents')
|
||||
@UseGuards(AuthGuard)
|
||||
export class AgentConfigsController {
|
||||
@@ -41,10 +88,22 @@ export class AgentConfigsController {
|
||||
|
||||
@Post()
|
||||
async create(@Body() dto: CreateAgentConfigDto, @CurrentUser() user: { id: string }) {
|
||||
// Merge capability shorthand fields into config.capabilities (M4-011)
|
||||
const capabilities = buildCapabilities(dto);
|
||||
const config = mergeCapabilities(dto.config, capabilities);
|
||||
|
||||
return this.brain.agents.create({
|
||||
...dto,
|
||||
ownerId: user.id,
|
||||
name: dto.name,
|
||||
provider: dto.provider,
|
||||
model: dto.model,
|
||||
status: dto.status,
|
||||
projectId: dto.projectId,
|
||||
systemPrompt: dto.systemPrompt,
|
||||
allowedTools: dto.allowedTools,
|
||||
skills: dto.skills,
|
||||
isSystem: false,
|
||||
config,
|
||||
ownerId: user.id,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,10 +122,32 @@ export class AgentConfigsController {
|
||||
throw new ForbiddenException('Agent does not belong to the current user');
|
||||
}
|
||||
|
||||
// Merge capability shorthand fields into config.capabilities (M4-011)
|
||||
const capabilities = buildCapabilities(dto);
|
||||
const baseConfig =
|
||||
dto.config !== undefined
|
||||
? dto.config
|
||||
: (agent.config as Record<string, unknown> | null | undefined);
|
||||
const config = mergeCapabilities(baseConfig ?? undefined, capabilities);
|
||||
|
||||
// Pass ownerId for user agents so the repo WHERE clause enforces ownership.
|
||||
// For system agents (admin path) pass undefined so the WHERE matches only on id.
|
||||
const ownerId = agent.isSystem ? undefined : user.id;
|
||||
const updated = await this.brain.agents.update(id, dto, ownerId);
|
||||
const updated = await this.brain.agents.update(
|
||||
id,
|
||||
{
|
||||
name: dto.name,
|
||||
provider: dto.provider,
|
||||
model: dto.model,
|
||||
status: dto.status,
|
||||
projectId: dto.projectId,
|
||||
systemPrompt: dto.systemPrompt,
|
||||
allowedTools: dto.allowedTools,
|
||||
skills: dto.skills,
|
||||
config: capabilities !== undefined || dto.config !== undefined ? config : undefined,
|
||||
},
|
||||
ownerId,
|
||||
);
|
||||
if (!updated) throw new NotFoundException('Agent not found');
|
||||
return updated;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user