feat(agents): resolve agent shortnames to UUIDs in route params

Add router.param middleware that normalizes agent references, allowing
shortnames to be used in place of UUIDs in agent API routes. Includes
company-scoped lookup with ambiguity detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-02 17:02:08 -06:00
parent 90dfa2e9cd
commit dab5737b41

View File

@@ -8,6 +8,7 @@ import {
createAgentKeySchema,
createAgentHireSchema,
createAgentSchema,
isUuidLike,
resetAgentSessionSchema,
testAdapterEnvironmentSchema,
updateAgentPermissionsSchema,
@@ -26,7 +27,7 @@ import {
logActivity,
secretService,
} from "../services/index.js";
import { forbidden, unprocessable } from "../errors.js";
import { conflict, forbidden, unprocessable } from "../errors.js";
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
import { findServerAdapter, listAdapterModels } from "../adapters/index.js";
import { redactEventPayload } from "../redaction.js";
@@ -114,6 +115,38 @@ export function agentRoutes(db: Db) {
throw forbidden("Only CEO or agent creators can modify other agents");
}
async function resolveCompanyIdForAgentReference(req: Request): Promise<string | null> {
const companyIdQuery = req.query.companyId;
const requestedCompanyId =
typeof companyIdQuery === "string" && companyIdQuery.trim().length > 0
? companyIdQuery.trim()
: null;
if (requestedCompanyId) {
assertCompanyAccess(req, requestedCompanyId);
return requestedCompanyId;
}
if (req.actor.type === "agent" && req.actor.companyId) {
return req.actor.companyId;
}
return null;
}
async function normalizeAgentReference(req: Request, rawId: string): Promise<string> {
const raw = rawId.trim();
if (isUuidLike(raw)) return raw;
const companyId = await resolveCompanyIdForAgentReference(req);
if (!companyId) {
throw unprocessable("Agent shortname lookup requires companyId query parameter");
}
const resolved = await svc.resolveByReference(companyId, raw);
if (resolved.ambiguous) {
throw conflict("Agent shortname is ambiguous in this company. Use the agent ID.");
}
return resolved.agent?.id ?? raw;
}
function parseSourceIssueIds(input: {
sourceIssueId?: string | null;
sourceIssueIds?: string[];
@@ -259,6 +292,15 @@ export function agentRoutes(db: Db) {
};
}
router.param("id", async (req, _res, next, rawId) => {
try {
req.params.id = await normalizeAgentReference(req, String(rawId));
next();
} catch (err) {
next(err);
}
});
router.get("/adapters/:type/models", async (req, res) => {
const type = req.params.type as string;
const models = await listAdapterModels(type);