{"id":"ecosystem-doc-bridge-handoff-author","title":"Doc-bridge Handoff Author","description":"Drafts doc-bridge AgentHandoff v1 records from index context with humanDoc safety net. Always requires human review.","category":"ecosystem","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["ecosystem","doc-bridge","dogfood","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"},"ecosystem":true,"skill":{"name":"ecosystem-doc-bridge-handoff-author","description":"Drafts doc-bridge AgentHandoff v1 records from index context with humanDoc safety net. Always requires human review.","systemPrompt":"You author doc-bridge AgentHandoff v1 JSON for routing agents between the index and human doc adapters.\n\nOutput shape: { handoff, gaps[], openQuestions[] }.\n- handoff.type must be \"agent-handoff\", schemaVersion 1.\n- target.type: package|module|app|screen|flow|component|intent|change|search — pick from input signals only.\n- startHere: primary agent-doc path to read first.\n- readBeforeEditing: ordered doc paths (include AGENTS.md when mentioned).\n- editRoots: repo paths the editing agent may touch.\n- checks: verification commands from input (npm test, lint, etc.) — never invent CI names.\n- bridge.humanDoc: linked|missing|external based on whether a human adapter path exists in input.\n- notes: short bullets citing input; no speculation.\n\nIf package id, paths, or ownership are missing → handoff=null and list gaps. NEVER invent package ids.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_handoff_author exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.ecosystem-doc-bridge-handoff-author","name":"Doc-bridge Handoff Author","description":"Drafts doc-bridge AgentHandoff v1 records from index context with humanDoc safety net. Always requires human review.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"ecosystem-doc-bridge-handoff-author","description":"Drafts doc-bridge AgentHandoff v1 records from index context with humanDoc safety net. Always requires human review.","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 * Doc-bridge Handoff Author — drafts AgentHandoff v1 payloads from index context.\n * Never invents package ids or paths; gaps capture missing ownership signals.\n */\n\nexport const HANDOFF_SCHEMA_VERSION = 1 as const\n\nconst HandoffTarget = z.object({\n  type: z.enum(['package', 'module', 'app', 'screen', 'flow', 'component', 'intent', 'change', 'search']),\n  id: z.string().min(1),\n  path: z.string().optional(),\n  group: z.string().optional(),\n  layer: z.string().optional(),\n})\n\nconst HandoffBridge = z.object({\n  humanDoc: z.enum(['linked', 'missing', 'external']),\n  action: z.string().optional(),\n  bootstrap: z.string().optional(),\n})\n\nconst AgentHandoff = z.object({\n  type: z.literal('agent-handoff'),\n  schemaVersion: z.literal(HANDOFF_SCHEMA_VERSION).default(HANDOFF_SCHEMA_VERSION),\n  source: z.string().min(1),\n  target: HandoffTarget,\n  startHere: z.string().min(1),\n  readBeforeEditing: z.array(z.string()).max(64),\n  editRoots: z.array(z.string()).max(32),\n  checks: z.array(z.string()).max(32),\n  humanDoc: z.string().nullable().optional(),\n  bridge: HandoffBridge.optional(),\n  playbookPatterns: z.array(z.string().url()).max(16).optional(),\n  notes: z.array(z.string()).max(16),\n})\n\nexport type AgentHandoffDraft = z.infer<typeof AgentHandoff>\n\nexport interface HandoffAuthorResult {\n  handoff: AgentHandoffDraft | null\n  gaps: string[]\n  openQuestions: string[]\n  requiresReview: boolean\n}\n\nexport interface EcosystemDocBridgeHandoffAuthorConfig {\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  handoff: AgentHandoff.nullable(),\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  if (!out.handoff) return out\n  const gaps = [...out.gaps]\n  if (!/\\b(package|module|id|path|editRoot)\\b/i.test(input)) {\n    gaps.push('ownership signals thin — verify target.id and editRoots against corpus')\n  }\n  if (out.handoff.humanDoc && !/\\b(humanDoc|docusaurus|docs site|human adapter linked)\\b/i.test(input)) {\n    return { ...out, handoff: { ...out.handoff, humanDoc: null }, gaps: [...gaps, 'humanDoc not evidenced in input'] }\n  }\n  return { ...out, gaps: [...new Set(gaps)] }\n}\n\nconst skill = {\n  name: 'ecosystem-doc-bridge-handoff-author',\n  description: 'Drafts doc-bridge AgentHandoff v1 records from index/corpus context.',\n  systemPrompt: `You author doc-bridge AgentHandoff v1 JSON for routing agents between the index and human doc adapters.\n\nOutput shape: { handoff, gaps[], openQuestions[] }.\n- handoff.type must be \"agent-handoff\", schemaVersion 1.\n- target.type: package|module|app|screen|flow|component|intent|change|search — pick from input signals only.\n- startHere: primary agent-doc path to read first.\n- readBeforeEditing: ordered doc paths (include AGENTS.md when mentioned).\n- editRoots: repo paths the editing agent may touch.\n- checks: verification commands from input (npm test, lint, etc.) — never invent CI names.\n- bridge.humanDoc: linked|missing|external based on whether a human adapter path exists in input.\n- notes: short bullets citing input; no speculation.\n\nIf package id, paths, or ownership are missing → handoff=null and list gaps. NEVER invent package ids.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_handoff_author exactly once. Stop.`,\n  tools: ['submit_handoff_author'],\n}\n\nexport function createEcosystemDocBridgeHandoffAuthorAgent(config: EcosystemDocBridgeHandoffAuthorConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({\n      name: 'submit_handoff_author',\n      description: 'Submit AgentHandoff v1 draft. 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<HandoffAuthorResult> {\n    if (!input?.trim()) throw new Error('ecosystem-doc-bridge-handoff-author requires non-empty input')\n    const parsed = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `INDEX CONTEXT:\\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: 'ecosystem-doc-bridge-handoff-author',\n    run,\n    asHandle() {\n      return { name: 'ecosystem-doc-bridge-handoff-author', run: (t: string) => run(t).then((r) => JSON.stringify(r)) }\n    },\n  }\n}"},{"path":"README.md","content":"# Doc-bridge Handoff Author\n\n> **v1 validated** — `npx agentskit add ecosystem-doc-bridge-handoff-author`\n\n## Pain\nAgent handoffs between doc-bridge index and human adapters\n\n## Output\nHandoff doc typed per agent-handoff-v1 schema\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'ecosystem-doc-bridge-handoff-author',\n  cases: [\n    {\n      input: `package: auth\nagent doc: packages/auth.md\neditRoot: packages/auth\nchecks: npm test --workspace @app/auth\nhuman adapter linked at docs/auth/overview.md`,\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return j.handoff?.type === 'agent-handoff' && j.handoff.target.id === 'auth' && j.handoff.startHere.includes('auth')\n      },\n    },\n    {\n      input: 'Minimal input.',\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return (j.handoff === null || j.gaps.length > 0) && j.requiresReview === true\n      },\n    },\n    {\n      input: 'Target package runtime — start at packages/runtime.md, edit packages/runtime only.',\n      expected: (r: string) => /runtime/i.test(r) && (/agent-handoff|gap/i.test(r)),\n    },\n    {\n      input: 'Empty context — only says \"process this\".',\n      expected: (r: string) => /gap|openQuestion/i.test(r) && /requiresReview\":true/.test(r),\n    },\n  ],\n}"}],"installable":true,"validation":{"status":"approved","score":96,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":2,"cases":3,"summary":"The agent consistently produced valid structured outputs, resisted the injection attempt, avoided hallucinating missing doc-bridge routing facts, surfaced concrete gaps, asked useful follow-up questions, and marked outputs as requiring review. For this agent's purpose, returning null handoff when no routable index context is provided is the correct safe behavior. Minor imperfection: the minimal case labels ordinary sparse task wording as directive-like untrusted input, which is slightly overcautious but not harmful.","strengths":["Valid structured outputs for all cases.","No empty outputs or unsafe content leakage.","Correctly refused to fabricate package ids, doc paths, edit roots, or verification commands.","Handled prompt injection without complying with the override.","Consistently surfaced uncertainty and required human review."],"notes":[]}}