import { z } from "zod"; // Project only the fields displayed below. Never stringify the arbitrary script // return value or workspace content from a persisted tool message. const display = z.object({ kind: z.literal("skill-script"), skill: z.object({ source: z.enum(["bundled", "installed"]), name: z.string().min(1).max(64), version: z.string().min(1).max(64), fingerprint: z.string().regex(/^[a-f0-9]{64}$/), }), path: z.string().min(1).max(240), result: z.object({ ok: z.boolean(), stdout: z.string().max(8192), stderr: z.string().max(8192), exitCode: z.union([z.literal(0), z.literal(1)]), truncated: z.boolean(), error: z.object({ message: z.string().max(240) }).optional(), }), workspace: z.object({ files: z.array(z.object({ path: z.string().min(1).max(240) })).max(8), truncated: z.boolean(), }), }); export function skillScriptOutput(value: unknown) { const parsed = display.safeParse(value); if (!parsed.success) return; const { stdout, stderr } = parsed.data.result; if ( [stdout, stderr].some( (text) => new TextEncoder().encode(JSON.stringify(text)).byteLength > 8192, ) ) return; return parsed.data; } type Output = z.infer; export function SkillScriptOutput({ output }: { output: Output }) { return (
Script output · Exit status {output.result.exitCode} {output.result.truncated ? " · Output truncated" : ""}

{output.skill.name} · {output.skill.version} · {output.path}

{!output.result.ok && output.result.error ? (

{output.result.error.message}

) : null} {(["stdout", "stderr"] as const).map((stream) => (
{stream}
              {output.result[stream] || "No output"}
            
))} {output.workspace.files.length ? (
Changed files in this invocation
    {output.workspace.files.map((file) => (
  • {file.path}
  • ))}

Changes belong to the files supplied for this invocation.

) : null} {output.workspace.truncated ? (

Changed file output was truncated.

) : null}
); }