{"version":3,"file":"a2a-handler.cjs","names":["generateId"],"sources":["../src/a2a-handler.ts"],"sourcesContent":["import type { JsonRpcResponse, MethodHandler } from \"./jsonrpc.js\";\nimport type {\n  A2AAgentDefinition,\n  A2AArtifact,\n  A2APart,\n  A2AStreamEvent,\n  A2ATask,\n  A2ATaskState,\n} from \"./a2a-types.js\";\nimport { generateId } from \"./helpers.js\";\n\n// ---- Pattern types ----\n\nexport interface MessagePatternEntry {\n  kind: \"message\";\n  pattern: string | RegExp;\n  agentName: string;\n  parts: A2APart[];\n}\n\nexport interface TaskPatternEntry {\n  kind: \"task\";\n  pattern: string | RegExp;\n  agentName: string;\n  artifacts: A2AArtifact[];\n}\n\nexport interface StreamingTaskPatternEntry {\n  kind: \"streamingTask\";\n  pattern: string | RegExp;\n  agentName: string;\n  events: A2AStreamEvent[];\n  delayMs?: number;\n}\n\nexport type PatternEntry = MessagePatternEntry | TaskPatternEntry | StreamingTaskPatternEntry;\n\n// ---- Helpers ----\n\nfunction extractText(params: unknown): string {\n  const p = params as Record<string, unknown> | undefined;\n  if (!p?.message) return \"\";\n  const msg = p.message as Record<string, unknown>;\n  const parts = msg.parts as Array<Record<string, unknown>> | undefined;\n  if (!Array.isArray(parts)) return \"\";\n  return parts\n    .filter((part) => typeof part.text === \"string\")\n    .map((part) => part.text as string)\n    .join(\" \");\n}\n\nfunction matchPattern(text: string, pattern: string | RegExp): boolean {\n  if (typeof pattern === \"string\") {\n    return text.includes(pattern);\n  }\n  return pattern.test(text);\n}\n\nexport const TERMINAL_STATES: Set<string> = new Set([\n  \"TASK_STATE_COMPLETED\",\n  \"TASK_STATE_FAILED\",\n  \"TASK_STATE_CANCELED\",\n]);\n\n// ---- Agent card builder ----\n\nexport function buildAgentCard(\n  agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }>,\n  baseUrl: string,\n): Record<string, unknown> {\n  // Use the first registered agent as the primary card, or a default\n  const first = agents.values().next().value;\n  const def = first?.def;\n\n  return {\n    name: def?.name ?? \"a2a-mock\",\n    description: def?.description ?? \"A2A mock agent\",\n    version: def?.version ?? \"1.0.0\",\n    supportedInterfaces: [\n      {\n        url: baseUrl,\n        protocolBinding: \"JSONRPC\",\n        protocolVersion: \"1.0\",\n      },\n    ],\n    skills: def?.skills ?? [],\n    capabilities: def?.capabilities ?? { streaming: true },\n  };\n}\n\n// ---- Method handlers ----\n\nexport function createA2AMethods(\n  agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }>,\n  tasks: Map<string, A2ATask>,\n): Record<string, MethodHandler> {\n  function findMatch(text: string): PatternEntry | null {\n    for (const agent of agents.values()) {\n      for (const entry of agent.patterns) {\n        if (matchPattern(text, entry.pattern)) {\n          return entry;\n        }\n      }\n    }\n    return null;\n  }\n\n  function createTask(\n    _agentName: string,\n    artifacts: A2AArtifact[],\n    userParts: A2APart[],\n    state: A2ATaskState = \"TASK_STATE_COMPLETED\",\n  ): A2ATask {\n    const taskId = generateId(\"task\");\n    const contextId = generateId(\"ctx\");\n    const task: A2ATask = {\n      id: taskId,\n      contextId,\n      status: { state, timestamp: new Date().toISOString() },\n      artifacts,\n      history: [\n        {\n          messageId: generateId(\"msg\"),\n          role: \"ROLE_USER\",\n          parts: userParts,\n        },\n      ],\n    };\n    tasks.set(taskId, task);\n    return task;\n  }\n\n  const methods: Record<string, MethodHandler> = {\n    SendMessage: async (params: unknown, id: string | number): Promise<JsonRpcResponse> => {\n      const text = extractText(params);\n      const entry = findMatch(text);\n\n      if (!entry) {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32000, message: \"No matching pattern for message\" },\n        };\n      }\n\n      const p = params as Record<string, unknown> | undefined;\n      const msg = p?.message as Record<string, unknown> | undefined;\n      const userParts: A2APart[] = (msg?.parts as A2APart[]) ?? [{ text }];\n\n      if (entry.kind === \"message\") {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          result: {\n            message: {\n              messageId: generateId(\"msg\"),\n              role: \"ROLE_AGENT\",\n              parts: entry.parts,\n            },\n          },\n        };\n      }\n\n      if (entry.kind === \"task\") {\n        const task = createTask(entry.agentName, entry.artifacts, userParts);\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          result: {\n            task: {\n              id: task.id,\n              contextId: task.contextId,\n              status: task.status,\n              artifacts: task.artifacts,\n            },\n          },\n        };\n      }\n\n      // streamingTask patterns matched via SendMessage just return task\n      if (entry.kind === \"streamingTask\") {\n        const artifacts: A2AArtifact[] = [];\n        for (const evt of entry.events) {\n          if (evt.type === \"artifact\") {\n            artifacts.push({ parts: evt.parts, name: evt.name });\n          }\n        }\n        const task = createTask(entry.agentName, artifacts, userParts);\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          result: {\n            task: {\n              id: task.id,\n              contextId: task.contextId,\n              status: task.status,\n              artifacts: task.artifacts,\n            },\n          },\n        };\n      }\n\n      return {\n        jsonrpc: \"2.0\",\n        id,\n        error: { code: -32000, message: \"No matching pattern for message\" },\n      };\n    },\n\n    // SendStreamingMessage is handled specially in A2AMock (SSE response),\n    // but we register a placeholder so the dispatcher doesn't return \"method not found\".\n    SendStreamingMessage: async (\n      params: unknown,\n      id: string | number,\n    ): Promise<JsonRpcResponse | null> => {\n      // This is intercepted before reaching the dispatcher in a2a-mock.ts\n      // If it reaches here, return an error\n      const text = extractText(params);\n      const entry = findMatch(text);\n      if (!entry) {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32000, message: \"No matching pattern for message\" },\n        };\n      }\n      return null;\n    },\n\n    GetTask: async (params: unknown, id: string | number): Promise<JsonRpcResponse> => {\n      const p = params as Record<string, unknown> | undefined;\n      const taskId = p?.id as string | undefined;\n\n      if (!taskId || !tasks.has(taskId)) {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32001, message: \"Task not found\" },\n        };\n      }\n\n      return {\n        jsonrpc: \"2.0\",\n        id,\n        result: { task: tasks.get(taskId) },\n      };\n    },\n\n    ListTasks: async (params: unknown, id: string | number): Promise<JsonRpcResponse> => {\n      const p = params as Record<string, unknown> | undefined;\n      const contextId = p?.contextId as string | undefined;\n      const status = p?.status as string | undefined;\n\n      let results = Array.from(tasks.values());\n\n      if (contextId) {\n        results = results.filter((t) => t.contextId === contextId);\n      }\n      if (status) {\n        results = results.filter((t) => t.status.state === status);\n      }\n\n      return {\n        jsonrpc: \"2.0\",\n        id,\n        result: { tasks: results },\n      };\n    },\n\n    CancelTask: async (params: unknown, id: string | number): Promise<JsonRpcResponse> => {\n      const p = params as Record<string, unknown> | undefined;\n      const taskId = p?.id as string | undefined;\n\n      if (!taskId || !tasks.has(taskId)) {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32001, message: \"Task not found\" },\n        };\n      }\n\n      const task = tasks.get(taskId)!;\n\n      if (TERMINAL_STATES.has(task.status.state)) {\n        return {\n          jsonrpc: \"2.0\",\n          id,\n          error: { code: -32002, message: \"Task already in terminal state\" },\n        };\n      }\n\n      task.status = {\n        state: \"TASK_STATE_CANCELED\",\n        timestamp: new Date().toISOString(),\n      };\n\n      return {\n        jsonrpc: \"2.0\",\n        id,\n        result: { task },\n      };\n    },\n  };\n\n  return methods;\n}\n\n// ---- Streaming helpers ----\n\nexport function findStreamingMatch(\n  text: string,\n  agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }>,\n): StreamingTaskPatternEntry | null {\n  for (const agent of agents.values()) {\n    for (const entry of agent.patterns) {\n      if (entry.kind === \"streamingTask\" && matchPattern(text, entry.pattern)) {\n        return entry;\n      }\n    }\n  }\n  return null;\n}\n\nexport { extractText };\n"],"mappings":";;;AAuCA,SAAS,YAAY,QAAyB;CAC5C,MAAM,IAAI;AACV,KAAI,CAAC,GAAG,QAAS,QAAO;CAExB,MAAM,QADM,EAAE,QACI;AAClB,KAAI,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO;AAClC,QAAO,MACJ,QAAQ,SAAS,OAAO,KAAK,SAAS,SAAS,CAC/C,KAAK,SAAS,KAAK,KAAe,CAClC,KAAK,IAAI;;AAGd,SAAS,aAAa,MAAc,SAAmC;AACrE,KAAI,OAAO,YAAY,SACrB,QAAO,KAAK,SAAS,QAAQ;AAE/B,QAAO,QAAQ,KAAK,KAAK;;AAG3B,MAAa,kBAA+B,IAAI,IAAI;CAClD;CACA;CACA;CACD,CAAC;AAIF,SAAgB,eACd,QACA,SACyB;CAGzB,MAAM,MADQ,OAAO,QAAQ,CAAC,MAAM,CAAC,OAClB;AAEnB,QAAO;EACL,MAAM,KAAK,QAAQ;EACnB,aAAa,KAAK,eAAe;EACjC,SAAS,KAAK,WAAW;EACzB,qBAAqB,CACnB;GACE,KAAK;GACL,iBAAiB;GACjB,iBAAiB;GAClB,CACF;EACD,QAAQ,KAAK,UAAU,EAAE;EACzB,cAAc,KAAK,gBAAgB,EAAE,WAAW,MAAM;EACvD;;AAKH,SAAgB,iBACd,QACA,OAC+B;CAC/B,SAAS,UAAU,MAAmC;AACpD,OAAK,MAAM,SAAS,OAAO,QAAQ,CACjC,MAAK,MAAM,SAAS,MAAM,SACxB,KAAI,aAAa,MAAM,MAAM,QAAQ,CACnC,QAAO;AAIb,SAAO;;CAGT,SAAS,WACP,YACA,WACA,WACA,QAAsB,wBACb;EACT,MAAM,SAASA,2BAAW,OAAO;EAEjC,MAAM,OAAgB;GACpB,IAAI;GACJ,WAHgBA,2BAAW,MAAM;GAIjC,QAAQ;IAAE;IAAO,4BAAW,IAAI,MAAM,EAAC,aAAa;IAAE;GACtD;GACA,SAAS,CACP;IACE,WAAWA,2BAAW,MAAM;IAC5B,MAAM;IACN,OAAO;IACR,CACF;GACF;AACD,QAAM,IAAI,QAAQ,KAAK;AACvB,SAAO;;AA+KT,QA5K+C;EAC7C,aAAa,OAAO,QAAiB,OAAkD;GACrF,MAAM,OAAO,YAAY,OAAO;GAChC,MAAM,QAAQ,UAAU,KAAK;AAE7B,OAAI,CAAC,MACH,QAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAmC;IACpE;GAKH,MAAM,aAFI,QACK,UACoB,SAAuB,CAAC,EAAE,MAAM,CAAC;AAEpE,OAAI,MAAM,SAAS,UACjB,QAAO;IACL,SAAS;IACT;IACA,QAAQ,EACN,SAAS;KACP,WAAWA,2BAAW,MAAM;KAC5B,MAAM;KACN,OAAO,MAAM;KACd,EACF;IACF;AAGH,OAAI,MAAM,SAAS,QAAQ;IACzB,MAAM,OAAO,WAAW,MAAM,WAAW,MAAM,WAAW,UAAU;AACpE,WAAO;KACL,SAAS;KACT;KACA,QAAQ,EACN,MAAM;MACJ,IAAI,KAAK;MACT,WAAW,KAAK;MAChB,QAAQ,KAAK;MACb,WAAW,KAAK;MACjB,EACF;KACF;;AAIH,OAAI,MAAM,SAAS,iBAAiB;IAClC,MAAM,YAA2B,EAAE;AACnC,SAAK,MAAM,OAAO,MAAM,OACtB,KAAI,IAAI,SAAS,WACf,WAAU,KAAK;KAAE,OAAO,IAAI;KAAO,MAAM,IAAI;KAAM,CAAC;IAGxD,MAAM,OAAO,WAAW,MAAM,WAAW,WAAW,UAAU;AAC9D,WAAO;KACL,SAAS;KACT;KACA,QAAQ,EACN,MAAM;MACJ,IAAI,KAAK;MACT,WAAW,KAAK;MAChB,QAAQ,KAAK;MACb,WAAW,KAAK;MACjB,EACF;KACF;;AAGH,UAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAmC;IACpE;;EAKH,sBAAsB,OACpB,QACA,OACoC;AAKpC,OAAI,CADU,UADD,YAAY,OAAO,CACH,CAE3B,QAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAmC;IACpE;AAEH,UAAO;;EAGT,SAAS,OAAO,QAAiB,OAAkD;GAEjF,MAAM,SADI,QACQ;AAElB,OAAI,CAAC,UAAU,CAAC,MAAM,IAAI,OAAO,CAC/B,QAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAkB;IACnD;AAGH,UAAO;IACL,SAAS;IACT;IACA,QAAQ,EAAE,MAAM,MAAM,IAAI,OAAO,EAAE;IACpC;;EAGH,WAAW,OAAO,QAAiB,OAAkD;GACnF,MAAM,IAAI;GACV,MAAM,YAAY,GAAG;GACrB,MAAM,SAAS,GAAG;GAElB,IAAI,UAAU,MAAM,KAAK,MAAM,QAAQ,CAAC;AAExC,OAAI,UACF,WAAU,QAAQ,QAAQ,MAAM,EAAE,cAAc,UAAU;AAE5D,OAAI,OACF,WAAU,QAAQ,QAAQ,MAAM,EAAE,OAAO,UAAU,OAAO;AAG5D,UAAO;IACL,SAAS;IACT;IACA,QAAQ,EAAE,OAAO,SAAS;IAC3B;;EAGH,YAAY,OAAO,QAAiB,OAAkD;GAEpF,MAAM,SADI,QACQ;AAElB,OAAI,CAAC,UAAU,CAAC,MAAM,IAAI,OAAO,CAC/B,QAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAkB;IACnD;GAGH,MAAM,OAAO,MAAM,IAAI,OAAO;AAE9B,OAAI,gBAAgB,IAAI,KAAK,OAAO,MAAM,CACxC,QAAO;IACL,SAAS;IACT;IACA,OAAO;KAAE,MAAM;KAAQ,SAAS;KAAkC;IACnE;AAGH,QAAK,SAAS;IACZ,OAAO;IACP,4BAAW,IAAI,MAAM,EAAC,aAAa;IACpC;AAED,UAAO;IACL,SAAS;IACT;IACA,QAAQ,EAAE,MAAM;IACjB;;EAEJ;;AAOH,SAAgB,mBACd,MACA,QACkC;AAClC,MAAK,MAAM,SAAS,OAAO,QAAQ,CACjC,MAAK,MAAM,SAAS,MAAM,SACxB,KAAI,MAAM,SAAS,mBAAmB,aAAa,MAAM,MAAM,QAAQ,CACrE,QAAO;AAIb,QAAO"}