docs(api): document decision queue routes

Add complete OpenAPI coverage for decision queue and triage endpoints, including request schemas, response shapes, authentication, and status codes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-01 18:34:27 +00:00
1 parent 3310823de0
commit ec41102ae7
2 files changed
+208

No files matched your search

@@ -26,6 +26,7 @@ const apiPrefixes: Record<string, string> = {
"company-skill-policy.ts": "/api",
"costs.ts": "/api",
"dashboard.ts": "/api",
"decision-queues.ts": "/api",
"decisions.ts": "/api",
"decision-training.ts": "/api",
"environments.ts": "/api",
@@ -190,6 +191,26 @@ describe("openapi routes", () => {
expect(res.body.paths["/api/companies/{companyId}/folders/items/move"].post.summary).toBe(
"Move an item into or out of a folder",
);
const createQueue = res.body.paths["/api/companies/{companyId}/decision-queues"].post;
expect(createQueue.security).toContainEqual({ AgentBearerAuth: [] });
expect(createQueue.responses["200"]).toBeDefined();
expect(createQueue.responses["201"]).toBeDefined();
expect(createQueue.requestBody.content["application/json"].schema).toMatchObject({
type: "object",
properties: {
key: { type: "string", minLength: 1, maxLength: 80 },
title: { type: "string", minLength: 1, maxLength: 120 },
},
required: ["key", "title"],
});
const updateTriage = res.body.paths[
"/api/companies/{companyId}/decision-triage/{sourceKind}/{sourceId}"
].put;
expect(updateTriage.responses["422"]).toBeDefined();
expect(updateTriage.requestBody.content["application/json"].schema.properties).toMatchObject({
decideBy: { nullable: true },
snoozedUntil: { type: "string", format: "date-time", nullable: true },
});
expect(JSON.stringify(res.body.paths["/api/tool-gateway/tools"].get)).not.toContain("sessionToken");
expect(JSON.stringify(res.body.paths["/api/tool-gateway/tools/call"].post)).not.toContain("sessionToken");
});
+187
View File
@@ -48,8 +48,13 @@ import {
companyArtifactsQuerySchema,
companyArtifactsResponseSchema,
// Decisions
addDecisionQueueItemSchema,
createDecisionQueueSchema,
decisionAttentionSourceKindSchema,
decisionInputsSchema,
decisionOptionsSchema,
updateDecisionQueueSchema,
updateDecisionTriageSchema,
// Routine
createRoutineSchema,
updateRoutineSchema,
@@ -3277,6 +3282,188 @@ registry.registerPath({
// ─── Decisions ──────────────────────────────────────────────────────────────
// Decision queues and triage
const decisionQueueSeedRuleSchema = z.object({
key: z.string(),
description: z.string(),
signal: z.enum([
"issue_has_pull_request_work_product",
"plan_document_confirmation",
"ask_user_questions",
]),
}).strict();
const decisionQueueSchema = z.object({
id: z.string(),
companyId: z.string(),
key: z.string(),
title: z.string(),
description: z.string().nullable(),
createdByType: z.enum(["agent", "user", "system"]),
createdByAgentId: z.string().nullable(),
createdByUserId: z.string().nullable(),
createdByRunId: z.string().nullable(),
retentionDays: z.number().int().nullable(),
seedRules: z.array(decisionQueueSeedRuleSchema),
seedRulesEnabled: z.boolean(),
itemCount: z.number().int().nonnegative(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
}).strict();
const decisionQueueItemSchema = z.object({
id: z.string(),
companyId: z.string(),
queueId: z.string(),
sourceKind: decisionAttentionSourceKindSchema,
sourceId: z.string(),
addedByType: z.enum(["agent", "user", "system"]),
addedByAgentId: z.string().nullable(),
addedByUserId: z.string().nullable(),
addedByRunId: z.string().nullable(),
responsibleUserId: z.string().nullable(),
createdAt: z.string().datetime(),
}).strict();
const decisionTriageSchema = z.object({
id: z.string(),
companyId: z.string(),
sourceKind: decisionAttentionSourceKindSchema,
sourceId: z.string(),
decideBy: z.string().nullable(),
snoozedUntil: z.string().datetime().nullable(),
setByType: z.enum(["agent", "user"]),
setByAgentId: z.string().nullable(),
setByUserId: z.string().nullable(),
setByRunId: z.string().nullable(),
responsibleUserId: z.string().nullable(),
version: z.number().int().positive(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
}).strict();
registerCurrentRoute({
method: "get",
path: "/api/companies/{companyId}/decision-queue-seed-rules",
tags: ["decision-queues"],
summary: "List built-in decision queue seed rules",
responses: { 200: r.ok(z.array(decisionQueueSeedRuleSchema)), 401: r.unauthorized, 403: r.forbidden },
});
registerCurrentRoute({
method: "get",
path: "/api/companies/{companyId}/decision-queues",
tags: ["decision-queues"],
summary: "List decision queues",
responses: { 200: r.ok(z.array(decisionQueueSchema)), 401: r.unauthorized, 403: r.forbidden },
});
registerCurrentRoute({
method: "post",
path: "/api/companies/{companyId}/decision-queues",
tags: ["decision-queues"],
summary: "Create a decision queue",
body: createDecisionQueueSchema,
responses: {
200: r.ok(decisionQueueSchema),
201: r.ok(decisionQueueSchema),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
},
});
registerCurrentRoute({
method: "patch",
path: "/api/companies/{companyId}/decision-queues/{key}",
tags: ["decision-queues"],
summary: "Update a decision queue",
body: updateDecisionQueueSchema,
responses: {
200: r.ok(decisionQueueSchema),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
},
});
registerCurrentRoute({
method: "get",
path: "/api/companies/{companyId}/decision-queues/{key}/items",
tags: ["decision-queues"],
summary: "List visible items in a decision queue",
responses: {
200: r.ok(z.array(decisionQueueItemSchema)),
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
},
});
registerCurrentRoute({
method: "post",
path: "/api/companies/{companyId}/decision-queues/{key}/items",
tags: ["decision-queues"],
summary: "Add an item to a decision queue",
body: addDecisionQueueItemSchema,
responses: {
200: r.ok(decisionQueueItemSchema),
201: r.ok(decisionQueueItemSchema),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
},
});
registerCurrentRoute({
method: "delete",
path: "/api/companies/{companyId}/decision-queues/{key}/items/{sourceKind}/{sourceId}",
tags: ["decision-queues"],
summary: "Remove an item from a decision queue",
responses: {
200: r.ok(decisionQueueItemSchema),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
},
});
registerCurrentRoute({
method: "get",
path: "/api/companies/{companyId}/decision-triage/{sourceKind}/{sourceId}",
tags: ["decision-queues"],
summary: "Get decision triage for an attention source",
responses: {
200: r.ok(decisionTriageSchema.nullable()),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
},
});
registerCurrentRoute({
method: "put",
path: "/api/companies/{companyId}/decision-triage/{sourceKind}/{sourceId}",
tags: ["decision-queues"],
summary: "Set decision triage for an attention source",
body: updateDecisionTriageSchema,
responses: {
200: r.ok(decisionTriageSchema),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
422: r.unprocessable,
},
});
// Decisions
const createDecisionBodySchema = z.object({
title: z.string().trim().min(1).max(500),
body: z.string().max(100_000),