Scaffold agent permissions, approval comments, and hiring governance types

Add pending_approval agent status, permissions jsonb column, and AgentPermissions
type with canCreateAgents flag. Add approval_comments table and ApprovalComment
type. Extend approval statuses with revision_requested. Add validators for
permission updates, approval revision requests, resubmission, and approval
comments. Add requireBoardApprovalForNewAgents to company update schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-19 09:10:38 -06:00
parent 9f250acf43
commit e0a878f4eb
13 changed files with 131 additions and 2 deletions

View File

@@ -7,6 +7,7 @@ export const AGENT_STATUSES = [
"idle",
"running",
"error",
"pending_approval",
"terminated",
] as const;
export type AgentStatus = (typeof AGENT_STATUSES)[number];
@@ -61,7 +62,13 @@ export type ProjectStatus = (typeof PROJECT_STATUSES)[number];
export const APPROVAL_TYPES = ["hire_agent", "approve_ceo_strategy"] as const;
export type ApprovalType = (typeof APPROVAL_TYPES)[number];
export const APPROVAL_STATUSES = ["pending", "approved", "rejected", "cancelled"] as const;
export const APPROVAL_STATUSES = [
"pending",
"revision_requested",
"approved",
"rejected",
"cancelled",
] as const;
export type ApprovalStatus = (typeof APPROVAL_STATUSES)[number];
export const HEARTBEAT_INVOCATION_SOURCES = [

View File

@@ -36,12 +36,14 @@ export {
export type {
Company,
Agent,
AgentPermissions,
AgentKeyCreated,
Project,
Issue,
IssueComment,
Goal,
Approval,
ApprovalComment,
CostEvent,
CostSummary,
HeartbeatRun,
@@ -62,10 +64,13 @@ export {
updateAgentSchema,
createAgentKeySchema,
wakeAgentSchema,
agentPermissionsSchema,
updateAgentPermissionsSchema,
type CreateAgent,
type UpdateAgent,
type CreateAgentKey,
type WakeAgent,
type UpdateAgentPermissions,
createProjectSchema,
updateProjectSchema,
type CreateProject,
@@ -84,8 +89,14 @@ export {
type UpdateGoal,
createApprovalSchema,
resolveApprovalSchema,
requestApprovalRevisionSchema,
resubmitApprovalSchema,
addApprovalCommentSchema,
type CreateApproval,
type ResolveApproval,
type RequestApprovalRevision,
type ResubmitApproval,
type AddApprovalComment,
createCostEventSchema,
updateBudgetSchema,
type CreateCostEvent,

View File

@@ -4,6 +4,10 @@ import type {
AgentStatus,
} from "../constants.js";
export interface AgentPermissions {
canCreateAgents: boolean;
}
export interface Agent {
id: string;
companyId: string;
@@ -18,6 +22,7 @@ export interface Agent {
runtimeConfig: Record<string, unknown>;
budgetMonthlyCents: number;
spentMonthlyCents: number;
permissions: AgentPermissions;
lastHeartbeatAt: Date | null;
metadata: Record<string, unknown> | null;
createdAt: Date;

View File

@@ -14,3 +14,14 @@ export interface Approval {
createdAt: Date;
updatedAt: Date;
}
export interface ApprovalComment {
id: string;
companyId: string;
approvalId: string;
authorAgentId: string | null;
authorUserId: string | null;
body: string;
createdAt: Date;
updatedAt: Date;
}

View File

@@ -3,7 +3,7 @@ export type { Agent, AgentKeyCreated } from "./agent.js";
export type { Project } from "./project.js";
export type { Issue, IssueComment, IssueAncestor } from "./issue.js";
export type { Goal } from "./goal.js";
export type { Approval } from "./approval.js";
export type { Approval, ApprovalComment } from "./approval.js";
export type { CostEvent, CostSummary } from "./cost.js";
export type {
HeartbeatRun,

View File

@@ -5,6 +5,10 @@ import {
AGENT_STATUSES,
} from "../constants.js";
export const agentPermissionsSchema = z.object({
canCreateAgents: z.boolean().optional().default(false),
});
export const createAgentSchema = z.object({
name: z.string().min(1),
role: z.enum(AGENT_ROLES).optional().default("general"),
@@ -15,6 +19,7 @@ export const createAgentSchema = z.object({
adapterConfig: z.record(z.unknown()).optional().default({}),
runtimeConfig: z.record(z.unknown()).optional().default({}),
budgetMonthlyCents: z.number().int().nonnegative().optional().default(0),
permissions: agentPermissionsSchema.optional(),
metadata: z.record(z.unknown()).optional().nullable(),
});
@@ -44,3 +49,9 @@ export const wakeAgentSchema = z.object({
});
export type WakeAgent = z.infer<typeof wakeAgentSchema>;
export const updateAgentPermissionsSchema = z.object({
canCreateAgents: z.boolean(),
});
export type UpdateAgentPermissions = z.infer<typeof updateAgentPermissionsSchema>;

View File

@@ -15,3 +15,22 @@ export const resolveApprovalSchema = z.object({
});
export type ResolveApproval = z.infer<typeof resolveApprovalSchema>;
export const requestApprovalRevisionSchema = z.object({
decisionNote: z.string().optional().nullable(),
decidedByUserId: z.string().optional().default("board"),
});
export type RequestApprovalRevision = z.infer<typeof requestApprovalRevisionSchema>;
export const resubmitApprovalSchema = z.object({
payload: z.record(z.unknown()).optional(),
});
export type ResubmitApproval = z.infer<typeof resubmitApprovalSchema>;
export const addApprovalCommentSchema = z.object({
body: z.string().min(1),
});
export type AddApprovalComment = z.infer<typeof addApprovalCommentSchema>;

View File

@@ -14,6 +14,7 @@ export const updateCompanySchema = createCompanySchema
.extend({
status: z.enum(COMPANY_STATUSES).optional(),
spentMonthlyCents: z.number().int().nonnegative().optional(),
requireBoardApprovalForNewAgents: z.boolean().optional(),
});
export type UpdateCompany = z.infer<typeof updateCompanySchema>;

View File

@@ -10,10 +10,13 @@ export {
updateAgentSchema,
createAgentKeySchema,
wakeAgentSchema,
agentPermissionsSchema,
updateAgentPermissionsSchema,
type CreateAgent,
type UpdateAgent,
type CreateAgentKey,
type WakeAgent,
type UpdateAgentPermissions,
} from "./agent.js";
export {
@@ -44,8 +47,14 @@ export {
export {
createApprovalSchema,
resolveApprovalSchema,
requestApprovalRevisionSchema,
resubmitApprovalSchema,
addApprovalCommentSchema,
type CreateApproval,
type ResolveApproval,
type RequestApprovalRevision,
type ResubmitApproval,
type AddApprovalComment,
} from "./approval.js";
export {