Skip to content

Commit f27061c

Browse files
committed
feat: enhance schemas with stricter typing and validation for timestamps and metadata
1 parent 7c98777 commit f27061c

File tree

9 files changed

+179
-72
lines changed

9 files changed

+179
-72
lines changed

packages/spec/src/ai/agent-action.zod.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,58 @@ export const AgentActionSchema = z.object({
308308
intent: z.string().optional().describe('Original user intent/query'),
309309
confidence: z.number().min(0).max(1).optional().describe('Confidence score (0-1)'),
310310
agentName: z.string().optional().describe('Agent that generated this action'),
311-
timestamp: z.string().optional().describe('Generation timestamp (ISO 8601)'),
311+
timestamp: z.string().datetime().optional().describe('Generation timestamp (ISO 8601)'),
312312
}).optional(),
313313
});
314314

315-
export type AgentAction = z.infer<typeof AgentActionSchema>;
315+
/**
316+
* Agent Action Typed Schemas
317+
* Bind params to specific action types for type safety
318+
*/
319+
export const NavigationAgentActionSchema = AgentActionSchema.extend({
320+
type: NavigationActionTypeSchema,
321+
params: NavigationActionParamsSchema,
322+
});
323+
324+
export const ViewAgentActionSchema = AgentActionSchema.extend({
325+
type: ViewActionTypeSchema,
326+
params: ViewActionParamsSchema,
327+
});
328+
329+
export const FormAgentActionSchema = AgentActionSchema.extend({
330+
type: FormActionTypeSchema,
331+
params: FormActionParamsSchema,
332+
});
333+
334+
export const DataAgentActionSchema = AgentActionSchema.extend({
335+
type: DataActionTypeSchema,
336+
params: DataActionParamsSchema,
337+
});
338+
339+
export const WorkflowAgentActionSchema = AgentActionSchema.extend({
340+
type: WorkflowActionTypeSchema,
341+
params: WorkflowActionParamsSchema,
342+
});
343+
344+
export const ComponentAgentActionSchema = AgentActionSchema.extend({
345+
type: ComponentActionTypeSchema,
346+
params: ComponentActionParamsSchema,
347+
});
348+
349+
/**
350+
* Typed Agent Action Union
351+
* Replaces the generic AgentActionSchema for stricter typing where possible
352+
*/
353+
export const TypedAgentActionSchema = z.union([
354+
NavigationAgentActionSchema,
355+
ViewAgentActionSchema,
356+
FormAgentActionSchema,
357+
DataAgentActionSchema,
358+
WorkflowAgentActionSchema,
359+
ComponentAgentActionSchema,
360+
]);
361+
362+
export type AgentAction = z.infer<typeof TypedAgentActionSchema>;
316363

317364
/**
318365
* Agent Action Sequence Schema
@@ -342,8 +389,8 @@ export const AgentActionSequenceSchema = z.object({
342389
/**
343390
* Transaction mode (all-or-nothing)
344391
*/
345-
atomic: z.boolean().default(false).describe('Rollback all changes if any action fails'),
346-
392+
atomic: z.boolean().defadatetime().optional().describe('Execution start time (ISO 8601)'),
393+
endTime: z.string().datetime
347394
/**
348395
* Metadata
349396
*/

packages/spec/src/ai/agent.zod.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export const AgentSchema = z.object({
5252
/** Interface */
5353
active: z.boolean().default(true),
5454
access: z.array(z.string()).optional().describe('Who can chat with this agent'),
55+
56+
/** Multi-tenancy & Visibility */
57+
tenantId: z.string().optional().describe('Tenant/Organization ID'),
58+
visibility: z.enum(['global', 'organization', 'private']).default('organization'),
5559
});
5660

5761
export type Agent = z.infer<typeof AgentSchema>;

packages/spec/src/ai/conversation.zod.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,43 @@ export const MessageContentTypeSchema = z.enum([
3131
]);
3232

