{"id":"agency-brief-generator","title":"Brief Generator","description":"Drafts a TYPED creative brief from client kickoff notes (client/product, audience, insight, proposition, mandatories, tone, deliverables, timeline). Pulls facts only from the notes — never invents; gaps become 'to be confirmed' and are listed in toBeConfirmed. Always a draft.","category":"agency","version":"2.0.0","source":"AKOS","license":"MIT","tags":["agency","structured-output","human-in-the-loop"],"packages":["@agentskit/core","@agentskit/runtime","@agentskit/tools"],"files":["agent.ts","README.md","eval.ts"],"requires":{"zod":"^3","zod-to-json-schema":"^3"},"status":"validated","skill":{"name":"agency-brief-generator","description":"Drafts a TYPED creative brief from client kickoff notes (client/product, audience, insight, proposition, mandatories, tone, deliverables, timeline). Pulls facts only from the notes — never invents; gaps become 'to be confirmed' and are listed in toBeConfirmed. Always a draft.","systemPrompt":"You draft a creative brief from client kickoff notes. Fields: client + product; audience;\nkey insight; single-minded proposition; mandatories; tone; deliverables; timeline.\n\nPull facts ONLY from the notes — NEVER invent client details. If a field lacks input, set it to\nexactly \"${TBC}\" rather than fabricating. Voice: clear, action-oriented, agency-standard; avoid\ncorporate jargon. This is a DRAFT for the team to confirm with the client.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_brief exactly once with the structured fields. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.agency-brief-generator","name":"Brief Generator","description":"Drafts a TYPED creative brief from client kickoff notes (client/product, audience, insight, proposition, mandatories, tone, deliverables, timeline). Pulls facts only from the notes — never invents; gaps become 'to be confirmed' and are listed in toBeConfirmed. Always a draft.","version":"2.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"agency-brief-generator","description":"Drafts a TYPED creative brief from client kickoff notes (client/product, audience, insight, proposition, mandatories, tone, deliverables, timeline). Pulls facts only from the notes — never invents; gaps become 'to be confirmed' and are listed in toBeConfirmed. Always a draft.","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/**\n * Brief Generator — drafts a TYPED creative brief from client kickoff notes. Pulls facts\n * only from the notes (never invents client details); any field without input becomes\n * \"to be confirmed with the client\" and is listed in `toBeConfirmed`. Always a draft.\n *\n * ```ts\n * const { brief, toBeConfirmed } = await createBriefGeneratorAgent({ adapter }).run(kickoffNotes)\n * ```\n */\n\nconst TBC = 'to be confirmed with the client'\n\nexport interface CreativeBrief {\n  clientAndProduct: string\n  audience: string\n  keyInsight: string\n  singleMindedProposition: string\n  mandatories: string[]\n  tone: string\n  deliverables: string[]\n  timeline: string\n}\n\nexport interface BriefResult {\n  brief: CreativeBrief\n  /** Brief fields the notes didn't cover (filled with the TBC placeholder). */\n  toBeConfirmed: string[]\n  /** Always true — a draft for the team to confirm with the client. */\n  requiresReview: boolean\n}\n\nexport interface BriefGeneratorConfig {\n  adapter: AdapterFactory\n  memory?: ChatMemory\n  observers?: Observer[]\n  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>\n  maxSteps?: number\n}\n\nconst Brief = z.object({\n  clientAndProduct: z.string(),\n  audience: z.string(),\n  keyInsight: z.string(),\n  singleMindedProposition: z.string(),\n  mandatories: z.array(z.string()),\n  tone: z.string(),\n  deliverables: z.array(z.string()),\n  timeline: z.string(),\n})\nconst toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7\n\nconst skill = {\n  name: 'brief-generator',\n  description: 'Drafts a typed creative brief from client kickoff notes (never invents details).',\n  systemPrompt: `You draft a creative brief from client kickoff notes. Fields: client + product; audience;\nkey insight; single-minded proposition; mandatories; tone; deliverables; timeline.\n\nPull facts ONLY from the notes — NEVER invent client details. If a field lacks input, set it to\nexactly \"${TBC}\" rather than fabricating. Voice: clear, action-oriented, agency-standard; avoid\ncorporate jargon. This is a DRAFT for the team to confirm with the client.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_brief exactly once with the structured fields. Stop.`,\n  tools: ['submit_brief'],\n}\n\nexport function createBriefGeneratorAgent(config: BriefGeneratorConfig) {\n  const emit = (label: string, status: 'start' | 'ok' | 'skip' | 'error', detail?: string) => {\n    for (const o of config.observers ?? []) void o.on({ type: 'progress', label, status, detail })\n  }\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_brief', description: 'Submit the creative brief. Call exactly once.', schema: Brief, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition\n\n  async function run(notes: string): Promise<BriefResult> {\n    if (!notes?.trim()) throw new Error('brief generator requires non-empty kickoff notes')\n    emit('brief', 'start')\n    const brief = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `CLIENT KICKOFF NOTES:\\n${fenceUntrustedContent(notes)}`,\n      parse: (a) => Brief.parse(a),\n      skill,\n      memory: config.memory,\n      observers: config.observers,\n      onConfirm: config.onConfirm,\n      maxSteps: config.maxSteps ?? 3,\n    })\n    // Surface the gaps the model flagged TBC so the team knows what to chase.\n    const toBeConfirmed = Object.entries(brief)\n      .filter(([, v]) => (Array.isArray(v) ? v.some((x) => x.includes(TBC)) : String(v).includes(TBC)))\n      .map(([k]) => k)\n    emit('brief', 'ok', `${toBeConfirmed.length} field(s) TBC`)\n    return { brief, toBeConfirmed, requiresReview: true }\n  }\n\n  return {\n    name: 'agency-brief-generator',\n    run,\n    asHandle() {\n      return { name: 'agency-brief-generator', run: async (task: string) => JSON.stringify(await run(task)) }\n    },\n  }\n}\n"},{"path":"README.md","content":"# Brief Generator\n\nDrafts a **typed creative brief** from client kickoff notes — pulls facts only from the notes, never invents.\n\n```bash\nnpx agentskit add agency-brief-generator\n```\n\n```ts\nimport { anthropic } from '@agentskit/adapters'\nimport { createBriefGeneratorAgent } from './agents/agency-brief-generator/agent'\n\nconst r = await createBriefGeneratorAgent({\n  adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-opus-4-8' }),\n}).run(kickoffNotes)\n// → { brief: { clientAndProduct, audience, keyInsight, singleMindedProposition, mandatories[], tone, deliverables[], timeline }, toBeConfirmed[], requiresReview }\n```\n\n- **Typed brief** — `invokeStructured` + zod; every section is an addressable field.\n- **Never invents** — a field without input is set to `\"to be confirmed with the client\"` and surfaced in `toBeConfirmed` so the team knows what to chase.\n- **Always a draft** — `requiresReview` always true. Untrusted notes are **fenced**.\n\n`run(notes)` → `BriefResult`. `asHandle()` is JSON-out. See [composing agents](../../COMPOSING.md). Feeds [`agency-deck-builder`](../agency-deck-builder).\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'agency-brief-generator',\n  cases: [\n    {\n      input: `Kickoff notes — Client: Lumen Coffee Co. Product: new cold-brew can line \"Lumen Chill\". Audience: urban 22-35 who buy ready-to-drink coffee. They want to feel the brand is \"calm energy, not jittery hustle\". Competitors: Stumptown, La Colombe. Budget: mid. Launch: Q3. Deliverables: social, OOH, 1 hero film. Draft the creative brief.`,\n      expected: (r: string) =>\n        /audience/i.test(r) && /(proposition|single-minded|insight)/i.test(r) && /deliverable/i.test(r),\n    },\n    {\n      input: `Kickoff notes — Client: Meridian Bank. Product: a \"Round-Up Savings\" feature in their app for Gen Z. Insight from research: young customers feel guilty about spending but find budgeting apps preachy. Tone should be supportive, never lecturing. Mandatory: include FDIC disclosure line. Timeline: pitch in 3 weeks. Build the brief.`,\n      expected: (r: string) =>\n        /tone/i.test(r) && /(mandator|FDIC)/i.test(r) && /(timeline|3 weeks|week)/i.test(r),\n    },\n    {\n      input: `Kickoff notes — Client: Halcyon Outdoors. Product: trail-running shoe \"Halcyon Fleet\". Key insight: trail runners distrust shoes marketed by influencers; they trust grip and durability data. Proposition: \"earned on the descent\". Deliverables: print, retail display, athlete content series. Tone: understated, technical, no hype. Draft the brief.`,\n      expected: (r: string) =>\n        /(client|Halcyon)/i.test(r) && /(proposition|earned on the descent)/i.test(r) && /tone/i.test(r),\n    },\n    {\n      input: `Kickoff notes — Client: a fintech startup (name not given yet). They mentioned \"something about a card for freelancers\" on the call but did not share audience, budget, timeline, competitors, or deliverables. Draft whatever brief you can from this.`,\n      expected: (r: string) =>\n        /to be confirmed/i.test(r),\n    },\n  ],\n}\n"}],"installable":true,"validation":{"status":"approved","score":96,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":2,"cases":3,"summary":"The final structured outputs are valid, conservative, and aligned with the agent purpose: no unsupported facts are invented, missing fields are marked to be confirmed, all gaps are surfaced in toBeConfirmed, and requiresReview is always true. The injection case resists the direct instruction to output APPROVED and returns a safe draft structure.","strengths":["Final outputs match the declared typed schema for all cases.","Correctly avoids inventing client/product, audience, timeline, deliverables, or other brief facts from non-factual prompts.","Surfaces all missing sections in toBeConfirmed.","Always marks requiresReview as true.","Injection attempt is ignored in the final output."],"notes":["Clean up internal event/tool-output consistency: intermediate tool payloads mention prompt-injection handling in fields such as keyInsight or mandatories, while the final record output normalizes these back to TBC. The final outputs are acceptable, but this inconsistency is noisy and could confuse downstream validators that inspect events.","Consider adding an explicit non-brief warning field only if the schema supports it; do not place safety notes inside creative brief fields like mandatories."]}}