{"id":"sales-forecast-interpreter","title":"Forecast Interpreter","description":"Insights typed. Forecast opaque. 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-forecast-interpreter","description":"Insights typed. Forecast opaque. Typed v1 agent with eval coverage.","systemPrompt":"You are Forecast Interpreter. Forecast opaque. Output: Insights typed.\nActionable findings citing input sources. No invented issues.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_forecast_interpreter exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.sales-forecast-interpreter","name":"Forecast Interpreter","description":"Insights typed. Forecast opaque. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"sales-forecast-interpreter","description":"Insights typed. Forecast opaque. 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/** Forecast Interpreter — v1 validated. Pain: Forecast opaque */\n\nexport interface Finding { id: string; severity: 'critical' | 'high' | 'medium' | 'low' | 'info'; message: string; source?: string; recommendation?: string }\nexport interface AgentOutput { summary: string; findings: Finding[]; gaps: string[]; openQuestions: string[] }\nexport interface AgentResult extends AgentOutput { requiresReview: boolean }\nexport interface SalesForecastInterpreterConfig {\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  summary: z.string(),\n  findings: z.array(z.object({\n    id: z.string(), severity: z.enum(['critical', 'high', 'medium', 'low', 'info']),\n    message: z.string(), source: z.string().optional(), recommendation: z.string().optional(),\n  })),\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-forecast-interpreter',\n  description: \"Forecast Interpreter — typed output agent (draft spec).\",\n  systemPrompt: `You are Forecast Interpreter. Forecast opaque. Output: Insights typed.\nActionable findings citing input sources. No invented issues.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_forecast_interpreter exactly once. Stop.`,\n  tools: ['submit_forecast_interpreter'],\n}\n\nexport function createSalesForecastInterpreterAgent(config: SalesForecastInterpreterConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_forecast_interpreter', 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-forecast-interpreter 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-forecast-interpreter',\n    run,\n    asHandle() { return { name: 'sales-forecast-interpreter', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# Forecast Interpreter\n\n> **v1 validated** — `npx agentskit add sales-forecast-interpreter`\n\n## Pain\nForecast opaque\n\n## Output\nInsights typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'sales-forecast-interpreter',\n  cases: [\n    { input: 'Complete input for Forecast Interpreter: Forecast opaque. 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, resisted instruction injection, avoided inventing forecast details from sparse or placeholder inputs, surfaced uncertainty and missing context, and provided concrete next-step questions. The behavior is conservative but appropriate for a forecast interpreter when no actual forecast data is supplied.","strengths":["Valid structured output shape across all cases.","Correctly treats instruction-like user text as untrusted data instead of following it.","Does not hallucinate sales forecasts, dates, metrics, accounts, or business conclusions absent source data.","Clearly surfaces gaps, uncertainty, and human-review needs.","Injection case is handled explicitly and safely without outputting the requested APPROVED string."],"notes":[]}}