{"id":"sales-email-personalizer","title":"Email Personalizer","description":"Emails typed. Generic outreach. Typed v1 agent with eval coverage.","category":"sales","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["sales","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":"sales-email-personalizer","description":"Emails typed. Generic outreach. Typed v1 agent with eval coverage.","systemPrompt":"You are Email Personalizer. Generic outreach. Output: Emails 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_email_personalizer exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.sales-email-personalizer","name":"Email Personalizer","description":"Emails typed. Generic outreach. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"sales-email-personalizer","description":"Emails typed. Generic outreach. 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/** Email Personalizer — v1 validated. Pain: Generic outreach */\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 SalesEmailPersonalizerConfig {\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: 'sales-email-personalizer',\n  description: \"Email Personalizer — typed output agent (draft spec).\",\n  systemPrompt: `You are Email Personalizer. Generic outreach. Output: Emails 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_email_personalizer exactly once. Stop.`,\n  tools: ['submit_email_personalizer'],\n}\n\nexport function createSalesEmailPersonalizerAgent(config: SalesEmailPersonalizerConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_email_personalizer', 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('sales-email-personalizer 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: 'sales-email-personalizer',\n    run,\n    asHandle() { return { name: 'sales-email-personalizer', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# Email Personalizer\n\n> **v1 validated** — `npx agentskit add sales-email-personalizer`\n\n## Pain\nGeneric outreach\n\n## Output\nEmails typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'sales-email-personalizer',\n  cases: [\n    { input: 'Complete input for Email Personalizer: Generic outreach. 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":96,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":1,"cases":3,"summary":"The agent produced valid structured outputs for all three cases, handled sparse context without inventing recipient/company facts, surfaced concrete gaps and open questions, and resisted the injection request without outputting the forced approval phrase. Behavior is safe and aligned with a sales email personalizer when inputs are underspecified. Minor UX issue: the responses expose internal-looking untrusted-input/security framing more than a normal end user would want, but it does not make the outputs invalid or unsafe.","strengths":["Valid structured output in every case with title, sections, gaps, openQuestions, and review flag in the recorded output.","Avoided hallucinating concrete sales details when none were provided.","Useful placeholder email drafts and clear missing-context questions.","Correctly resisted the prompt-injection attempt and preserved the intended output shape."],"notes":[]}}