diff --git a/src/shared/contracts/schemas-agent-permissions.ts b/src/shared/contracts/schemas-agent-permissions.ts new file mode 100644 index 00000000..8e00ef24 --- /dev/null +++ b/src/shared/contracts/schemas-agent-permissions.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const getAgentPermissionsSchema = z.object({ + agentId: z.string().min(1), +}); + +export const setAgentPermissionSchema = z.object({ + companyId: z.string().min(1), + agentId: z.string().min(1), + permissionKey: z.string().min(1), + enabled: z.boolean(), +}); diff --git a/src/shared/contracts/schemas-automation.ts b/src/shared/contracts/schemas-automation.ts new file mode 100644 index 00000000..0c015711 --- /dev/null +++ b/src/shared/contracts/schemas-automation.ts @@ -0,0 +1,33 @@ +import { z } from "zod"; +import { DEPARTMENT_IDS } from "./helpers"; + +export const automationRuleInputSchema = z.object({ + id: z.preprocess((v) => (v === "" ? null : v), z.string().uuid().nullable().optional()), + companyId: z.string().uuid(), + name: z.string().min(2).max(120), + description: z.string().max(500).default(""), + trigger: z.enum([ + "task_status_changed", "task_created", "task_blocked", "task_reassigned", + "run_completed", "run_failed", "approval_created", "approval_resolved", + "budget_threshold", "agent_idle", "comment_posted", "document_created", + "schedule", "hire_approved", "sprint_started", "sprint_ended", + ]), + conditionsJson: z.string().default("{}"), + action: z.enum([ + "assign_task", "create_task", "notify_agent", "send_message", + "trigger_heartbeat", "create_approval", "update_task_status", + "escalate_to_manager", "cross_department_notify", "schedule_meeting", + "create_document", "auto_approve", "reassign_task", "run_task", + ]), + actionConfigJson: z.string().default("{}"), + sourceDepartment: DEPARTMENT_IDS.nullable().optional(), + targetDepartment: DEPARTMENT_IDS.nullable().optional(), + priority: z.number().int().min(0).max(100).default(50), + status: z.enum(["active", "paused", "disabled", "error"]).default("active"), +}); + +export const toggleAutomationRuleSchema = z.object({ + id: z.string().uuid(), + companyId: z.string().uuid(), + status: z.enum(["active", "paused"]), +}); diff --git a/src/shared/contracts/schemas-badges.ts b/src/shared/contracts/schemas-badges.ts new file mode 100644 index 00000000..71815fb0 --- /dev/null +++ b/src/shared/contracts/schemas-badges.ts @@ -0,0 +1,27 @@ +import { z } from "zod"; + +export const deleteEntitySchema = z.object({ + id: z.string().uuid(), + companyId: z.string().uuid(), +}); + +export const sidebarBadgesSchema = z.object({ + pendingApprovals: z.number(), + activeRuns: z.number(), + failedRuns: z.number(), + todoTasks: z.number(), + budgetWarnings: z.number(), + unreadMessages: z.number(), +}); +export type SidebarBadges = z.infer; + +export const entityGetterSchema = z.object({ + companyId: z.string().uuid(), + id: z.string().uuid(), +}); + +export const metricsQuerySchema = z.object({ + companyId: z.string().uuid(), + agentId: z.string().uuid().optional(), + days: z.number().int().min(1).max(90).default(30), +}); diff --git a/src/shared/contracts/schemas-budget.ts b/src/shared/contracts/schemas-budget.ts new file mode 100644 index 00000000..5c9844dd --- /dev/null +++ b/src/shared/contracts/schemas-budget.ts @@ -0,0 +1,39 @@ +import { z } from "zod"; +import { optionalId } from "./helpers"; + +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(), +}); diff --git a/src/shared/contracts/schemas-config-revisions.ts b/src/shared/contracts/schemas-config-revisions.ts new file mode 100644 index 00000000..15d99fea --- /dev/null +++ b/src/shared/contracts/schemas-config-revisions.ts @@ -0,0 +1,17 @@ +import { z } from "zod"; + +export const listConfigRevisionsSchema = z.object({ + companyId: z.string().uuid(), + agentId: z.string().uuid(), +}); + +export const getConfigRevisionSchema = z.object({ + companyId: z.string().uuid(), + revisionId: z.string().uuid(), +}); + +export const rollbackConfigRevisionSchema = z.object({ + companyId: z.string().uuid(), + agentId: z.string().uuid(), + revisionId: z.string().uuid(), +}); diff --git a/src/shared/contracts/schemas-heartbeat.ts b/src/shared/contracts/schemas-heartbeat.ts new file mode 100644 index 00000000..3a972c0e --- /dev/null +++ b/src/shared/contracts/schemas-heartbeat.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +export const heartbeatPolicySchema = z.object({ + companyId: z.string().min(1), + agentId: z.string().min(1), +}); + +export const setHeartbeatPolicySchema = z.object({ + companyId: z.string().min(1), + agentId: z.string().min(1), + enabled: z.boolean().optional(), + intervalSec: z.number().optional(), + cooldownSec: z.number().optional(), + maxConcurrentRuns: z.number().optional(), + wakeOnDemand: z.boolean().optional(), +}); diff --git a/src/shared/contracts/schemas-misc.ts b/src/shared/contracts/schemas-misc.ts index 8541cb52..9c217286 100644 --- a/src/shared/contracts/schemas-misc.ts +++ b/src/shared/contracts/schemas-misc.ts @@ -1,281 +1,14 @@ -import { z } from "zod"; -import { DEPARTMENT_IDS, optionalId } from "./helpers"; - -export const deleteEntitySchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), -}); - -export const sidebarBadgesSchema = z.object({ - pendingApprovals: z.number(), - activeRuns: z.number(), - failedRuns: z.number(), - todoTasks: z.number(), - budgetWarnings: z.number(), - unreadMessages: z.number(), -}); -export type SidebarBadges = z.infer; - -export const automationRuleInputSchema = z.object({ - id: z.preprocess((v) => (v === "" ? null : v), z.string().uuid().nullable().optional()), - companyId: z.string().uuid(), - name: z.string().min(2).max(120), - description: z.string().max(500).default(""), - trigger: z.enum([ - "task_status_changed", "task_created", "task_blocked", "task_reassigned", - "run_completed", "run_failed", "approval_created", "approval_resolved", - "budget_threshold", "agent_idle", "comment_posted", "document_created", - "schedule", "hire_approved", "sprint_started", "sprint_ended", - ]), - conditionsJson: z.string().default("{}"), - action: z.enum([ - "assign_task", "create_task", "notify_agent", "send_message", - "trigger_heartbeat", "create_approval", "update_task_status", - "escalate_to_manager", "cross_department_notify", "schedule_meeting", - "create_document", "auto_approve", "reassign_task", "run_task", - ]), - actionConfigJson: z.string().default("{}"), - sourceDepartment: DEPARTMENT_IDS.nullable().optional(), - targetDepartment: DEPARTMENT_IDS.nullable().optional(), - priority: z.number().int().min(0).max(100).default(50), - status: z.enum(["active", "paused", "disabled", "error"]).default("active"), -}); - -export const toggleAutomationRuleSchema = z.object({ - id: z.string().uuid(), - companyId: z.string().uuid(), - status: z.enum(["active", "paused"]), -}); - -export const metricsQuerySchema = z.object({ - companyId: z.string().uuid(), - agentId: z.string().uuid().optional(), - days: z.number().int().min(1).max(90).default(30), -}); - -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(), - agentId: z.string().uuid().optional(), - limit: z.number().int().min(1).max(100).default(50), -}); - -export interface WakeupRequestRecord { - id: string; - companyId: string; - agentId: string; - source: string; - triggerDetail: string | null; - status: string; - coalescedCount: number; - runId: string | null; - requestedAt: string; - claimedAt: string | null; - finishedAt: string | null; - createdAt: string; - updatedAt: string; -} - -export const entityGetterSchema = z.object({ - companyId: z.string().uuid(), - id: z.string().uuid(), -}); - -export const listConfigRevisionsSchema = z.object({ - companyId: z.string().uuid(), - agentId: z.string().uuid(), -}); - -export const getConfigRevisionSchema = z.object({ - companyId: z.string().uuid(), - revisionId: z.string().uuid(), -}); - -export const rollbackConfigRevisionSchema = z.object({ - companyId: z.string().uuid(), - agentId: z.string().uuid(), - revisionId: z.string().uuid(), -}); - -export const listRunEventsSchema = z.object({ - companyId: z.string().min(1), - runId: z.string().min(1), - offset: z.number().optional(), - limit: z.number().optional(), -}); - -export const getAgentPermissionsSchema = z.object({ - agentId: z.string().min(1), -}); - -export const setAgentPermissionSchema = z.object({ - companyId: z.string().min(1), - agentId: z.string().min(1), - permissionKey: z.string().min(1), - enabled: z.boolean(), -}); - -export const heartbeatPolicySchema = z.object({ - companyId: z.string().min(1), - agentId: z.string().min(1), -}); - -export const setHeartbeatPolicySchema = z.object({ - companyId: z.string().min(1), - agentId: z.string().min(1), - enabled: z.boolean().optional(), - intervalSec: z.number().optional(), - cooldownSec: z.number().optional(), - maxConcurrentRuns: z.number().optional(), - wakeOnDemand: z.boolean().optional(), -}); - -export const runtimeStateSchema = z.object({ - companyId: z.string().min(1), - agentId: z.string().min(1), -}); - -export const setRuntimeStateSchema = z.object({ - companyId: z.string().min(1), - agentId: z.string().min(1), - state: z.record(z.string(), z.unknown()), -}); - -// ─── Thread Runtime Schemas ─────────────────────────────────────────────────── - -export const createThreadSchema = z.object({ - companyId: z.string().uuid(), - title: z.string().min(1).max(200), - type: z.enum(["task", "approval", "general", "announcement"]), - relatedEntityType: z.string().nullable().optional(), - relatedEntityId: z.string().nullable().optional(), -}); - -export const listThreadsSchema = z.object({ - companyId: z.string().uuid(), - type: z.enum(["task", "approval", "general", "announcement"]).optional(), - relatedEntityType: z.string().optional(), - relatedEntityId: z.string().optional(), - limit: z.number().int().min(1).max(100).default(50), - offset: z.number().int().min(0).default(0), -}); - -export const threadGetterSchema = z.object({ - companyId: z.string().uuid(), - threadId: z.string().uuid(), -}); - -export const addThreadEventSchema = z.object({ - companyId: z.string().uuid(), - threadId: z.string().uuid(), - actorId: z.string().min(1), - actorType: z.enum(["agent", "user", "system"]), - eventType: z.enum(["message", "status_change", "approval_request", "approval_decision", "file_attachment", "system_note"]), - body: z.string().default(""), - metadataJson: z.string().default("{}"), -}); - -export const listThreadEventsSchema = z.object({ - companyId: z.string().uuid(), - threadId: z.string().uuid(), - limit: z.number().int().min(1).max(100).default(50), - offset: z.number().int().min(0).default(0), -}); - -export const setThreadWaitStateSchema = z.object({ - companyId: z.string().uuid(), - threadId: z.string().uuid(), - waitingFor: z.enum(["user_review", "user_approval", "user_reply"]), - relatedApprovalId: z.string().nullable().optional(), - reason: z.string().min(1).max(500), -}); - -export const resolveThreadWaitStateSchema = z.object({ - companyId: z.string().uuid(), - waitStateId: z.string().uuid(), -}); - -export const listThreadWaitStatesSchema = z.object({ - companyId: z.string().uuid(), - resolvedOnly: z.boolean().default(false), -}); - -export const listParticipantsSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), -}); - -export const addParticipantSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), - participantId: z.string(), - participantType: z.enum(["agent", "user", "system"]), - role: z.enum(["owner", "member", "observer"]).default("member"), -}); - -export const removeParticipantSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), - participantId: z.string(), -}); - -export const subscribeThreadSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), - subscriberId: z.string(), - subscriberType: z.enum(["agent", "user", "system"]), - notifyOn: z.enum(["all", "mentions", "none"]).default("all"), -}); - -export const unsubscribeThreadSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), - subscriberId: z.string(), -}); - -export const listSubscriptionsSchema = z.object({ - threadId: z.string().uuid(), - companyId: z.string().uuid(), -}); - -export const listSubscribedThreadsSchema = z.object({ - subscriberId: z.string(), - companyId: z.string().uuid(), -}); +// Barrel re-export. Canonical domain files live alongside. +// New code should import directly from the domain file. +export * from "./schemas-badges"; +export * from "./schemas-automation"; +export * from "./schemas-budget"; +export * from "./schemas-wakeup"; +export * from "./schemas-config-revisions"; +export * from "./schemas-run-events"; +export * from "./schemas-agent-permissions"; +export * from "./schemas-heartbeat"; +export * from "./schemas-runtime-state"; +export * from "./schemas-threads"; +export * from "./schemas-participants"; +export * from "./schemas-subscriptions"; diff --git a/src/shared/contracts/schemas-participants.ts b/src/shared/contracts/schemas-participants.ts new file mode 100644 index 00000000..84ac1633 --- /dev/null +++ b/src/shared/contracts/schemas-participants.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; + +export const listParticipantsSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), +}); + +export const addParticipantSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), + participantId: z.string(), + participantType: z.enum(["agent", "user", "system"]), + role: z.enum(["owner", "member", "observer"]).default("member"), +}); + +export const removeParticipantSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), + participantId: z.string(), +}); diff --git a/src/shared/contracts/schemas-run-events.ts b/src/shared/contracts/schemas-run-events.ts new file mode 100644 index 00000000..c78a1861 --- /dev/null +++ b/src/shared/contracts/schemas-run-events.ts @@ -0,0 +1,8 @@ +import { z } from "zod"; + +export const listRunEventsSchema = z.object({ + companyId: z.string().min(1), + runId: z.string().min(1), + offset: z.number().optional(), + limit: z.number().optional(), +}); diff --git a/src/shared/contracts/schemas-runtime-state.ts b/src/shared/contracts/schemas-runtime-state.ts new file mode 100644 index 00000000..c6355a9f --- /dev/null +++ b/src/shared/contracts/schemas-runtime-state.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const runtimeStateSchema = z.object({ + companyId: z.string().min(1), + agentId: z.string().min(1), +}); + +export const setRuntimeStateSchema = z.object({ + companyId: z.string().min(1), + agentId: z.string().min(1), + state: z.record(z.string(), z.unknown()), +}); diff --git a/src/shared/contracts/schemas-subscriptions.ts b/src/shared/contracts/schemas-subscriptions.ts new file mode 100644 index 00000000..59ae2eb1 --- /dev/null +++ b/src/shared/contracts/schemas-subscriptions.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; + +export const subscribeThreadSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), + subscriberId: z.string(), + subscriberType: z.enum(["agent", "user", "system"]), + notifyOn: z.enum(["all", "mentions", "none"]).default("all"), +}); + +export const unsubscribeThreadSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), + subscriberId: z.string(), +}); + +export const listSubscriptionsSchema = z.object({ + threadId: z.string().uuid(), + companyId: z.string().uuid(), +}); + +export const listSubscribedThreadsSchema = z.object({ + subscriberId: z.string(), + companyId: z.string().uuid(), +}); diff --git a/src/shared/contracts/schemas-threads.ts b/src/shared/contracts/schemas-threads.ts new file mode 100644 index 00000000..7139b065 --- /dev/null +++ b/src/shared/contracts/schemas-threads.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; + +export const createThreadSchema = z.object({ + companyId: z.string().uuid(), + title: z.string().min(1).max(200), + type: z.enum(["task", "approval", "general", "announcement"]), + relatedEntityType: z.string().nullable().optional(), + relatedEntityId: z.string().nullable().optional(), +}); + +export const listThreadsSchema = z.object({ + companyId: z.string().uuid(), + type: z.enum(["task", "approval", "general", "announcement"]).optional(), + relatedEntityType: z.string().optional(), + relatedEntityId: z.string().optional(), + limit: z.number().int().min(1).max(100).default(50), + offset: z.number().int().min(0).default(0), +}); + +export const threadGetterSchema = z.object({ + companyId: z.string().uuid(), + threadId: z.string().uuid(), +}); + +export const addThreadEventSchema = z.object({ + companyId: z.string().uuid(), + threadId: z.string().uuid(), + actorId: z.string().min(1), + actorType: z.enum(["agent", "user", "system"]), + eventType: z.enum(["message", "status_change", "approval_request", "approval_decision", "file_attachment", "system_note"]), + body: z.string().default(""), + metadataJson: z.string().default("{}"), +}); + +export const listThreadEventsSchema = z.object({ + companyId: z.string().uuid(), + threadId: z.string().uuid(), + limit: z.number().int().min(1).max(100).default(50), + offset: z.number().int().min(0).default(0), +}); + +export const setThreadWaitStateSchema = z.object({ + companyId: z.string().uuid(), + threadId: z.string().uuid(), + waitingFor: z.enum(["user_review", "user_approval", "user_reply"]), + relatedApprovalId: z.string().nullable().optional(), + reason: z.string().min(1).max(500), +}); + +export const resolveThreadWaitStateSchema = z.object({ + companyId: z.string().uuid(), + waitStateId: z.string().uuid(), +}); + +export const listThreadWaitStatesSchema = z.object({ + companyId: z.string().uuid(), + resolvedOnly: z.boolean().default(false), +}); diff --git a/src/shared/contracts/schemas-wakeup.ts b/src/shared/contracts/schemas-wakeup.ts new file mode 100644 index 00000000..a16e2b8a --- /dev/null +++ b/src/shared/contracts/schemas-wakeup.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; + +export const wakeupQuerySchema = z.object({ + companyId: z.string().uuid(), + status: z.enum(["queued", "claimed", "coalesced", "deferred", "completed"]).optional(), + agentId: z.string().uuid().optional(), + limit: z.number().int().min(1).max(100).default(50), +}); + +export interface WakeupRequestRecord { + id: string; + companyId: string; + agentId: string; + source: string; + triggerDetail: string | null; + status: string; + coalescedCount: number; + runId: string | null; + requestedAt: string; + claimedAt: string | null; + finishedAt: string | null; + createdAt: string; + updatedAt: string; +}