feat(swads): add daily image report workflow
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { approvalRequestSchema, imageAttachmentSchema, llmApiSchema } from "./schemas.js";
|
||||
import { approvalRequestSchema, imageAttachmentSchema, llmApiSchema, reportArtifactSchema } from "./schemas.js";
|
||||
|
||||
const baseShape = {
|
||||
eventId: z.string(),
|
||||
@@ -22,8 +22,9 @@ export const harnessEventSchema = z.discriminatedUnion("type", [
|
||||
event("user.message", z.object({ messageId: z.string(), text: z.string(), attachmentIds: z.array(z.string()) }).strict()),
|
||||
event("assistant.delta", z.object({ messageId: z.string(), delta: z.string() }).strict()),
|
||||
event("assistant.completed", z.object({ messageId: z.string() }).strict()),
|
||||
event("skill.requested", z.object({ name: z.string(), filePath: z.string(), toolCallId: z.string() }).strict()),
|
||||
event("skill.loaded", z.object({ name: z.string(), filePath: z.string(), toolCallId: z.string() }).strict()),
|
||||
event("skill.requested", z.object({ name: z.string(), filePath: z.string(), toolCallId: z.string().optional(), source: z.enum(["read", "command"]) }).strict()),
|
||||
event("skill.loaded", z.object({ name: z.string(), filePath: z.string(), toolCallId: z.string().optional(), source: z.enum(["read", "command"]) }).strict()),
|
||||
event("report.generated", z.object({ assistantMessageId: z.string(), artifact: reportArtifactSchema }).strict()),
|
||||
event("tool.requested", toolBase),
|
||||
event("tool.completed", z.object({ toolCallId: z.string(), toolName: z.string(), result: z.unknown().optional() }).strict()),
|
||||
event("tool.failed", z.object({ toolCallId: z.string(), toolName: z.string(), error: z.string() }).strict()),
|
||||
|
||||
@@ -89,3 +89,17 @@ export const skillCatalogItemSchema = z.object({
|
||||
diagnostics: z.array(skillDiagnosticSchema),
|
||||
}).strict();
|
||||
export type SkillCatalogItem = z.infer<typeof skillCatalogItemSchema>;
|
||||
|
||||
export const reportSegmentsSchema = z.object({
|
||||
accountKey: z.string().regex(/^acct_[a-f0-9]{24}$/),
|
||||
reportDate: z.iso.date(),
|
||||
reportId: z.string().uuid(),
|
||||
}).strict();
|
||||
export const reportFormatSchema = z.enum(["png", "html", "json"]);
|
||||
const reportUrl = z.string().regex(/^\/api\/reports\/acct_[a-f0-9]{24}\/\d{4}-\d{2}-\d{2}\/[a-f0-9-]{36}\/(png|html|json)$/);
|
||||
export const reportArtifactSchema = reportSegmentsSchema.extend({
|
||||
previewUrl: reportUrl.optional(),
|
||||
downloads: z.object({ json: reportUrl, html: reportUrl, png: reportUrl.optional() }).strict(),
|
||||
warning: z.object({ code: z.enum(["CHROME_UNAVAILABLE", "PNG_RENDER_FAILED", "PNG_TIMEOUT", "PNG_TRUNCATED"]), message: z.string().max(300) }).strict().optional(),
|
||||
}).strict();
|
||||
export type ReportArtifact = z.infer<typeof reportArtifactSchema>;
|
||||
|
||||
@@ -31,3 +31,13 @@ describe("shared boundary schemas", () => {
|
||||
expect(apiErrorSchema.parse({ error: { code: "SESSION_NOT_FOUND", message: "Session not found", requestId: "req_1", details: {} } }).error.code).toBe("SESSION_NOT_FOUND");
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts report metadata and rejects paths and malformed URL formats", () => {
|
||||
const base = { eventId: "report", sessionId: "s", timestamp: new Date().toISOString(), sequence: 1, type: "report.generated" };
|
||||
const accountKey = "acct_0123456789abcdef01234567", reportDate = "2026-09-06", reportId = "12345678-1234-4234-8234-123456789abc";
|
||||
const url = `/api/reports/${accountKey}/${reportDate}/${reportId}`;
|
||||
const artifact = { accountKey, reportDate, reportId, downloads: { json: `${url}/json`, html: `${url}/html` } };
|
||||
expect(harnessEventSchema.safeParse({ ...base, payload: { assistantMessageId: "m", artifact } }).success).toBe(true);
|
||||
expect(harnessEventSchema.safeParse({ ...base, payload: { assistantMessageId: "m", artifact: { ...artifact, absolutePath: "/private/report" } } }).success).toBe(false);
|
||||
expect(harnessEventSchema.safeParse({ ...base, payload: { assistantMessageId: "m", artifact: { ...artifact, previewUrl: "https://evil.test/report.png" } } }).success).toBe(false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user