fix(data): canonicalize operation json fields

This commit is contained in:
ZacharyZhang-NY committed 2026-05-15 02:36:19 -04:00
1 parent 10c1f6d103
commit b92cf62c4c
10 files changed
+246 -21

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
export * from "./contracts/channels";
export * from "./contracts/helpers";
export * from "./contracts/json-fields";
export * from "./contracts/schemas-company";
export * from "./contracts/schemas-agents";
export * from "./contracts/schemas-tasks";
+80
View File
@@ -0,0 +1,80 @@
import { z } from "zod";
export type JsonValue =
| null
| boolean
| number
| string
| JsonValue[]
| { [key: string]: JsonValue };
export const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() => z.union([
z.null(),
z.boolean(),
z.number(),
z.string(),
z.array(jsonValueSchema),
z.record(z.string(), jsonValueSchema),
]));
export const jsonStringArraySchema = z.array(z.string());
export const jsonArraySchema = z.array(jsonValueSchema);
export const meetingActionItemArraySchema = z.array(z.object({
title: z.string().optional(),
assigneeAgentId: z.string().optional(),
description: z.string().optional(),
}).catchall(jsonValueSchema));
export function canonicalizeJsonString<T>(value: string | null | undefined, schema: z.ZodType<T>, fallback: T): string {
const raw = value ?? JSON.stringify(fallback);
const parsed = JSON.parse(raw);
return JSON.stringify(schema.parse(parsed));
}
export function canonicalJsonString<T>(schema: z.ZodType<T>, fallback: T) {
const fallbackJson = JSON.stringify(fallback);
return z.string().default(fallbackJson).superRefine((value, ctx) => {
let parsed: unknown;
try {
parsed = JSON.parse(value);
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Must be valid JSON.",
});
return;
}
const result = schema.safeParse(parsed);
if (!result.success) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "JSON value does not match the expected shape.",
});
}
}).transform((value) => canonicalizeJsonString(value, schema, fallback));
}
export function canonicalOptionalJsonString<T>(schema: z.ZodType<T>) {
return z.string().superRefine((value, ctx) => {
let parsed: unknown;
try {
parsed = JSON.parse(value);
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Must be valid JSON.",
});
return;
}
const result = schema.safeParse(parsed);
if (!result.success) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "JSON value does not match the expected shape.",
});
}
}).transform((value) => JSON.stringify(schema.parse(JSON.parse(value))));
}
+8 -7
View File
@@ -1,4 +1,5 @@
import { z } from "zod";
import { canonicalJsonString, jsonArraySchema, jsonStringArraySchema, meetingActionItemArraySchema } from "./json-fields";
import { MESSAGE_CHANNELS, optionalId } from "./helpers";
export const agentMessageInputSchema = z.object({
@@ -86,7 +87,7 @@ export const documentInputSchema = z.object({
parentDocId: optionalId,
version: z.number().int().min(1).default(1),
status: z.enum(["draft", "in_review", "approved", "archived", "superseded"]).default("draft"),
tagsJson: z.string().default("[]"),
tagsJson: canonicalJsonString(jsonStringArraySchema, []),
});
export const knowledgeEntryInputSchema = z.object({
@@ -97,7 +98,7 @@ export const knowledgeEntryInputSchema = z.object({
content: z.string().min(1),
authorAgentId: optionalId,
importance: z.enum(["critical", "high", "medium", "low"]).default("medium"),
tagsJson: z.string().default("[]"),
tagsJson: canonicalJsonString(jsonStringArraySchema, []),
referencedEntityType: z.enum([
"agents",
"goals",
@@ -122,13 +123,13 @@ export const meetingInputSchema = z.object({
type: z.enum(["standup", "sprint_planning", "sprint_review", "retrospective", "one_on_one", "all_hands", "department_sync", "incident_review", "hiring_committee", "budget_review", "design_review", "architecture_review"]),
title: z.string().min(2).max(160),
organizerAgentId: optionalId,
participantAgentIds: z.string().default("[]"),
participantAgentIds: canonicalJsonString(jsonStringArraySchema, []),
scheduledAt: z.string(),
durationMinutes: z.number().int().min(5).max(480).default(30),
agendaJson: z.string().default("[]"),
notesJson: z.string().default("[]"),
decisionsJson: z.string().default("[]"),
actionItemsJson: z.string().default("[]"),
agendaJson: canonicalJsonString(jsonArraySchema, []),
notesJson: canonicalJsonString(jsonArraySchema, []),
decisionsJson: canonicalJsonString(jsonArraySchema, []),
actionItemsJson: canonicalJsonString(meetingActionItemArraySchema, []),
status: z.enum(["scheduled", "in_progress", "completed", "cancelled"]).default("scheduled"),
});