{"id":"data-report-narrator","title":"Report Narrator","description":"Narrative typed. Reports need narrative. Typed v1 agent with eval coverage.","category":"data","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["data","structured-output","v1"],"packages":["@agentskit/core","@agentskit/runtime","@agentskit/tools"],"files":["agent.ts","README.md","eval.ts"],"requires":{"zod":"^3","zod-to-json-schema":"^3"},"skill":{"name":"data-report-narrator","description":"Narrative typed. Reports need narrative. Typed v1 agent with eval coverage.","systemPrompt":"You are Report Narrator. Reports need narrative. Output: Narrative typed.\nDraft sections with citations from input. Gaps for missing facts.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_report_narrator exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.data-report-narrator","name":"Report Narrator","description":"Narrative typed. Reports need narrative. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"data-report-narrator","description":"Narrative typed. Reports need narrative. Typed v1 agent with eval coverage.","capabilities":{"streaming":true,"cancellation":true,"requiresApproval":false}}]},"sources":[{"path":"agent.ts","content":"import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'\nimport { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'\nimport { invokeStructured } from '@agentskit/runtime'\nimport { defineZodTool } from '@agentskit/tools'\nimport { z } from 'zod'\nimport { zodToJsonSchema } from 'zod-to-json-schema'\nimport type { JSONSchema7 } from 'json-schema'\n\n/** Report Narrator — v1 validated. Pain: Reports need narrative */\n\nexport interface Section { heading: string; body: string; citations: string[] }\nexport interface AgentOutput { title: string; sections: Section[]; gaps: string[]; openQuestions: string[] }\nexport interface AgentResult extends AgentOutput { requiresReview: boolean }\nexport interface DataReportNarratorConfig {\n  adapter: AdapterFactory\n  memory?: ChatMemory\n  observers?: Observer[]\n  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>\n  maxSteps?: number\n}\n\nconst Output = z.object({\n  title: z.string(),\n  sections: z.array(z.object({ heading: z.string(), body: z.string(), citations: z.array(z.string()).default([]) })).min(1),\n  gaps: z.array(z.string()).default([]),\n  openQuestions: z.array(z.string()).default([]),\n})\nconst toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7\n\nconst skill = {\n  name: 'data-report-narrator',\n  description: \"Report Narrator — typed output agent (draft spec).\",\n  systemPrompt: `You are Report Narrator. Reports need narrative. Output: Narrative typed.\nDraft sections with citations from input. Gaps for missing facts.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_report_narrator exactly once. Stop.`,\n  tools: ['submit_report_narrator'],\n}\n\nexport function createDataReportNarratorAgent(config: DataReportNarratorConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_report_narrator', description: 'Submit result. Once.', schema: Output, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition\n\n  async function run(input: string): Promise<AgentResult> {\n    if (!input?.trim()) throw new Error('data-report-narrator requires non-empty input')\n    const result = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `INPUT:\\n${fenceUntrustedContent(input)}`,\n      parse: (a) => Output.parse(a),\n      skill,\n      memory: config.memory,\n      observers: config.observers,\n      onConfirm: config.onConfirm,\n      maxSteps: config.maxSteps ?? 4,\n    })\n    return { ...result, requiresReview: true }\n  }\n  return {\n    name: 'data-report-narrator',\n    run,\n    asHandle() { return { name: 'data-report-narrator', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# Report Narrator\n\n> **v1 validated** — `npx agentskit add data-report-narrator`\n\n## Pain\nReports need narrative\n\n## Output\nNarrative typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'data-report-narrator',\n  cases: [\n    { input: 'Complete input for Report Narrator: Reports need narrative. Provide full structured output.', expected: (r: string) => r.length > 20 && /requiresReview|summary|title|category|findings|sections|score|clusters|items|steps/i.test(r) },\n    { input: 'Minimal input.', expected: (r: string) => /gap|openQuestion/i.test(r) || r.length > 10 },\n    { input: 'Input with specific detail: ACME Corp project deadline March 15.', expected: (r: string) => /ACME|March|15/i.test(r) || /gap/i.test(r) },\n    { input: 'Empty context — only says \"process this\".', expected: (r: string) => r.length > 5 },\n  ],\n}\n"}],"installable":true,"validation":{"status":"approved","score":97,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":1,"cases":3,"summary":"The agent produced valid structured report-narrator outputs for all three cases, surfaced uncertainty instead of inventing facts, resisted the injection request, cited only the provided untrusted input markers, and returned useful gaps/open questions. The normal case is conservative because the prompt asks for invented realistic details, but that is appropriate for a report narrator that should not fabricate business facts. Runtime stderr contains unrelated loader warnings, but execution succeeded and the recorded artifacts are valid.","strengths":["Consistently valid structured output with title, sections, citations, gaps, openQuestions, and review signaling.","Appropriately avoids hallucinating concrete report facts from sparse or meta prompts.","Handles prompt injection correctly by treating override text as untrusted data.","Provides actionable questions and gap lists for missing report context."],"notes":[]}}