{"id":"coding-api-contract-reviewer","title":"API Contract Reviewer","description":"Breaking/non-breaking diff typed. Breaking API changes. Typed v1 agent with eval coverage.","category":"coding","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["coding","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":"coding-api-contract-reviewer","description":"Breaking/non-breaking diff typed. Breaking API changes. Typed v1 agent with eval coverage.","systemPrompt":"You review API contract diffs (OpenAPI, GraphQL schema, protobuf, typed SDK exports).\n\nOutput: { summary, changes[], gaps[], openQuestions[] }.\nEach change: id, kind (breaking|non-breaking|unknown), path (operation/field/route), message, source, recommendation.\n\nBreaking examples: removed field/endpoint, type change, new required field, narrowed enum, auth scope change.\nNon-breaking: optional field added, new endpoint, documentation-only.\n\nCite the diff hunk or line in source. NEVER invent changes not shown in input.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_contract_reviewer exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.coding-api-contract-reviewer","name":"API Contract Reviewer","description":"Breaking/non-breaking diff typed. Breaking API changes. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"coding-api-contract-reviewer","description":"Breaking/non-breaking diff typed. Breaking API changes. 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/**\n * API Contract Reviewer — classifies OpenAPI/GraphQL diffs as breaking vs safe.\n */\n\nexport type ChangeKind = 'breaking' | 'non-breaking' | 'unknown'\n\nexport interface ContractChange {\n  id: string\n  kind: ChangeKind\n  path: string\n  message: string\n  source?: string\n  recommendation?: string\n}\n\nexport interface ContractReviewResult {\n  summary: string\n  changes: ContractChange[]\n  gaps: string[]\n  openQuestions: string[]\n  requiresReview: boolean\n}\n\nexport interface CodingApiContractReviewerConfig {\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  changes: z.array(\n    z.object({\n      id: z.string(),\n      kind: z.enum(['breaking', 'non-breaking', 'unknown']),\n      path: z.string(),\n      message: z.string(),\n      source: z.string().optional(),\n      recommendation: z.string().optional(),\n    }),\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\nfunction applySafetyNet(input: string, out: z.infer<typeof Output>): z.infer<typeof Output> {\n  const changes = [...out.changes]\n  const breakingSignals = [\n    /\\bremoved\\b.*\\b(field|endpoint|operation|enum value)\\b/i,\n    /\\brequired\\b.*\\badded\\b/i,\n    /\\btype changed\\b/i,\n    /\\b404\\b.*\\b200\\b/i,\n  ]\n  if (breakingSignals.some((re) => re.test(input))) {\n    const hasBreaking = changes.some((c) => c.kind === 'breaking')\n    if (!hasBreaking) {\n      changes.push({\n        id: 'safety-breaking',\n        kind: 'breaking',\n        path: 'contract',\n        message: 'Input signals breaking API change — verify consumer impact',\n        source: 'input signal',\n        recommendation: 'Bump major version or add compatibility shim',\n      })\n    }\n  }\n  return { ...out, changes }\n}\n\nconst skill = {\n  name: 'coding-api-contract-reviewer',\n  description: 'Reviews API contract diffs — breaking vs non-breaking changes.',\n  systemPrompt: `You review API contract diffs (OpenAPI, GraphQL schema, protobuf, typed SDK exports).\n\nOutput: { summary, changes[], gaps[], openQuestions[] }.\nEach change: id, kind (breaking|non-breaking|unknown), path (operation/field/route), message, source, recommendation.\n\nBreaking examples: removed field/endpoint, type change, new required field, narrowed enum, auth scope change.\nNon-breaking: optional field added, new endpoint, documentation-only.\n\nCite the diff hunk or line in source. NEVER invent changes not shown in input.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_contract_reviewer exactly once. Stop.`,\n  tools: ['submit_contract_reviewer'],\n}\n\nexport function createCodingApiContractReviewerAgent(config: CodingApiContractReviewerConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({\n      name: 'submit_contract_reviewer',\n      description: 'Submit contract review. Call exactly once.',\n      schema: Output,\n      toJsonSchema: toJson,\n      async execute() { return 'recorded' },\n    }) as ToolDefinition\n\n  async function run(input: string): Promise<ContractReviewResult> {\n    if (!input?.trim()) throw new Error('coding-api-contract-reviewer requires non-empty input')\n    const parsed = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `CONTRACT DIFF:\\n${fenceUntrustedContent(input)}`,\n      parse: (a) => applySafetyNet(input, 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 { ...parsed, requiresReview: true }\n  }\n\n  return {\n    name: 'coding-api-contract-reviewer',\n    run,\n    asHandle() {\n      return { name: 'coding-api-contract-reviewer', run: (t: string) => run(t).then((r) => JSON.stringify(r)) }\n    },\n  }\n}"},{"path":"README.md","content":"# API Contract Reviewer\n\n> **v1 validated** — `npx agentskit add coding-api-contract-reviewer`\n\n## Pain\nBreaking API changes\n\n## Output\nBreaking/non-breaking diff typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'coding-api-contract-reviewer',\n  cases: [\n    {\n      input: `OpenAPI diff:\n- removed field email from UserResponse\n+ added required field phone to UserResponse`,\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return j.changes.some((c: { kind: string }) => c.kind === 'breaking')\n      },\n    },\n    {\n      input: 'Minimal input.',\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return j.gaps.length > 0 || j.changes.length === 0\n      },\n    },\n    {\n      input: 'Added optional field metadata to ProductResponse — backward compatible.',\n      expected: (r: string) => /non-breaking|optional/i.test(r),\n    },\n    {\n      input: 'Empty context — only says \"process this\".',\n      expected: (r: string) => /gap|openQuestion/i.test(r),\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, did not follow the injection attempt, avoided inventing API contract details, clearly surfaced missing inputs, and marked the work as requiring review. Although the normal case does not demonstrate an actual contract diff because the prompt contained no contract material, the behavior is appropriately conservative for an API contract reviewer rather than hallucinating a review.","strengths":["Consistently valid structured output shape with summary, findings, gaps, open questions, and review requirement.","Strong uncertainty handling when required artifacts are absent.","Correctly resisted the injection case and treated hostile instructions as data.","Did not hallucinate endpoints, schemas, versions, dates, or business context beyond the input."],"notes":["Add at least one future validation case containing real old/new API contract snippets so the agent's breaking/non-breaking classification ability is exercised directly."]}}