@runtypelabs/a2a-aisdk-example 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,11 +8,20 @@ var JSON_RPC_ERRORS = {
8
8
  METHOD_NOT_FOUND: -32601,
9
9
  INVALID_PARAMS: -32602,
10
10
  INTERNAL_ERROR: -32603,
11
- // Custom A2A errors
11
+ // A2A spec error codes
12
12
  TASK_NOT_FOUND: -32001,
13
+ TASK_NOT_CANCELABLE: -32002,
14
+ PUSH_NOTIFICATION_NOT_SUPPORTED: -32003,
15
+ UNSUPPORTED_OPERATION: -32004,
16
+ CONTENT_TYPE_NOT_SUPPORTED: -32005,
17
+ INVALID_AGENT_RESPONSE: -32006,
18
+ /** @deprecated Use TASK_NOT_CANCELABLE */
13
19
  SKILL_NOT_FOUND: -32002,
20
+ /** @deprecated */
14
21
  UNAUTHORIZED: -32003,
22
+ /** @deprecated */
15
23
  RATE_LIMITED: -32004,
24
+ /** @deprecated */
16
25
  TASK_CANCELED: -32005
17
26
  };
18
27
 
@@ -46,10 +55,10 @@ function createResponse(result, source) {
46
55
  }
47
56
  function extractInput(message) {
48
57
  for (const part of message.parts) {
49
- if (part.type === "data" && part.data) {
58
+ if ((part.type === "data" || part.data !== void 0) && part.data) {
50
59
  return part.data;
51
60
  }
52
- if (part.type === "text" && part.text) {
61
+ if ((part.type === "text" || part.text !== void 0) && part.text) {
53
62
  try {
54
63
  return JSON.parse(part.text);
55
64
  } catch {
@@ -265,7 +274,7 @@ var TOOL_STOP_CONDITION = stepCountIs(5);
265
274
  var TOOL_TAG = "tool";
266
275
  var LLM_TAG = "llm";
267
276
  function extractTextFromMessage(message) {
268
- return message.parts.filter((p) => p.type === "text" && p.text).map((p) => p.text).join("\n");
277
+ return message.parts.filter((p) => (p.type === "text" || p.text !== void 0) && p.text).map((p) => p.text).join("\n");
269
278
  }
270
279
  function createLLMProvider(config) {
271
280
  const gatewayKey = process.env.AI_GATEWAY_API_KEY;
@@ -614,8 +623,8 @@ function resolveConfig(config) {
614
623
  version: config.version || "1.0.0",
615
624
  skills: config.skills || DEFAULT_SKILLS,
616
625
  provider: config.provider || { organization: "Runtype", url: "https://runtype.com" },
617
- defaultInputModes: config.defaultInputModes || ["text", "data"],
618
- defaultOutputModes: config.defaultOutputModes || ["text", "data"],
626
+ defaultInputModes: config.defaultInputModes || ["text/plain", "application/json"],
627
+ defaultOutputModes: config.defaultOutputModes || ["text/plain", "application/json"],
619
628
  echoMode: config.echoMode ?? false,
620
629
  llmConfig: config.llmConfig || DEFAULT_LLM_CONFIG,
621
630
  agentUrl: config.agentUrl
@@ -626,10 +635,8 @@ function toAgentCardSkills(skills) {
626
635
  id: s.id,
627
636
  name: s.name,
628
637
  description: s.description,
629
- inputModes: ["text", "data"],
630
- outputModes: ["text", "data"],
631
638
  tags: s.tags ?? [],
632
- inputSchema: s.inputSchema
639
+ examples: []
633
640
  }));
634
641
  }
635
642
 
@@ -655,12 +662,77 @@ function jsonResponse(data, status = 200) {
655
662
  }
656
663
  });
657
664
  }
665
+ function normalizePart(part) {
666
+ if (part.type) return part;
667
+ if (part.text !== void 0) return { type: "text", text: part.text, mediaType: part.mediaType };
668
+ if (part.data !== void 0) return { type: "data", data: part.data, mediaType: part.mediaType };
669
+ if (part.url !== void 0 || part.raw !== void 0) return { type: "file", uri: part.url ?? part.uri, mimeType: part.mediaType };
670
+ return part;
671
+ }
672
+ function normalizeRole(role) {
673
+ if (role === "ROLE_USER" || role === "user") return "user";
674
+ if (role === "ROLE_AGENT" || role === "agent") return "agent";
675
+ return "user";
676
+ }
677
+ function normalizeMessage(msg) {
678
+ const raw = msg;
679
+ return {
680
+ role: normalizeRole(raw.role || "user"),
681
+ messageId: raw.messageId,
682
+ parts: (raw.parts || []).map(normalizePart),
683
+ contextId: raw.contextId,
684
+ taskId: raw.taskId,
685
+ metadata: raw.metadata
686
+ };
687
+ }
688
+ function extractSendParams(params) {
689
+ const rawMessage = params.message || {};
690
+ const message = normalizeMessage(rawMessage);
691
+ const metadata = params.metadata;
692
+ const skillId = params.skill ?? metadata?.skill;
693
+ const contextId = params.contextId ?? message.contextId;
694
+ return { skillId, message, contextId, metadata };
695
+ }
696
+ function resolveSkill(skillId, skills, echoMode) {
697
+ if (skillId) {
698
+ const found = skills.find((s) => s.id === skillId);
699
+ if (found) return found;
700
+ }
701
+ if (echoMode) return skills.find((s) => s.id === "echo") || skills[0];
702
+ return skills.find((s) => s.id === "chat") || skills[0];
703
+ }
704
+ function toSpecPart(part) {
705
+ const out = {};
706
+ if (part.type === "text" || part.text !== void 0) out.text = part.text ?? "";
707
+ else if (part.type === "data" || part.data !== void 0) out.data = part.data;
708
+ else if (part.type === "file") {
709
+ out.url = part.uri ?? part.url;
710
+ out.mediaType = part.mimeType ?? part.mediaType;
711
+ }
712
+ if (part.mediaType || part.mimeType) out.mediaType = part.mediaType ?? part.mimeType;
713
+ return out;
714
+ }
715
+ function toSpecArtifact(a) {
716
+ return {
717
+ artifactId: a.artifactId ?? uuidv4(),
718
+ name: a.name,
719
+ parts: a.parts.map(toSpecPart)
720
+ };
721
+ }
658
722
  function createAgentCardHandler(config) {
659
723
  const resolved = resolveConfig(config);
724
+ const agentUrl = resolved.agentUrl || "/api/a2a";
660
725
  const agentCard = {
661
726
  name: resolved.name,
662
727
  description: resolved.description,
663
- url: resolved.agentUrl || "/api/a2a",
728
+ url: agentUrl,
729
+ supportedInterfaces: [
730
+ {
731
+ url: agentUrl,
732
+ protocolBinding: "JSONRPC",
733
+ protocolVersion: A2A_PROTOCOL_VERSION
734
+ }
735
+ ],
664
736
  version: resolved.version,
665
737
  protocolVersion: A2A_PROTOCOL_VERSION,
666
738
  defaultInputModes: resolved.defaultInputModes,
@@ -668,14 +740,9 @@ function createAgentCardHandler(config) {
668
740
  provider: resolved.provider,
669
741
  capabilities: {
670
742
  streaming: true,
671
- pushNotifications: false,
672
- statefulness: "none"
673
- // Serverless is stateless
743
+ pushNotifications: false
674
744
  },
675
- skills: toAgentCardSkills(resolved.skills),
676
- authentication: {
677
- type: "none"
678
- }
745
+ skills: toAgentCardSkills(resolved.skills)
679
746
  };
680
747
  return async function handler(_request) {
681
748
  return new Response(JSON.stringify(agentCard), {
@@ -720,8 +787,12 @@ function createA2AHandler(config) {
720
787
  try {
721
788
  switch (method) {
722
789
  case "tasks/send":
790
+ case "message/send":
791
+ case "SendMessage":
723
792
  return await handleTasksSend(id, params, skills, llmConfig, echoMode);
724
793
  case "tasks/sendSubscribe":
794
+ case "message/stream":
795
+ case "SendStreamingMessage":
725
796
  return await handleTasksSendSubscribe(
726
797
  id,
727
798
  params,
@@ -730,14 +801,16 @@ function createA2AHandler(config) {
730
801
  echoMode
731
802
  );
732
803
  case "tasks/get":
804
+ case "GetTask":
733
805
  return jsonResponse(
734
806
  jsonRpcError(
735
807
  id,
736
808
  JSON_RPC_ERRORS.TASK_NOT_FOUND,
737
- "Task state not available in serverless mode. Use tasks/sendSubscribe for streaming."
809
+ "Task state not available in serverless mode. Use message/stream for streaming."
738
810
  )
739
811
  );
740
812
  case "tasks/cancel":
813
+ case "CancelTask":
741
814
  return jsonResponse(
742
815
  jsonRpcError(
743
816
  id,
@@ -760,28 +833,22 @@ function createA2AHandler(config) {
760
833
  };
761
834
  }
762
835
  async function handleTasksSend(id, params, skills, llmConfig, echoMode) {
763
- const { skill: skillId, message, contextId, metadata } = params;
764
- const skill = skills.find((s) => s.id === skillId);
765
- if (!skill) {
766
- return jsonResponse(
767
- jsonRpcError(id, JSON_RPC_ERRORS.SKILL_NOT_FOUND, `Skill not found: ${skillId}`, {
768
- availableSkills: skills.map((s) => s.id)
769
- })
770
- );
771
- }
836
+ const { skillId, message, contextId, metadata } = extractSendParams(params);
837
+ const skill = resolveSkill(skillId, skills, echoMode);
838
+ const resolvedContextId = contextId || uuidv4();
772
839
  const taskId = generateTaskId();
773
840
  try {
774
841
  const context = {
775
842
  taskId,
776
- contextId,
843
+ contextId: resolvedContextId,
777
844
  skill,
778
845
  message,
779
846
  metadata
780
847
  };
781
848
  let result;
782
- if (skillId.startsWith("time/")) {
849
+ if (skill.id.startsWith("time/")) {
783
850
  result = await executeTimeSkill(context);
784
- } else if (echoMode || skillId === "echo") {
851
+ } else if (echoMode || skill.id === "echo") {
785
852
  result = await executeEcho(context);
786
853
  } else {
787
854
  result = await executeTask(context, llmConfig, skills);
@@ -790,9 +857,9 @@ async function handleTasksSend(id, params, skills, llmConfig, echoMode) {
790
857
  jsonRpcSuccess(id, {
791
858
  task: {
792
859
  id: taskId,
793
- contextId,
794
- status: "completed",
795
- artifacts: result.artifacts,
860
+ contextId: resolvedContextId,
861
+ status: { state: "completed", timestamp: (/* @__PURE__ */ new Date()).toISOString() },
862
+ artifacts: (result.artifacts || []).map(toSpecArtifact),
796
863
  metadata
797
864
  }
798
865
  })
@@ -804,95 +871,112 @@ async function handleTasksSend(id, params, skills, llmConfig, echoMode) {
804
871
  );
805
872
  }
806
873
  }
807
- async function handleTasksSendSubscribe(_id, params, skills, llmConfig, echoMode) {
808
- const { skill: skillId, message, contextId, metadata } = params;
809
- const skill = skills.find((s) => s.id === skillId);
810
- if (!skill) {
811
- return jsonResponse(
812
- jsonRpcError(void 0, JSON_RPC_ERRORS.SKILL_NOT_FOUND, `Skill not found: ${skillId}`, {
813
- availableSkills: skills.map((s) => s.id)
814
- })
815
- );
816
- }
874
+ async function handleTasksSendSubscribe(id, params, skills, llmConfig, echoMode) {
875
+ const { skillId, message, contextId, metadata } = extractSendParams(params);
876
+ const skill = resolveSkill(skillId, skills, echoMode);
877
+ const resolvedContextId = contextId || uuidv4();
817
878
  const taskId = generateTaskId();
818
879
  const stream = new ReadableStream({
819
880
  async start(controller) {
820
881
  const encoder = new TextEncoder();
821
- const sendEvent = (event, data) => {
822
- controller.enqueue(encoder.encode(`event: ${event}
823
- data: ${JSON.stringify(data)}
882
+ const sendStreamResponse = (result) => {
883
+ const rpcResponse = { jsonrpc: "2.0", id, result };
884
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(rpcResponse)}
824
885
 
825
886
  `));
826
887
  };
827
- sendEvent("task/status", { taskId, contextId, status: "submitted" });
828
- sendEvent("task/status", { taskId, contextId, status: "working" });
888
+ sendStreamResponse({
889
+ task: {
890
+ id: taskId,
891
+ contextId: resolvedContextId,
892
+ status: { state: "submitted", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
893
+ }
894
+ });
895
+ sendStreamResponse({
896
+ statusUpdate: {
897
+ taskId,
898
+ contextId: resolvedContextId,
899
+ status: { state: "working", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
900
+ }
901
+ });
829
902
  try {
830
903
  const context = {
831
904
  taskId,
832
- contextId,
905
+ contextId: resolvedContextId,
833
906
  skill,
834
907
  message,
835
908
  metadata
836
909
  };
837
- let artifactIndex = 0;
910
+ const artifactId = uuidv4();
911
+ let isFirstChunk = true;
838
912
  const callbacks = {
839
913
  onChunk: async (text) => {
840
- sendEvent("task/artifact", {
841
- taskId,
842
- artifact: {
843
- name: "response",
844
- parts: [{ type: "text", text }],
845
- index: artifactIndex,
846
- append: artifactIndex > 0,
914
+ sendStreamResponse({
915
+ artifactUpdate: {
916
+ taskId,
917
+ contextId: resolvedContextId,
918
+ artifact: { artifactId, name: "response", parts: [{ text }] },
919
+ append: !isFirstChunk,
847
920
  lastChunk: false
848
921
  }
849
922
  });
850
- artifactIndex++;
923
+ isFirstChunk = false;
851
924
  },
852
925
  onComplete: async () => {
853
- sendEvent("task/artifact", {
854
- taskId,
855
- artifact: {
856
- name: "response",
857
- parts: [{ type: "text", text: "" }],
858
- index: artifactIndex,
926
+ sendStreamResponse({
927
+ artifactUpdate: {
928
+ taskId,
929
+ contextId: resolvedContextId,
930
+ artifact: { artifactId, name: "response", parts: [{ text: "" }] },
859
931
  append: true,
860
932
  lastChunk: true
861
933
  }
862
934
  });
863
- sendEvent("task/status", { taskId, contextId, status: "completed", final: true });
935
+ sendStreamResponse({
936
+ statusUpdate: {
937
+ taskId,
938
+ contextId: resolvedContextId,
939
+ status: { state: "completed", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
940
+ }
941
+ });
864
942
  controller.close();
865
943
  },
866
944
  onError: async (error) => {
867
- sendEvent("task/error", { taskId, error: { code: -32603, message: error.message } });
868
- sendEvent("task/status", {
869
- taskId,
870
- contextId,
871
- status: "failed",
872
- message: error.message,
873
- final: true
945
+ sendStreamResponse({
946
+ statusUpdate: {
947
+ taskId,
948
+ contextId: resolvedContextId,
949
+ status: {
950
+ state: "failed",
951
+ message: { role: "agent", parts: [{ text: error.message }] },
952
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
953
+ }
954
+ }
874
955
  });
875
956
  controller.close();
876
957
  }
877
958
  };
878
- if (skillId.startsWith("time/")) {
959
+ if (skill.id.startsWith("time/")) {
879
960
  const timeResult = await executeTimeSkill(context);
880
961
  await callbacks.onChunk(timeResult.text);
881
962
  await callbacks.onComplete();
882
- } else if (echoMode || skillId === "echo") {
963
+ } else if (echoMode || skill.id === "echo") {
883
964
  await executeEchoStreaming(context, callbacks);
884
965
  } else {
885
966
  await executeTaskStreaming(context, llmConfig, callbacks, skills);
886
967
  }
887
968
  } catch (error) {
888
969
  const errorMessage = error instanceof Error ? error.message : String(error);
889
- sendEvent("task/error", { taskId, error: { code: -32603, message: errorMessage } });
890
- sendEvent("task/status", {
891
- taskId,
892
- contextId,
893
- status: "failed",
894
- message: errorMessage,
895
- final: true
970
+ sendStreamResponse({
971
+ statusUpdate: {
972
+ taskId,
973
+ contextId: resolvedContextId,
974
+ status: {
975
+ state: "failed",
976
+ message: { role: "agent", parts: [{ text: errorMessage }] },
977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
978
+ }
979
+ }
896
980
  });
897
981
  controller.close();
898
982
  }