mirror of
https://github.com/vee1e/workorder-desk.git
synced 2026-09-01 09:50:13 +00:00
feat(shared): agentic AI types, error codes, tool and SSE schemas
Extend the closed error catalog with the AI error codes (AI_UNAVAILABLE, AI_BUDGET_EXCEEDED, approval lifecycle codes, AI_INJECTION_BLOCKED), add the AgentRun/AgentToolCall/AgentConfig/CopilotSession/TriageSuggestion DTOs, add aiEnabled to UserAdmin, and add the LLM-facing tool arg schemas plus the zod-validated SSE event schema. Owner/version stay out of tool args per the agentic spec (the runtime injects them).
This commit is contained in:
parent
96f960e0c9
commit
3f3fdf8a85
2 changed files with 207 additions and 1 deletions
|
|
@ -103,6 +103,12 @@ export const updateStatusSchema = z
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
export const updateAiSchema = z
|
||||||
|
.object({
|
||||||
|
aiEnabled: z.boolean(),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
// ── Query params ─────────────────────────────────────────────────────
|
// ── Query params ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
export const cursorQuerySchema = z.object({
|
export const cursorQuerySchema = z.object({
|
||||||
|
|
@ -120,6 +126,95 @@ export const offsetQuerySchema = z.object({
|
||||||
search: z.string().trim().max(64).optional(),
|
search: z.string().trim().max(64).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Agentic AI ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// LLM-facing tool arg schemas. `version` and `owner` are intentionally absent:
|
||||||
|
// the runtime injects them (see agent spec §4.3).
|
||||||
|
|
||||||
|
export const aiListWorkOrdersSchema = z
|
||||||
|
.object({
|
||||||
|
status: workOrderStatusSchema.optional(),
|
||||||
|
priority: workOrderPrioritySchema.optional(),
|
||||||
|
search: z.string().trim().max(64).optional(),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const aiGetWorkOrderSchema = z
|
||||||
|
.object({
|
||||||
|
id: z.string().min(1),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const aiCreateWorkOrderSchema = createWorkOrderSchema;
|
||||||
|
|
||||||
|
export const aiUpdateWorkOrderSchema = z
|
||||||
|
.object({
|
||||||
|
id: z.string().min(1),
|
||||||
|
title: z.string().trim().min(3).max(100).optional(),
|
||||||
|
description: z.string().trim().max(2000).nullable().optional(),
|
||||||
|
priority: workOrderPrioritySchema.optional(),
|
||||||
|
status: workOrderStatusSchema.optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
.refine((v) => v.title !== undefined || v.description !== undefined || v.priority !== undefined || v.status !== undefined, {
|
||||||
|
message: 'at least one field to update is required',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const aiDeleteWorkOrderSchema = z
|
||||||
|
.object({
|
||||||
|
id: z.string().min(1),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const triageProposalSchema = z
|
||||||
|
.object({
|
||||||
|
summary: z.string().trim().min(1).max(200),
|
||||||
|
suggestedPriority: workOrderPrioritySchema,
|
||||||
|
flagForDispatcher: z.boolean(),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const sendCopilotMessageSchema = z
|
||||||
|
.object({
|
||||||
|
content: z.string().trim().min(1).max(4000),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
// ── SSE event payloads (agent spec §4.8) ──────────────────────────────────
|
||||||
|
|
||||||
|
export const sseEventSchema = z.discriminatedUnion('event', [
|
||||||
|
z.object({ event: z.literal('token'), content: z.string() }),
|
||||||
|
z.object({ event: z.literal('tool_call_start'), toolCallId: z.string(), tool: z.string(), args: z.unknown() }),
|
||||||
|
z.object({
|
||||||
|
event: z.literal('tool_approval_required'),
|
||||||
|
toolCallId: z.string(),
|
||||||
|
tool: z.string(),
|
||||||
|
args: z.unknown(),
|
||||||
|
preImage: z.unknown().nullable(),
|
||||||
|
afterDiff: z.unknown(),
|
||||||
|
summary: z.string(),
|
||||||
|
expiresAt: z.string(),
|
||||||
|
}),
|
||||||
|
z.object({ event: z.literal('tool_approval_expired'), toolCallId: z.string() }),
|
||||||
|
z.object({
|
||||||
|
event: z.literal('tool_result'),
|
||||||
|
toolCallId: z.string(),
|
||||||
|
outcome: z.string(),
|
||||||
|
result: z.unknown().optional(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
event: z.literal('message_done'),
|
||||||
|
runId: z.string(),
|
||||||
|
content: z.string(),
|
||||||
|
inputTokens: z.number().int().nonnegative(),
|
||||||
|
outputTokens: z.number().int().nonnegative(),
|
||||||
|
}),
|
||||||
|
z.object({ event: z.literal('error'), code: z.string(), message: z.string(), requestId: z.string() }),
|
||||||
|
z.object({ event: z.literal('ping'), ts: z.string() }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type SseEvent = z.infer<typeof sseEventSchema>;
|
||||||
|
|
||||||
// ── Inferred input types ─────────────────────────────────────────────
|
// ── Inferred input types ─────────────────────────────────────────────
|
||||||
|
|
||||||
export type RegisterInput = z.infer<typeof registerSchema>;
|
export type RegisterInput = z.infer<typeof registerSchema>;
|
||||||
|
|
@ -133,5 +228,13 @@ export type UpdateProfileInput = z.infer<typeof updateProfileSchema>;
|
||||||
export type ChangePasswordInput = z.infer<typeof changePasswordSchema>;
|
export type ChangePasswordInput = z.infer<typeof changePasswordSchema>;
|
||||||
export type UpdateRoleInput = z.infer<typeof updateRoleSchema>;
|
export type UpdateRoleInput = z.infer<typeof updateRoleSchema>;
|
||||||
export type UpdateStatusInput = z.infer<typeof updateStatusSchema>;
|
export type UpdateStatusInput = z.infer<typeof updateStatusSchema>;
|
||||||
|
export type UpdateAiInput = z.infer<typeof updateAiSchema>;
|
||||||
export type CursorQuery = z.infer<typeof cursorQuerySchema>;
|
export type CursorQuery = z.infer<typeof cursorQuerySchema>;
|
||||||
export type OffsetQuery = z.infer<typeof offsetQuerySchema>;
|
export type OffsetQuery = z.infer<typeof offsetQuerySchema>;
|
||||||
|
export type AiListWorkOrdersInput = z.infer<typeof aiListWorkOrdersSchema>;
|
||||||
|
export type AiGetWorkOrderInput = z.infer<typeof aiGetWorkOrderSchema>;
|
||||||
|
export type AiCreateWorkOrderInput = z.infer<typeof aiCreateWorkOrderSchema>;
|
||||||
|
export type AiUpdateWorkOrderInput = z.infer<typeof aiUpdateWorkOrderSchema>;
|
||||||
|
export type AiDeleteWorkOrderInput = z.infer<typeof aiDeleteWorkOrderSchema>;
|
||||||
|
export type TriageProposal = z.infer<typeof triageProposalSchema>;
|
||||||
|
export type SendCopilotMessageInput = z.infer<typeof sendCopilotMessageSchema>;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export interface UserPublic {
|
||||||
export interface UserAdmin extends UserPublic {
|
export interface UserAdmin extends UserPublic {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
lastLoginAt: string | null;
|
lastLoginAt: string | null;
|
||||||
|
aiEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkOrderPublic {
|
export interface WorkOrderPublic {
|
||||||
|
|
@ -53,7 +54,15 @@ export type ErrorCode =
|
||||||
| 'AUTH_GENERIC'
|
| 'AUTH_GENERIC'
|
||||||
| 'EMAIL_TAKEN'
|
| 'EMAIL_TAKEN'
|
||||||
| 'REFRESH_REUSE'
|
| 'REFRESH_REUSE'
|
||||||
| 'INTERNAL';
|
| 'INTERNAL'
|
||||||
|
| 'AI_UNAVAILABLE'
|
||||||
|
| 'AI_BUDGET_EXCEEDED'
|
||||||
|
| 'AI_APPROVAL_PENDING'
|
||||||
|
| 'AI_APPROVAL_RESOLVED'
|
||||||
|
| 'AI_APPROVAL_STALE'
|
||||||
|
| 'AI_APPROVAL_EXPIRED'
|
||||||
|
| 'AI_MESSAGE_DUPLICATE'
|
||||||
|
| 'AI_INJECTION_BLOCKED';
|
||||||
|
|
||||||
export interface ApiErrorBody {
|
export interface ApiErrorBody {
|
||||||
code: ErrorCode;
|
code: ErrorCode;
|
||||||
|
|
@ -97,3 +106,97 @@ export const REFRESH_TOKEN_TTL_SECONDS = 604800;
|
||||||
|
|
||||||
export const APP_ISS = 'workorders';
|
export const APP_ISS = 'workorders';
|
||||||
export const APP_AUD = 'workorders-api';
|
export const APP_AUD = 'workorders-api';
|
||||||
|
|
||||||
|
// ── Agentic AI ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type CopilotSessionStatus = 'active' | 'archived' | 'expired';
|
||||||
|
|
||||||
|
export interface CopilotSession {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
status: CopilotSessionStatus;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AgentRunMode = 'copilot' | 'autonomous';
|
||||||
|
|
||||||
|
export type AgentRunStatus = 'running' | 'complete' | 'error' | 'budget_exceeded' | 'expired' | 'aborted';
|
||||||
|
|
||||||
|
export interface AgentRun {
|
||||||
|
id: string;
|
||||||
|
mode: AgentRunMode;
|
||||||
|
actorId: string | null;
|
||||||
|
agentName?: string;
|
||||||
|
status: AgentRunStatus;
|
||||||
|
model: string;
|
||||||
|
inputTokens: number;
|
||||||
|
outputTokens: number;
|
||||||
|
startedAt: string;
|
||||||
|
finishedAt: string | null;
|
||||||
|
errorCode?: ErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AgentToolOutcome =
|
||||||
|
| 'executed'
|
||||||
|
| 'approved'
|
||||||
|
| 'rejected'
|
||||||
|
| 'expired'
|
||||||
|
| 'stale'
|
||||||
|
| 'error'
|
||||||
|
| 'blocked'
|
||||||
|
| 'aborted';
|
||||||
|
|
||||||
|
export type AgentApprovalStatus = 'pending' | 'approved' | 'rejected' | 'expired' | 'stale';
|
||||||
|
|
||||||
|
export interface AgentApproval {
|
||||||
|
status: AgentApprovalStatus;
|
||||||
|
summary: string;
|
||||||
|
expiresAt: string;
|
||||||
|
decidedBy?: string;
|
||||||
|
decidedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentToolCall {
|
||||||
|
id: string;
|
||||||
|
runId: string;
|
||||||
|
tool: string;
|
||||||
|
args: unknown;
|
||||||
|
outcome: AgentToolOutcome;
|
||||||
|
result?: unknown;
|
||||||
|
latencyMs: number;
|
||||||
|
createdAt: string;
|
||||||
|
stagedVersion?: number;
|
||||||
|
executedVersion?: number;
|
||||||
|
preImage?: unknown;
|
||||||
|
approval?: AgentApproval;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentToolCallPublic extends AgentToolCall {
|
||||||
|
result?: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TriageMode = 'suggest' | 'auto-apply';
|
||||||
|
|
||||||
|
export interface AgentConfig {
|
||||||
|
name: string;
|
||||||
|
enabled: boolean;
|
||||||
|
mode: TriageMode;
|
||||||
|
allowedFields: string[];
|
||||||
|
dailyActionCap: number;
|
||||||
|
flagThreshold: WorkOrderPriority;
|
||||||
|
workingHours: string;
|
||||||
|
updatedBy: string | null;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TriageSuggestion {
|
||||||
|
id: string;
|
||||||
|
workOrderId: string;
|
||||||
|
runId: string;
|
||||||
|
summary: string;
|
||||||
|
suggestedPriority: WorkOrderPriority;
|
||||||
|
flagForDispatcher: boolean;
|
||||||
|
applied: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue