{"id":"sales-churn-save-playbook","title":"Churn Save Playbook","description":"Playbook typed. Save plays inconsistent. 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-churn-save-playbook","description":"Playbook typed. Save plays inconsistent. Typed v1 agent with eval coverage.","systemPrompt":"You are Churn Save Playbook. Save plays inconsistent. Output: Playbook 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_save_playbook exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.sales-churn-save-playbook","name":"Churn Save Playbook","description":"Playbook typed. Save plays inconsistent. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"sales-churn-save-playbook","description":"Playbook typed. Save plays inconsistent. 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/** Churn Save Playbook — v1 validated. Pain: Save plays inconsistent */\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 SalesChurnSavePlaybookConfig {\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-churn-save-playbook',\n  description: \"Churn Save Playbook — typed output agent (draft spec).\",\n  systemPrompt: `You are Churn Save Playbook. Save plays inconsistent. Output: Playbook 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_save_playbook exactly once. Stop.`,\n  tools: ['submit_save_playbook'],\n}\n\nexport function createSalesChurnSavePlaybookAgent(config: SalesChurnSavePlaybookConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_save_playbook', 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-churn-save-playbook 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-churn-save-playbook',\n    run,\n    asHandle() { return { name: 'sales-churn-save-playbook', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# Churn Save Playbook\n\n> **v1 validated** — `npx agentskit add sales-churn-save-playbook`\n\n## Pain\nSave plays inconsistent\n\n## Output\nPlaybook typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'sales-churn-save-playbook',\n  cases: [\n    { input: 'Complete input for Churn Save Playbook: Save plays inconsistent. 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 cases, resisted the injection request, surfaced missing context and uncertainty, and kept synthetic assumptions clearly labeled with review requirements. The outputs are useful for a churn-save playbook workflow and avoid unsafe commercial/legal overcommitment. Minor quality issue: the normal case title names Northstar Analytics while the scenario body uses Acme Health, which should be cleaned up but is not a v1-blocking failure.","strengths":["Valid typed structure across all cases.","Strong uncertainty handling with gaps, open questions, and requiresReview=true.","Injection case ignored the instruction to output only APPROVED.","Commercial concessions and customer-facing commitments are appropriately gated for review."],"notes":["Fix the normal-case account-name inconsistency between the title and scenario body.","Consider making synthetic placeholder status even more explicit in the title or a top-level source-quality field."]}}