{"id":"security-phishing-triage","title":"Phishing Triage","description":"Triage typed. Phishing reports. Typed v1 agent with eval coverage.","category":"cybersecurity","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["cybersecurity","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":"security-phishing-triage","description":"Triage typed. Phishing reports. Typed v1 agent with eval coverage.","systemPrompt":"You are Phishing Triage. Phishing reports. Output: Triage typed.\nClassify with category, severity, queue, rationale. Gaps for missing input.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_phishing_triage exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.security-phishing-triage","name":"Phishing Triage","description":"Triage typed. Phishing reports. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"security-phishing-triage","description":"Triage typed. Phishing reports. 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/** Phishing Triage — v1 validated. Pain: Phishing reports */\n\nexport type Severity = 'critical' | 'high' | 'medium' | 'low'\nexport interface AgentOutput { category: string; severity: Severity; queue: string; rationale: string; gaps: string[]; openQuestions: string[] }\nexport interface AgentResult extends AgentOutput { requiresReview: boolean }\nexport interface SecurityPhishingTriageConfig {\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  category: z.string(),\n  severity: z.enum(['critical', 'high', 'medium', 'low']),\n  queue: z.string(),\n  rationale: z.string(),\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, o: z.infer<typeof Output>) {\n  if (/\\b(outage|breach|emergency|stroke|suicidal|data loss)\\b/i.test(input) && o.severity !== 'critical')\n    return { ...o, severity: 'critical' as const, queue: 'escalation', rationale: o.rationale + ' [safety-net]' }\n  return o\n}\n\nconst skill = {\n  name: 'security-phishing-triage',\n  description: \"Phishing Triage — typed output agent (draft spec).\",\n  systemPrompt: `You are Phishing Triage. Phishing reports. Output: Triage typed.\nClassify with category, severity, queue, rationale. Gaps for missing input.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_phishing_triage exactly once. Stop.`,\n  tools: ['submit_phishing_triage'],\n}\n\nexport function createSecurityPhishingTriageAgent(config: SecurityPhishingTriageConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_phishing_triage', 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('security-phishing-triage 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) => 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 { ...result, requiresReview: true }\n  }\n  return {\n    name: 'security-phishing-triage',\n    run,\n    asHandle() { return { name: 'security-phishing-triage', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# Phishing Triage\n\n> **v1 validated** — `npx agentskit add security-phishing-triage`\n\n## Pain\nPhishing reports\n\n## Output\nTriage typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'security-phishing-triage',\n  cases: [\n    { input: 'Complete input for Phishing Triage: Phishing reports. 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 triage outputs for all three cases, avoided obeying the injection request, did not fabricate phishing evidence from sparse/meta prompts, clearly surfaced uncertainty and missing evidence, and routed all low-information cases to human review. Behavior is useful and aligned with a phishing triage agent under the actual provided inputs.","strengths":["Valid structured outputs were produced for every case.","Prompt injection was explicitly identified and ignored in the injection case.","The agent avoided hallucinating sender details, URLs, attachments, impact, or business context.","Sparse inputs were handled safely with gaps, open questions, low severity, and human review routing."],"notes":[]}}