feat(05-01): budget enforcement types, schemas, IPC channels, and database CRUD
- Add BudgetPolicyRecord, BudgetIncidentRecord, BudgetStatusSummary types - Add 6 IPC channels for budget operations in contracts.ts - Add Zod schemas for all budget payloads - Add DesktopApi entries for budget methods - Add database CRUD: saveBudgetPolicy, deleteBudgetPolicy, listBudgetPolicies, getBudgetPolicy, getActivePoliciesForRun, getSpentInWindow (cents aggregation), upsertBudgetIncident (D-08 dedup), resolveIncidentsForPolicy, resolveWarnIncidentsForPolicy, autoCloseExpiredIncidents - Update recordRunCompletion to include project_id in cost_entries INSERT - Add migration 18: convert agent budgetMonthlyUsd to budget_policies rows - Add preload bridge stubs for budget IPC channels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
245eb4ccf3
commit
3a68cfa707
4 files changed
+408
-3
No files matched your search
+50
-1
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import type { AgentMessageRecord, AgentMetrics, BrowserActionRecord, CommentRecord, CompanyMetrics, DesktopEvent, InboxItem, OnboardingBootstrapResult, ProfileSnapshot, RunLogChunk, SkillImportResult, SkillRecord, SkillUpdateStatus, StandupReport } from "./types";
|
||||
import type { AgentMessageRecord, AgentMetrics, BrowserActionRecord, BudgetIncidentRecord, BudgetPolicyRecord, BudgetStatusSummary, CommentRecord, CompanyMetrics, DesktopEvent, InboxItem, OnboardingBootstrapResult, ProfileSnapshot, RunLogChunk, SkillImportResult, SkillRecord, SkillUpdateStatus, StandupReport } from "./types";
|
||||
|
||||
export const IPC_CHANNELS = {
|
||||
loadSnapshot: "agent-company:load-snapshot",
|
||||
@@ -83,6 +83,12 @@ export const IPC_CHANNELS = {
|
||||
listSkillAgents: "agent-company:list-skill-agents",
|
||||
listAgentSkills: "agent-company:list-agent-skills",
|
||||
listWakeupRequests: "agent-company:list-wakeup-requests",
|
||||
saveBudgetPolicy: "agent-company:save-budget-policy",
|
||||
deleteBudgetPolicy: "agent-company:delete-budget-policy",
|
||||
listBudgetPolicies: "agent-company:list-budget-policies",
|
||||
listBudgetIncidents: "agent-company:list-budget-incidents",
|
||||
resolveBudgetIncident: "agent-company:resolve-budget-incident",
|
||||
getBudgetStatus: "agent-company:get-budget-status",
|
||||
} as const;
|
||||
|
||||
const optionalId = z.preprocess(
|
||||
@@ -570,6 +576,43 @@ export const listAgentSkillsSchema = z.object({
|
||||
agentId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const budgetPolicyInputSchema = z.object({
|
||||
id: optionalId,
|
||||
companyId: z.string().uuid(),
|
||||
scopeType: z.enum(["company", "agent", "project"]),
|
||||
scopeId: z.string().uuid(),
|
||||
metric: z.literal("billed_cents").default("billed_cents"),
|
||||
windowKind: z.literal("calendar_month_utc").default("calendar_month_utc"),
|
||||
amount: z.number().int().min(1),
|
||||
warnPercent: z.number().int().min(1).max(100).default(80),
|
||||
hardStopEnabled: z.boolean().default(true),
|
||||
notifyEnabled: z.boolean().default(true),
|
||||
isActive: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const listBudgetPoliciesSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const deleteBudgetPolicySchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
policyId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const listBudgetIncidentsSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
status: z.enum(["open", "resolved", "auto_resolved_limit_increase", "auto_closed", "all"]).default("all"),
|
||||
});
|
||||
|
||||
export const resolveBudgetIncidentSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
incidentId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const getBudgetStatusSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const wakeupQuerySchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
status: z.enum(["queued", "claimed", "coalesced", "deferred", "completed"]).optional(),
|
||||
@@ -678,6 +721,12 @@ export type DesktopApi = {
|
||||
listSkillAgents: (companyId: string, skillId: string) => Promise<DesktopResult<string[]>>;
|
||||
listAgentSkills: (companyId: string, agentId: string) => Promise<DesktopResult<string[]>>;
|
||||
listWakeupRequests: (input: z.infer<typeof wakeupQuerySchema>) => Promise<DesktopResult<WakeupRequestRecord[]>>;
|
||||
saveBudgetPolicy: (input: z.infer<typeof budgetPolicyInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteBudgetPolicy: (input: z.infer<typeof deleteBudgetPolicySchema>) => Promise<DesktopResult<boolean>>;
|
||||
listBudgetPolicies: (companyId: string) => Promise<DesktopResult<BudgetPolicyRecord[]>>;
|
||||
listBudgetIncidents: (input: z.infer<typeof listBudgetIncidentsSchema>) => Promise<DesktopResult<BudgetIncidentRecord[]>>;
|
||||
resolveBudgetIncident: (input: z.infer<typeof resolveBudgetIncidentSchema>) => Promise<DesktopResult<boolean>>;
|
||||
getBudgetStatus: (companyId: string) => Promise<DesktopResult<BudgetStatusSummary>>;
|
||||
recoveryRetry: (taskId: string, agentId: string, companyId: string) => Promise<DesktopResult<null>>;
|
||||
recoveryDismiss: (taskId: string) => Promise<DesktopResult<null>>;
|
||||
recoveryFail: (taskId: string) => Promise<DesktopResult<null>>;
|
||||
|
||||
@@ -269,6 +269,54 @@ export interface CostRecord {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface BudgetPolicyRecord {
|
||||
id: string;
|
||||
companyId: string;
|
||||
scopeType: "company" | "agent" | "project";
|
||||
scopeId: string;
|
||||
metric: string;
|
||||
windowKind: string;
|
||||
amount: number; // INTEGER cents
|
||||
warnPercent: number;
|
||||
hardStopEnabled: boolean;
|
||||
notifyEnabled: boolean;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface BudgetIncidentRecord {
|
||||
id: string;
|
||||
companyId: string;
|
||||
policyId: string;
|
||||
scopeType: string;
|
||||
scopeId: string;
|
||||
metric: string;
|
||||
windowKind: string;
|
||||
windowStart: string;
|
||||
windowEnd: string;
|
||||
thresholdType: "warn_threshold" | "hard_stop";
|
||||
amountLimit: number;
|
||||
amountObserved: number;
|
||||
status: "open" | "resolved" | "auto_resolved_limit_increase" | "auto_closed";
|
||||
resolvedAt: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface BudgetStatusSummary {
|
||||
policies: Array<{
|
||||
policyId: string;
|
||||
scopeType: string;
|
||||
scopeId: string;
|
||||
amount: number;
|
||||
spent: number;
|
||||
warnPercent: number;
|
||||
hardStopEnabled: boolean;
|
||||
hasOpenIncident: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ActivityRecord {
|
||||
id: string;
|
||||
companyId: string;
|
||||
|
||||
Reference in new issue
Block a user