3333
/**
34-
* Message Content
34+
* Message Content - Discriminated Union
3535
*/
36-
export const MessageContentSchema = z.object({
37-
type: MessageContentTypeSchema.default('text'),
38-
text: z.string().optional().describe('Text content'),
39-
imageUrl: z.string().url().optional().describe('Image URL for vision models'),
40-
fileUrl: z.string().url().optional().describe('File attachment URL'),
41-
mimeType: z.string().optional().describe('MIME type for files'),
42-
metadata: z.record(z.string(), z.any()).optional().describe('Additional metadata'),
36+
export const TextContentSchema = z.object({
37+
type: z.literal('text'),
38+
text: z.string().describe('Text content'),
39+
metadata: z.record(z.string(), z.any()).optional(),
40+
});
41+
42+
export const ImageContentSchema = z.object({
43+
type: z.literal('image'),
44+
imageUrl: z.string().url().describe('Image URL'),
45+
detail: z.enum(['low', 'high', 'auto']).optional().default('auto'),
46+
metadata: z.record(z.string(), z.any()).optional(),
47+
});
48+
49+
export const FileContentSchema = z.object({
50+
type: z.literal('file'),
51+
fileUrl: z.string().url().describe('File attachment URL'),
52+
mimeType: z.string().describe('MIME type'),
53+
fileName: z.string().optional(),
54+
metadata: z.record(z.string(), z.any()).optional(),
55+
});
56+
57+
export const CodeContentSchema = z.object({
58+
type: z.literal('code'),
59+
text: z.string().describe('Code snippet'),
60+
language: z.string().optional().default('text'),
61+
metadata: z.record(z.string(), z.any()).optional(),
4362
});
4463

64+
export const MessageContentSchema = z.union([
65+
TextContentSchema,
66+
ImageContentSchema,
67+
FileContentSchema,
68+
CodeContentSchema
69+
]);
70+
4571
/**
4672
* Function Call
4773
*/
@@ -66,14 +92,11 @@ export const ToolCallSchema = z.object({
6692
export const ConversationMessageSchema = z.object({
6793
/** Identity */
6894
id: z.string().describe('Unique message ID'),
69-
timestamp: z.string().describe('ISO 8601 timestamp'),
95+
timestamp: z.string().datetime().describe('ISO 8601 timestamp'),
7096

7197
/** Content */
7298
role: MessageRoleSchema,
73-
content: z.union([
74-
z.string(),
75-
z.array(MessageContentSchema),
76-
]).describe('Message content (text or multimodal)'),
99+
content: z.array(MessageContentSchema).describe('Message content (multimodal array)'),
77100

78101
/** Function/Tool Calls */
79102
functionCall: FunctionCallSchema.optional().describe('Legacy function call'),
@@ -204,9 +227,9 @@ export const ConversationSessionSchema = z.object({
204227
createdAt: z.string().describe('ISO 8601 timestamp'),
205228
updatedAt: z.string().describe('ISO 8601 timestamp'),
206229
expiresAt: z.string().optional().describe('ISO 8601 timestamp'),
207-
208-
/** Metadata */
209-
tags: z.array(z.string()).optional(),
230+
atetime().describe('ISO 8601 timestamp'),
231+
updatedAt: z.string().datetime().describe('ISO 8601 timestamp'),
232+
expiresAt: z.string().datetimeing()).optional(),
210233
metadata: z.record(z.string(), z.any()).optional(),
211234
});
212235

@@ -233,16 +256,16 @@ export const ConversationSummarySchema = z.object({
233256
generatedAt: z.string().describe('ISO 8601 timestamp'),
234257
modelId: z.string().optional().describe('Model used for summarization'),
235258
});
259+
atetime().describe('ISO 8601 timestamp'),
260+
modelId: z.string().optional().describe('Model used for summarization'),
261+
});
236262

237263
/**
238264
* Message Pruning Event
239265
*/
240266
export const MessagePruningEventSchema = z.object({
241267
/** Event Details */
242-
timestamp: z.string().describe('ISO 8601 timestamp'),
243-
strategy: TokenBudgetStrategySchema,
244-
reason: z.string().describe('Reason for pruning'),
245-
268+
timestamp: z.string().datetime
246269
/** Pruned Messages */
247270
prunedMessages: z.array(z.object({
248271
messageId: z.string(),
@@ -289,8 +312,8 @@ export const ConversationAnalyticsSchema = z.object({
289312
firstMessageAt: z.string().optional().describe('ISO 8601 timestamp'),
290313
lastMessageAt: z.string().optional().describe('ISO 8601 timestamp'),
291314
});
292-
293-
// Type exports
315+
datetime().optional().describe('ISO 8601 timestamp'),
316+
lastMessageAt: z.string().datetime
294317
export type MessageRole = z.infer<typeof MessageRoleSchema>;
295318
export type MessageContentType = z.infer<typeof MessageContentTypeSchema>;
296319
export type MessageContent = z.infer<typeof MessageContentSchema>;

packages/spec/src/ai/cost.zod.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,15 @@ export const BillingPeriodSchema = z.enum([
6868
export const CostEntrySchema = z.object({
6969
/** Identity */
7070
id: z.string().describe('Unique cost entry ID'),
71-
timestamp: z.string().describe('ISO 8601 timestamp'),
71+
timestamp: z.string().datetime().describe('ISO 8601 timestamp'),
7272

7373
/** Request Details */
7474
modelId: z.string().describe('AI model used'),
7575
provider: z.string().describe('AI provider (e.g., "openai", "anthropic")'),
7676
operation: z.string().describe('Operation type (e.g., "chat_completion", "embedding")'),
7777

78-
/** Usage Metrics */
79-
promptTokens: z.number().int().nonnegative().optional(),
80-
completionTokens: z.number().int().nonnegative().optional(),
81-
totalTokens: z.number().int().nonnegative().optional(),
78+
/** Usage Metrics - Standardized */
79+
tokens: TokenUsageSchema.optional().describe('Standardized token usage'),
8280
requestCount: z.number().int().positive().default(1),
8381

8482
/** Cost Calculation */
@@ -156,8 +154,8 @@ export const BudgetStatusSchema = z.object({
156154
scope: z.string().optional(),
157155

158156
/** Current Period */
159-
periodStart: z.string().describe('ISO 8601 timestamp'),
160-
periodEnd: z.string().describe('ISO 8601 timestamp'),
157+
periodStart: z.string().datetime().describe('ISO 8601 timestamp'),
158+
periodEnd: z.string().datetime().describe('ISO 8601 timestamp'),
161159

162160
/** Usage */
163161
currentCost: z.number().nonnegative().default(0),
@@ -175,7 +173,7 @@ export const BudgetStatusSchema = z.object({
175173
projectedOverage: z.number().nonnegative().optional().describe('Projected overage'),
176174

177175
/** Last Update */
178-
lastUpdated: z.string().describe('ISO 8601 timestamp'),
176+
lastUpdated: z.string().datetime().describe('ISO 8601 timestamp'),
179177
});
180178

181179
/**
@@ -195,7 +193,7 @@ export const CostAlertTypeSchema = z.enum([
195193
export const CostAlertSchema = z.object({
196194
/** Alert Details */
197195
id: z.string(),
198-
timestamp: z.string().describe('ISO 8601 timestamp'),
196+
timestamp: z.string().datetime().describe('ISO 8601 timestamp'),
199197
type: CostAlertTypeSchema,
200198
severity: z.enum(['info', 'warning', 'critical']),
201199

@@ -217,7 +215,7 @@ export const CostAlertSchema = z.object({
217215
/** Status */
218216
acknowledged: z.boolean().default(false),
219217
acknowledgedBy: z.string().optional(),
220-
acknowledgedAt: z.string().optional(),
218+
acknowledgedAt: z.string().datetime().optional(),
221219
resolved: z.boolean().default(false),
222220

223221
/** Metadata */
@@ -255,17 +253,17 @@ export const CostBreakdownEntrySchema = z.object({
255253
percentageOfTotal: z.number().min(0).max(1),
256254

257255
/** Time Range */
258-
periodStart: z.string().optional(),
259-
periodEnd: z.string().optional(),
256+
periodStart: z.string().datetime().optional(),
257+
periodEnd: z.string().datetime().optional(),
260258
});
261259

262260
/**
263261
* Cost Analytics
264262
*/
265263
export const CostAnalyticsSchema = z.object({
266264
/** Time Range */
267-
periodStart: z.string().describe('ISO 8601 timestamp'),
268-
periodEnd: z.string().describe('ISO 8601 timestamp'),
265+
periodStart: z.string().datetime().describe('ISO 8601 timestamp'),
266+
periodEnd: z.string().datetime().describe('ISO 8601 timestamp'),
269267

270268
/** Summary Metrics */
271269
totalCost: z.number().nonnegative(),
@@ -334,7 +332,7 @@ export const CostOptimizationRecommendationSchema = z.object({
334332

335333
/** Status */
336334
status: z.enum(['pending', 'accepted', 'rejected', 'implemented']).default('pending'),
337-
implementedAt: z.string().optional(),
335+
implementedAt: z.string().datetime().optional(),
338336
});
339337

340338
/**
@@ -344,11 +342,11 @@ export const CostReportSchema = z.object({
344342
/** Report Metadata */
345343
id: z.string(),
346344
name: z.string(),
347-
generatedAt: z.string().describe('ISO 8601 timestamp'),
345+
generatedAt: z.string().datetime().describe('ISO 8601 timestamp'),
348346

349347
/** Time Range */
350-
periodStart: z.string().describe('ISO 8601 timestamp'),
351-
periodEnd: z.string().describe('ISO 8601 timestamp'),
348+
periodStart: z.string().datetime().describe('ISO 8601 timestamp'),
349+
periodEnd: z.string().datetime().describe('ISO 8601 timestamp'),
352350
period: BillingPeriodSchema,
353351

354352
/** Analytics */
@@ -383,8 +381,8 @@ export const CostReportSchema = z.object({
383381
*/
384382
export const CostQueryFiltersSchema = z.object({
385383
/** Time Range */
386-
startDate: z.string().optional().describe('ISO 8601 timestamp'),
387-
endDate: z.string().optional().describe('ISO 8601 timestamp'),
384+
startDate: z.string().datetime().optional().describe('ISO 8601 timestamp'),
385+
endDate: z.string().datetime().optional().describe('ISO 8601 timestamp'),
388386

389387
/** Dimensions */
390388
modelIds: z.array(z.string()).optional(),

packages/spec/src/ai/model-registry.zod.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ export const ModelConfigSchema = z.object({
7676

7777
/** Configuration */
7878
endpoint: z.string().url().optional().describe('Custom API endpoint'),
79-
apiKey: z.string().optional().describe('API key or reference to secret'),
79+
apiKey: z.string().optional().describe('API key (Warning: Prefer secretRef)'),
80+
secretRef: z.string().optional().describe('Reference to stored secret (e.g. system:openai_api_key)'),
8081
region: z.string().optional().describe('Deployment region (e.g., "us-east-1")'),
8182

8283
/** Metadata */

packages/spec/src/ai/nlq.zod.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const NLQParseResultSchema = z.object({
109109
timeframe: TimeframeSchema.optional(),
110110

111111
/** Query AST */
112-
ast: z.any().describe('Generated ObjectQL AST'),
112+
ast: z.record(z.string(), z.any()).describe('Generated ObjectQL AST'),
113113

114114
/** Metadata */
115115
confidence: z.number().min(0).max(1).describe('Overall confidence'),
@@ -181,7 +181,7 @@ export const NLQTrainingExampleSchema = z.object({
181181
/** Expected Output */
182182
expectedIntent: QueryIntentSchema,
183183
expectedObject: z.string().optional(),
184-
expectedAST: z.any().describe('Expected ObjectQL AST'),
184+
expectedAST: z.record(z.string(), z.any()).describe('Expected ObjectQL AST'),
185185

186186
/** Metadata */
187187
category: z.string().optional().describe('Example category'),
@@ -250,12 +250,12 @@ export const NLQAnalyticsSchema = z.object({
250250
lowConfidenceQueries: z.array(z.object({
251251
query: z.string(),
252252
confidence: z.number(),
253-
timestamp: z.string(),
253+
timestamp: z.string().datetime(),
254254
})),
255255

256256
/** Timeframe */
257-
startDate: z.string().describe('ISO timestamp'),
258-
endDate: z.string().describe('ISO timestamp'),
257+
startDate: z.string().datetime().describe('ISO timestamp'),
258+
endDate: z.string().datetime().describe('ISO timestamp'),
259259
});
260260

261261
/**
@@ -285,7 +285,7 @@ export const QueryTemplateSchema = z.object({
285285
})),
286286

287287
/** Generated AST */
288-
astTemplate: z.any().describe('AST template with variable placeholders'),
288+
astTemplate: z.record(z.string(), z.any()).describe('AST template with variable placeholders'),
289289

290290
/** Metadata */
291291
category: z.string().optional(),

0 commit comments

Comments
 (0)