@axiom-lattice/gateway 6.1.0 → 8.0.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.
Files changed (168) hide show
  1. package/README.md +0 -1
  2. package/dist/{WechatChannelAdapter-7WHE5UGY.mjs → WechatChannelAdapter-QLPJJD6S.mjs} +55 -4
  3. package/dist/WechatChannelAdapter-QLPJJD6S.mjs.map +1 -0
  4. package/dist/a2a-standard-VL5I7B4W.mjs +4188 -0
  5. package/dist/a2a-standard-VL5I7B4W.mjs.map +1 -0
  6. package/dist/{chunk-YKTXW6F6.mjs → chunk-2SR4F3ZD.mjs} +65 -1
  7. package/dist/chunk-2SR4F3ZD.mjs.map +1 -0
  8. package/dist/{chunk-FGSFOXIX.mjs → chunk-L6MBEEII.mjs} +2 -2
  9. package/dist/{chunk-QUT2DS4R.mjs → chunk-W3YH4S6G.mjs} +18 -4
  10. package/dist/chunk-W3YH4S6G.mjs.map +1 -0
  11. package/dist/{chunk-MWWLJMYN.mjs → chunk-XUWD4VKY.mjs} +4 -2
  12. package/dist/chunk-XUWD4VKY.mjs.map +1 -0
  13. package/dist/embed/embed-page-AgentDetailSideApp-NK6VQBXS.js +1 -0
  14. package/dist/embed/embed-page-AgentGovernanceWorkbench-TKVV2GA6.js +1 -0
  15. package/dist/embed/embed-page-LangfuseDashboard-EYK2T2VK.js +1 -0
  16. package/dist/embed/embed-page-MessageGraph-P73B52OR.js +6 -0
  17. package/dist/embed/embed-page-ReactChart-LK6DUA4R.js +1 -0
  18. package/dist/embed/{embed-page-ReactInfographic-5P36JLSX.js → embed-page-ReactInfographic-677NVKOU.js} +1 -1
  19. package/dist/embed/embed-page-SkillFlow-54RIT6M2.js +1 -0
  20. package/dist/embed/{embed-page-TasksSideApp-BQMOLOUF.js → embed-page-TasksSideApp-RK64OPAA.js} +1 -1
  21. package/dist/embed/embed-page-ThreadMaintenancePanel-PI3U76W3.js +1 -0
  22. package/dist/embed/{embed-page-TopologyRuntimeView-OSV75P6Y.js → embed-page-TopologyRuntimeView-X6QPRQE2.js} +1 -1
  23. package/dist/embed/{embed-page-WorkflowAutomationView-KJ6HJ5UT.js → embed-page-WorkflowAutomationView-3PE6YCRK.js} +1 -1
  24. package/dist/embed/embed-page-all-JEPF6WHD.js +1 -0
  25. package/dist/embed/{embed-page-architectureDiagram-VXUJARFQ-THUN2PFL.js → embed-page-architectureDiagram-VXUJARFQ-TYJ3NO2K.js} +1 -1
  26. package/dist/embed/embed-page-aspnet-GIV4ESY6.js +1 -0
  27. package/dist/embed/{embed-page-blockDiagram-VD42YOAC-K7OX6LIO.js → embed-page-blockDiagram-VD42YOAC-L4J3TZWR.js} +1 -1
  28. package/dist/embed/{embed-page-c4Diagram-YG6GDRKO-DJDAF5AO.js → embed-page-c4Diagram-YG6GDRKO-CP2IXU3Z.js} +1 -1
  29. package/dist/embed/{embed-page-chunk-FD2PRLMW.js → embed-page-chunk-2VJ7T35I.js} +1 -1
  30. package/dist/embed/{embed-page-chunk-WTOAQDQN.js → embed-page-chunk-3VNL5M2Q.js} +1 -1
  31. package/dist/embed/{embed-page-chunk-JUZPOR5Y.js → embed-page-chunk-5WWOKLDB.js} +1 -1
  32. package/dist/embed/{embed-page-chunk-HYPA2PHA.js → embed-page-chunk-6IIMW7OG.js} +1 -1
  33. package/dist/embed/embed-page-chunk-767LVAGR.js +7 -0
  34. package/dist/embed/{embed-page-chunk-XKGF5D6L.js → embed-page-chunk-7CJDHJEH.js} +1 -1
  35. package/dist/embed/{embed-page-chunk-J5AHS4PO.js → embed-page-chunk-7GLQRV3J.js} +1 -1
  36. package/dist/embed/embed-page-chunk-A5GIM22E.js +117 -0
  37. package/dist/embed/{embed-page-chunk-SQ4GANYE.js → embed-page-chunk-AEDAOTEO.js} +1 -1
  38. package/dist/embed/{embed-page-chunk-QULNM5O2.js → embed-page-chunk-AICFKU43.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-FGKHXQAJ.js → embed-page-chunk-BD7DUF2M.js} +1 -1
  40. package/dist/embed/embed-page-chunk-D7YTSCUO.js +1 -0
  41. package/dist/embed/{embed-page-chunk-A2AHDN6K.js → embed-page-chunk-DQJVWZOB.js} +1 -1
  42. package/dist/embed/embed-page-chunk-EB43T3BJ.js +6746 -0
  43. package/dist/embed/{embed-page-chunk-DTRFJLVZ.js → embed-page-chunk-F5QVZLK5.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-DBUC6J4G.js → embed-page-chunk-GYO2ELMI.js} +1 -1
  45. package/dist/embed/embed-page-chunk-KF3HZ4TX.js +8 -0
  46. package/dist/embed/{embed-page-chunk-G2RO6IBN.js → embed-page-chunk-KXVXKWED.js} +1 -1
  47. package/dist/embed/embed-page-chunk-MEY3NEJZ.js +211 -0
  48. package/dist/embed/{embed-page-chunk-PLNJRORR.js → embed-page-chunk-NVFNRWRN.js} +1 -1
  49. package/dist/embed/embed-page-chunk-OIML5DX4.js +5 -0
  50. package/dist/embed/{embed-page-chunk-IIEDLN7W.js → embed-page-chunk-OKZPPZMF.js} +1 -1
  51. package/dist/embed/embed-page-chunk-T2HKN7ZS.js +459 -0
  52. package/dist/embed/{embed-page-chunk-V2GBFWLG.js → embed-page-chunk-TSZQWEXD.js} +1 -1
  53. package/dist/embed/embed-page-chunk-US46Q3B7.js +15 -0
  54. package/dist/embed/embed-page-chunk-UV6IYDW5.js +242 -0
  55. package/dist/embed/{embed-page-chunk-WFUFTRWA.js → embed-page-chunk-WK77HIEN.js} +1 -1
  56. package/dist/embed/{embed-page-chunk-XSKNS3LY.js → embed-page-chunk-WWXRO4ZL.js} +1 -1
  57. package/dist/embed/embed-page-chunk-WXMPVIXV.js +59 -0
  58. package/dist/embed/{embed-page-chunk-VY6MAPHR.js → embed-page-chunk-XSKDYUNQ.js} +1 -1
  59. package/dist/embed/{embed-page-chunk-V7R2Y72J.js → embed-page-chunk-YJUCDIYK.js} +1 -1
  60. package/dist/embed/embed-page-chunk-YWJQVBZV.js +1 -0
  61. package/dist/embed/embed-page-chunk-ZQQFWEWO.js +1 -0
  62. package/dist/embed/embed-page-classDiagram-2ON5EDUG-MHNSVPAN.js +1 -0
  63. package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-QCMEVMYH.js +1 -0
  64. package/dist/embed/embed-page-code-highlighter-DD23LJY3.js +1 -0
  65. package/dist/embed/embed-page-cshtml-RYMZMLKB.js +1 -0
  66. package/dist/embed/{embed-page-dagre-6UL2VRFP-UQFZMKYW.js → embed-page-dagre-6UL2VRFP-LTZ2DVVO.js} +1 -1
  67. package/dist/embed/{embed-page-diagram-PSM6KHXK-4IKXWNA4.js → embed-page-diagram-PSM6KHXK-JHYXSMCB.js} +1 -1
  68. package/dist/embed/{embed-page-diagram-QEK2KX5R-245T32UF.js → embed-page-diagram-QEK2KX5R-2YGDFFTU.js} +1 -1
  69. package/dist/embed/{embed-page-diagram-S2PKOQOG-BOA5MJA5.js → embed-page-diagram-S2PKOQOG-6EW6MDJW.js} +1 -1
  70. package/dist/embed/embed-page-en-3Z4N5KXB.js +1 -0
  71. package/dist/embed/embed-page-en-BLF5THT6.js +1 -0
  72. package/dist/embed/embed-page-en-E35GE7TA.js +1 -0
  73. package/dist/embed/embed-page-en-EKHHBS6L.js +1 -0
  74. package/dist/embed/{embed-page-en-UWBIHR6I.js → embed-page-en-H4P63IAK.js} +1 -1
  75. package/dist/embed/embed-page-en-KAXIJJ4X.js +1 -0
  76. package/dist/embed/embed-page-en-OIICQSPX.js +3 -0
  77. package/dist/embed/embed-page-en-PQOH5CU4.js +1 -0
  78. package/dist/embed/embed-page-en-TMGUHZ6Y.js +1 -0
  79. package/dist/embed/embed-page-en-XJYWUJEL.js +1 -0
  80. package/dist/embed/{embed-page-en-WB2EPS63.js → embed-page-en-ZCNNAMUD.js} +1 -1
  81. package/dist/embed/{embed-page-erDiagram-Q2GNP2WA-S4J4SQPR.js → embed-page-erDiagram-Q2GNP2WA-KSUS6MHW.js} +1 -1
  82. package/dist/embed/{embed-page-flowDiagram-NV44I4VS-ZFIJXKMW.js → embed-page-flowDiagram-NV44I4VS-AYXRDKAJ.js} +1 -1
  83. package/dist/embed/{embed-page-ganttDiagram-JELNMOA3-7MSIEWL4.js → embed-page-ganttDiagram-JELNMOA3-AV7N2N3T.js} +1 -1
  84. package/dist/embed/{embed-page-gitGraphDiagram-NY62KEGX-F6KE6PZK.js → embed-page-gitGraphDiagram-NY62KEGX-IBVOXVO5.js} +1 -1
  85. package/dist/embed/embed-page-infoDiagram-WHAUD3N6-ZULKOSOZ.js +2 -0
  86. package/dist/embed/{embed-page-journeyDiagram-XKPGCS4Q-6VTW6GQ3.js → embed-page-journeyDiagram-XKPGCS4Q-3GILI7Y4.js} +1 -1
  87. package/dist/embed/{embed-page-kanban-definition-3W4ZIXB7-7IZYCLQZ.js → embed-page-kanban-definition-3W4ZIXB7-UGO3J7IH.js} +1 -1
  88. package/dist/embed/embed-page-mermaid-IN3A3YH2.js +18 -0
  89. package/dist/embed/{embed-page-mindmap-definition-VGOIOE7T-GKTOT34A.js → embed-page-mindmap-definition-VGOIOE7T-VXXZJS3Z.js} +1 -1
  90. package/dist/embed/{embed-page-pieDiagram-ADFJNKIX-VDPALO42.js → embed-page-pieDiagram-ADFJNKIX-ZFAWYA2N.js} +1 -1
  91. package/dist/embed/{embed-page-requirementDiagram-UZGBJVZJ-LTZH6RTD.js → embed-page-requirementDiagram-UZGBJVZJ-2QBINXR7.js} +1 -1
  92. package/dist/embed/{embed-page-sequenceDiagram-WL72ISMW-LV6Q4SHW.js → embed-page-sequenceDiagram-WL72ISMW-3MGTESG4.js} +1 -1
  93. package/dist/embed/{embed-page-stateDiagram-FKZM4ZOC-U4NHQDNP.js → embed-page-stateDiagram-FKZM4ZOC-W6VPIMB5.js} +1 -1
  94. package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-QYJ47QBY.js +1 -0
  95. package/dist/embed/{embed-page-tsx-PNW7N5T4.js → embed-page-tsx-LGDDERM2.js} +1 -1
  96. package/dist/embed/{embed-page-xychartDiagram-PRI3JC2R-VZSIXBFK.js → embed-page-xychartDiagram-PRI3JC2R-7XKT4R7V.js} +1 -1
  97. package/dist/embed/embed-page-zh-2UVQB4RP.js +3 -0
  98. package/dist/embed/embed-page-zh-6EBUEFKP.js +1 -0
  99. package/dist/embed/embed-page-zh-B3WBWQAV.js +1 -0
  100. package/dist/embed/embed-page-zh-GXE63PQ7.js +1 -0
  101. package/dist/embed/{embed-page-zh-ACZNJ4X4.js → embed-page-zh-IB3422D4.js} +1 -1
  102. package/dist/embed/embed-page-zh-KEMYJJLT.js +1 -0
  103. package/dist/embed/{embed-page-zh-NKHP3CFU.js → embed-page-zh-N2STRBUG.js} +1 -1
  104. package/dist/embed/embed-page-zh-RMGMUSDH.js +1 -0
  105. package/dist/embed/embed-page-zh-XO36BV2R.js +1 -0
  106. package/dist/embed/{embed-page-zh-XEYNKCTC.js → embed-page-zh-Z3URTIHW.js} +1 -1
  107. package/dist/embed/embed-page-zh-ZLK3GLM3.js +1 -0
  108. package/dist/embed/embed-page.js +1 -1
  109. package/dist/index.d.mts +1573 -232
  110. package/dist/index.d.ts +1573 -232
  111. package/dist/index.js +35707 -22367
  112. package/dist/index.js.map +1 -1
  113. package/dist/index.mjs +35226 -24724
  114. package/dist/index.mjs.map +1 -1
  115. package/dist/{open-MDC4VCR7.mjs → open-7ED27I5B.mjs} +43 -80
  116. package/dist/open-7ED27I5B.mjs.map +1 -0
  117. package/dist/{open-exposure-OX5NKWDG.mjs → open-exposure-I3DN2X4E.mjs} +17 -5
  118. package/dist/{open-exposure-OX5NKWDG.mjs.map → open-exposure-I3DN2X4E.mjs.map} +1 -1
  119. package/dist/{open-files-FS5AN6Q6.mjs → open-files-V3R2BFTV.mjs} +4 -4
  120. package/package.json +15 -8
  121. package/dist/WechatChannelAdapter-7WHE5UGY.mjs.map +0 -1
  122. package/dist/a2a-standard-3OZFXDCW.mjs +0 -1413
  123. package/dist/a2a-standard-3OZFXDCW.mjs.map +0 -1
  124. package/dist/chunk-K32XEQLH.mjs +0 -28
  125. package/dist/chunk-K32XEQLH.mjs.map +0 -1
  126. package/dist/chunk-MWWLJMYN.mjs.map +0 -1
  127. package/dist/chunk-QUT2DS4R.mjs.map +0 -1
  128. package/dist/chunk-YKTXW6F6.mjs.map +0 -1
  129. package/dist/embed/embed-page-AgentDetailSideApp-2UFH4RJG.js +0 -1
  130. package/dist/embed/embed-page-AgentGovernanceWorkbench-3JOSRW2V.js +0 -1
  131. package/dist/embed/embed-page-MessageGraph-SPB75EHC.js +0 -6
  132. package/dist/embed/embed-page-ReactChart-EE4A5WKS.js +0 -1
  133. package/dist/embed/embed-page-SkillFlow-H2ARZGEX.js +0 -1
  134. package/dist/embed/embed-page-TeamWorkspace-QXMRVXWU.js +0 -748
  135. package/dist/embed/embed-page-TeamWorkspace-YQFWXDLI.css +0 -1
  136. package/dist/embed/embed-page-all-NEJVJ4G7.js +0 -1
  137. package/dist/embed/embed-page-aspnet-SRK67AH2.js +0 -1
  138. package/dist/embed/embed-page-chunk-D5GP7FHH.js +0 -5
  139. package/dist/embed/embed-page-chunk-E6F5YZBR.js +0 -242
  140. package/dist/embed/embed-page-chunk-ESQ5V77Q.js +0 -1
  141. package/dist/embed/embed-page-chunk-GSCVATOA.js +0 -21
  142. package/dist/embed/embed-page-chunk-MHGVD4EV.js +0 -525
  143. package/dist/embed/embed-page-chunk-U4XSGC4Y.js +0 -7009
  144. package/dist/embed/embed-page-chunk-VXWRPT33.js +0 -2
  145. package/dist/embed/embed-page-classDiagram-2ON5EDUG-VYHHMIKZ.js +0 -1
  146. package/dist/embed/embed-page-classDiagram-v2-WZHVMYZB-5AISLXUF.js +0 -1
  147. package/dist/embed/embed-page-code-highlighter-IPU5ZNPJ.js +0 -1
  148. package/dist/embed/embed-page-cshtml-ZY7LJSTH.js +0 -1
  149. package/dist/embed/embed-page-en-2WLCQIB6.js +0 -1
  150. package/dist/embed/embed-page-en-7WXWAWGG.js +0 -1
  151. package/dist/embed/embed-page-en-FP6DWSG3.js +0 -1
  152. package/dist/embed/embed-page-en-GHTROC4H.js +0 -1
  153. package/dist/embed/embed-page-en-ICVL5QAC.js +0 -1
  154. package/dist/embed/embed-page-en-OLZHSUC3.js +0 -1
  155. package/dist/embed/embed-page-infoDiagram-WHAUD3N6-FQMKXERB.js +0 -2
  156. package/dist/embed/embed-page-mermaid-R7ZBUE3D.js +0 -18
  157. package/dist/embed/embed-page-stateDiagram-v2-4FDKWEC3-G3EWQCOW.js +0 -1
  158. package/dist/embed/embed-page-zh-EYLHUD5G.js +0 -1
  159. package/dist/embed/embed-page-zh-FPYCTKQK.js +0 -1
  160. package/dist/embed/embed-page-zh-OPUVZZSQ.js +0 -1
  161. package/dist/embed/embed-page-zh-VH6J7XZO.js +0 -1
  162. package/dist/embed/embed-page-zh-Y5X5Y2DF.js +0 -1
  163. package/dist/mcpTransportRegistry-VXUVJKBC.mjs +0 -14
  164. package/dist/mcpTransportRegistry-VXUVJKBC.mjs.map +0 -1
  165. package/dist/open-MDC4VCR7.mjs.map +0 -1
  166. /package/dist/{chunk-FGSFOXIX.mjs.map → chunk-L6MBEEII.mjs.map} +0 -0
  167. /package/dist/embed/{embed-page-chunk-34FVRFPW.js → embed-page-chunk-UH47IEAR.js} +0 -0
  168. /package/dist/{open-files-FS5AN6Q6.mjs.map → open-files-V3R2BFTV.mjs.map} +0 -0
@@ -0,0 +1,4188 @@
1
+ import {
2
+ buildAgentCardForAssistant,
3
+ deriveAgentSkills
4
+ } from "./chunk-W3YH4S6G.mjs";
5
+ import {
6
+ formatFileRefsSection,
7
+ getRunCancelRouter,
8
+ resolveRunTaskMaxActivePerTenant,
9
+ saveProjectFile,
10
+ wakeRunTaskQueue
11
+ } from "./chunk-2SR4F3ZD.mjs";
12
+ import {
13
+ A2AAuthError
14
+ } from "./chunk-GPHOYR3X.mjs";
15
+ import "./chunk-NFEGQTCC.mjs";
16
+
17
+ // src/routes/a2a-standard.ts
18
+ import { v4 as uuidv42 } from "uuid";
19
+ import {
20
+ A2AError as A2AError3,
21
+ DefaultExecutionEventBus as DefaultExecutionEventBus2,
22
+ DefaultRequestHandler,
23
+ JsonRpcTransportHandler,
24
+ RequestContext
25
+ } from "@a2a-js/sdk/server";
26
+ import { AgentCard as AgentCardV1 } from "@a2a-js/sdk-v1";
27
+ import {
28
+ agentInstanceManager as agentInstanceManager2,
29
+ ChunkCursorExpiredError,
30
+ chunkBufferStreamKey,
31
+ createTaskLifecycleService,
32
+ getChunkBuffer,
33
+ getStoreLattice
34
+ } from "@axiom-lattice/core";
35
+ import { MessageChunkTypes as MessageChunkTypes3 } from "@axiom-lattice/protocols";
36
+
37
+ // src/services/a2a/A2AAgentCardBuilderV1.ts
38
+ function buildAgentCardForAssistantV1(params) {
39
+ const { assistant, baseUrl } = params;
40
+ const v1Url = `${baseUrl}/a2a/v1/${assistant.id}`;
41
+ const v03Url = `${baseUrl}/a2a/${assistant.id}`;
42
+ const skills = deriveAgentSkills(assistant).map((skill) => ({
43
+ ...skill,
44
+ tags: skill.tags ?? [],
45
+ examples: skill.examples ?? [],
46
+ inputModes: [],
47
+ outputModes: [],
48
+ securityRequirements: []
49
+ }));
50
+ return {
51
+ name: assistant.name,
52
+ description: assistant.description ?? "",
53
+ supportedInterfaces: [
54
+ {
55
+ url: v1Url,
56
+ protocolBinding: "JSONRPC",
57
+ tenant: "",
58
+ protocolVersion: "1.0"
59
+ },
60
+ {
61
+ url: v03Url,
62
+ protocolBinding: "JSONRPC",
63
+ tenant: "",
64
+ protocolVersion: "0.3"
65
+ }
66
+ ],
67
+ provider: void 0,
68
+ version: "1.0.0",
69
+ capabilities: {
70
+ streaming: true,
71
+ pushNotifications: params.pushNotifications ?? true,
72
+ extensions: [
73
+ {
74
+ uri: "https://axiom-lattice.dev/a2a/extensions/push-notifications-experimental",
75
+ description: "Push notifications are experimental and best-effort. Delivery is at-most-once; clients should poll tasks/get or use SubscribeToTask for reliability.",
76
+ required: false,
77
+ params: {
78
+ experimental: true,
79
+ reliable: false,
80
+ delivery: "at-most-once"
81
+ }
82
+ }
83
+ ],
84
+ extendedAgentCard: true
85
+ },
86
+ securitySchemes: {
87
+ bearer: {
88
+ scheme: {
89
+ $case: "httpAuthSecurityScheme",
90
+ value: {
91
+ scheme: "Bearer",
92
+ description: "API key sent as an Authorization Bearer token",
93
+ bearerFormat: ""
94
+ }
95
+ }
96
+ },
97
+ apiKeyHeader: {
98
+ scheme: {
99
+ $case: "apiKeySecurityScheme",
100
+ value: {
101
+ location: "header",
102
+ name: "x-api-key",
103
+ description: "API key sent via the x-api-key header"
104
+ }
105
+ }
106
+ }
107
+ },
108
+ securityRequirements: [
109
+ { schemes: { bearer: { list: [] } } },
110
+ { schemes: { apiKeyHeader: { list: [] } } }
111
+ ],
112
+ defaultInputModes: ["text"],
113
+ defaultOutputModes: ["text"],
114
+ skills,
115
+ signatures: []
116
+ };
117
+ }
118
+
119
+ // src/services/a2a/a2aV1Wire.ts
120
+ var V1_TO_V03_METHOD = {
121
+ SendMessage: "message/send",
122
+ SendStreamingMessage: "message/stream",
123
+ GetTask: "tasks/get",
124
+ ListTasks: "tasks/list",
125
+ CancelTask: "tasks/cancel",
126
+ SubscribeToTask: "tasks/resubscribe",
127
+ CreateTaskPushNotificationConfig: "tasks/pushNotificationConfig/set",
128
+ GetTaskPushNotificationConfig: "tasks/pushNotificationConfig/get",
129
+ ListTaskPushNotificationConfigs: "tasks/pushNotificationConfig/list",
130
+ DeleteTaskPushNotificationConfig: "tasks/pushNotificationConfig/delete",
131
+ GetExtendedAgentCard: "agent/getAuthenticatedExtendedCard"
132
+ };
133
+ var V1_TO_V03_STATE = {
134
+ TASK_STATE_SUBMITTED: "submitted",
135
+ TASK_STATE_WORKING: "working",
136
+ TASK_STATE_COMPLETED: "completed",
137
+ TASK_STATE_FAILED: "failed",
138
+ TASK_STATE_CANCELED: "canceled",
139
+ TASK_STATE_INPUT_REQUIRED: "input-required",
140
+ TASK_STATE_REJECTED: "rejected",
141
+ TASK_STATE_AUTH_REQUIRED: "auth-required",
142
+ TASK_STATE_UNSPECIFIED: "unknown"
143
+ };
144
+ var V03_TO_V1_STATE = {
145
+ submitted: "TASK_STATE_SUBMITTED",
146
+ working: "TASK_STATE_WORKING",
147
+ completed: "TASK_STATE_COMPLETED",
148
+ failed: "TASK_STATE_FAILED",
149
+ canceled: "TASK_STATE_CANCELED",
150
+ "input-required": "TASK_STATE_INPUT_REQUIRED",
151
+ rejected: "TASK_STATE_REJECTED",
152
+ "auth-required": "TASK_STATE_AUTH_REQUIRED",
153
+ unknown: "TASK_STATE_UNSPECIFIED"
154
+ };
155
+ function asRecord(value) {
156
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
157
+ }
158
+ function asString(value) {
159
+ return typeof value === "string" && value.length > 0 ? value : void 0;
160
+ }
161
+ function asArray(value) {
162
+ return Array.isArray(value) ? value : void 0;
163
+ }
164
+ function withMetadata(value, metadata) {
165
+ return typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? { ...value, metadata } : value;
166
+ }
167
+ function v1TaskStateTo03(value) {
168
+ if (typeof value !== "string") return "unknown";
169
+ return V1_TO_V03_STATE[value] ?? value;
170
+ }
171
+ function v03TaskStateToV1(value) {
172
+ if (typeof value !== "string") return "TASK_STATE_UNSPECIFIED";
173
+ return V03_TO_V1_STATE[value] ?? "TASK_STATE_UNSPECIFIED";
174
+ }
175
+ function toV03Part(part) {
176
+ const record = asRecord(part);
177
+ if (!record) return void 0;
178
+ if (typeof record.text === "string") {
179
+ return withMetadata({ kind: "text", text: record.text }, record.metadata);
180
+ }
181
+ if (typeof record.raw === "string") {
182
+ return withMetadata({
183
+ kind: "file",
184
+ file: {
185
+ bytes: record.raw,
186
+ ...typeof record.mediaType === "string" ? { mimeType: record.mediaType } : {},
187
+ ...typeof record.filename === "string" ? { name: record.filename } : {}
188
+ }
189
+ }, record.metadata);
190
+ }
191
+ if (typeof record.url === "string") {
192
+ return withMetadata({
193
+ kind: "file",
194
+ file: {
195
+ uri: record.url,
196
+ ...typeof record.mediaType === "string" ? { mimeType: record.mediaType } : {},
197
+ ...typeof record.filename === "string" ? { name: record.filename } : {}
198
+ }
199
+ }, record.metadata);
200
+ }
201
+ if ("data" in record) {
202
+ return withMetadata({ kind: "data", data: record.data }, record.metadata);
203
+ }
204
+ if (record.kind === "text" && typeof record.text === "string") {
205
+ return withMetadata({ kind: "text", text: record.text }, record.metadata);
206
+ }
207
+ if (record.kind === "file") {
208
+ const file = asRecord(record.file);
209
+ if (!file) return void 0;
210
+ return withMetadata({ kind: "file", file }, record.metadata);
211
+ }
212
+ if (record.kind === "data") {
213
+ return withMetadata({ kind: "data", data: record.data }, record.metadata);
214
+ }
215
+ return void 0;
216
+ }
217
+ function toV1Part(part) {
218
+ if (part.kind === "text") {
219
+ return withMetadata({ text: part.text }, part.metadata);
220
+ }
221
+ if (part.kind === "data") {
222
+ return withMetadata({ data: part.data }, part.metadata);
223
+ }
224
+ const file = part.file;
225
+ const base = "uri" in file ? {
226
+ url: file.uri,
227
+ ...file.mimeType ? { mediaType: file.mimeType } : {},
228
+ ...file.name ? { filename: file.name } : {}
229
+ } : {
230
+ raw: file.bytes,
231
+ ...file.mimeType ? { mediaType: file.mimeType } : {},
232
+ ...file.name ? { filename: file.name } : {}
233
+ };
234
+ return withMetadata(base, part.metadata);
235
+ }
236
+ function v1RoleTo03(value) {
237
+ if (value === "ROLE_USER" || value === "user" || value === 1) return "user";
238
+ return "agent";
239
+ }
240
+ function v03RoleToV1(value) {
241
+ return value === "user" ? "ROLE_USER" : "ROLE_AGENT";
242
+ }
243
+ function toV03Message(value) {
244
+ const record = asRecord(value);
245
+ if (!record) return void 0;
246
+ const parts = (asArray(record.parts) ?? []).map(toV03Part).filter((part) => part !== void 0);
247
+ return {
248
+ kind: "message",
249
+ messageId: asString(record.messageId) ?? "",
250
+ role: v1RoleTo03(record.role),
251
+ parts,
252
+ ...asString(record.contextId) ? { contextId: asString(record.contextId) } : {},
253
+ ...asString(record.taskId) ? { taskId: asString(record.taskId) } : {},
254
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {},
255
+ ...asArray(record.extensions) ? { extensions: record.extensions } : {},
256
+ ...asArray(record.referenceTaskIds) ? { referenceTaskIds: record.referenceTaskIds } : {}
257
+ };
258
+ }
259
+ function toV1Message(message) {
260
+ return {
261
+ messageId: message.messageId,
262
+ ...message.contextId ? { contextId: message.contextId } : {},
263
+ ...message.taskId ? { taskId: message.taskId } : {},
264
+ role: v03RoleToV1(message.role),
265
+ parts: message.parts.map(toV1Part),
266
+ ...message.metadata ? { metadata: message.metadata } : {},
267
+ ...message.extensions?.length ? { extensions: message.extensions } : {},
268
+ ...message.referenceTaskIds?.length ? { referenceTaskIds: message.referenceTaskIds } : {}
269
+ };
270
+ }
271
+ function toV03Status(value) {
272
+ const record = asRecord(value);
273
+ if (!record) return void 0;
274
+ return {
275
+ state: v1TaskStateTo03(record.state),
276
+ ...asRecord(record.message) ? { message: toV03Message(record.message) } : {},
277
+ ...asString(record.timestamp) ? { timestamp: asString(record.timestamp) } : {}
278
+ };
279
+ }
280
+ function toV1Status(status) {
281
+ return {
282
+ state: v03TaskStateToV1(status.state),
283
+ ...status.message ? { message: toV1Message(status.message) } : {},
284
+ ...status.timestamp ? { timestamp: status.timestamp } : {}
285
+ };
286
+ }
287
+ function toV03Artifact(value) {
288
+ const record = asRecord(value);
289
+ if (!record) return void 0;
290
+ const parts = (asArray(record.parts) ?? []).map(toV03Part).filter((part) => part !== void 0);
291
+ return {
292
+ artifactId: asString(record.artifactId) ?? "",
293
+ ...asString(record.name) ? { name: asString(record.name) } : {},
294
+ ...asString(record.description) ? { description: asString(record.description) } : {},
295
+ parts,
296
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {}
297
+ };
298
+ }
299
+ function toV1Artifact(artifact) {
300
+ return {
301
+ artifactId: artifact.artifactId,
302
+ ...artifact.name ? { name: artifact.name } : {},
303
+ ...artifact.description ? { description: artifact.description } : {},
304
+ parts: artifact.parts.map(toV1Part),
305
+ ...artifact.metadata ? { metadata: artifact.metadata } : {}
306
+ };
307
+ }
308
+ function toV03Task(value) {
309
+ const record = asRecord(value);
310
+ if (!record) return void 0;
311
+ const status = toV03Status(record.status);
312
+ if (!status) return void 0;
313
+ const rawArtifacts = asArray(record.artifacts);
314
+ const rawHistory = asArray(record.history);
315
+ return {
316
+ kind: "task",
317
+ id: asString(record.id) ?? "",
318
+ contextId: asString(record.contextId) ?? "",
319
+ status,
320
+ ...rawArtifacts ? {
321
+ artifacts: rawArtifacts.map(toV03Artifact).filter((artifact) => artifact !== void 0)
322
+ } : {},
323
+ ...rawHistory ? {
324
+ history: rawHistory.map(toV03Message).filter((message) => message !== void 0)
325
+ } : {},
326
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {}
327
+ };
328
+ }
329
+ function toV1Task(task) {
330
+ return {
331
+ id: task.id,
332
+ contextId: task.contextId,
333
+ status: toV1Status(task.status),
334
+ ...task.artifacts?.length ? { artifacts: task.artifacts.map(toV1Artifact) } : {},
335
+ ...task.history?.length ? { history: task.history.map(toV1Message) } : {},
336
+ ...task.metadata ? { metadata: task.metadata } : {}
337
+ };
338
+ }
339
+ function toV03PushConfig(value) {
340
+ const record = asRecord(value);
341
+ if (!record) return void 0;
342
+ const authentication = asRecord(record.authentication);
343
+ return {
344
+ ...asString(record.id) ? { id: asString(record.id) } : {},
345
+ url: asString(record.url) ?? "",
346
+ ...asString(record.token) ? { token: asString(record.token) } : {},
347
+ ...authentication ? {
348
+ authentication: {
349
+ ...asString(authentication.scheme) ? { schemes: [authentication.scheme] } : {},
350
+ ...asString(authentication.credentials) ? { credentials: authentication.credentials } : {}
351
+ }
352
+ } : {}
353
+ };
354
+ }
355
+ function toV1PushConfig(value) {
356
+ const record = asRecord(value);
357
+ if (!record) return void 0;
358
+ const config = asRecord(record.pushNotificationConfig) ?? record;
359
+ const authentication = asRecord(config.authentication);
360
+ const schemes = asArray(authentication?.schemes);
361
+ return {
362
+ ...asString(record.taskId) ? { taskId: asString(record.taskId) } : {},
363
+ ...asString(config.id) ? { id: asString(config.id) } : {},
364
+ ...asString(config.url) ? { url: asString(config.url) } : {},
365
+ ...asString(config.token) ? { token: asString(config.token) } : {},
366
+ ...schemes?.[0] || asString(authentication?.credentials) ? {
367
+ authentication: {
368
+ ...typeof schemes?.[0] === "string" ? { scheme: schemes[0] } : {},
369
+ ...asString(authentication?.credentials) ? { credentials: asString(authentication?.credentials) } : {}
370
+ }
371
+ } : {}
372
+ };
373
+ }
374
+ function convertParams(method, params) {
375
+ const record = asRecord(params);
376
+ if (!record) return params;
377
+ if (method === "SendMessage" || method === "SendStreamingMessage") {
378
+ const configuration = asRecord(record.configuration);
379
+ const push = asRecord(configuration?.taskPushNotificationConfig);
380
+ return {
381
+ ...asRecord(record.message) ? { message: toV03Message(record.message) } : {},
382
+ ...configuration ? {
383
+ configuration: {
384
+ ...asArray(configuration.acceptedOutputModes) ? { acceptedOutputModes: configuration.acceptedOutputModes } : {},
385
+ blocking: configuration.returnImmediately !== true,
386
+ ...typeof configuration.historyLength === "number" ? { historyLength: configuration.historyLength } : {},
387
+ ...push ? { pushNotificationConfig: toV03PushConfig(push) } : {}
388
+ }
389
+ } : {},
390
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {}
391
+ };
392
+ }
393
+ if (method === "ListTasks") {
394
+ return {
395
+ ...asString(record.contextId) ? { contextId: asString(record.contextId) } : {},
396
+ ...record.status !== void 0 ? { status: v1TaskStateTo03(record.status) } : {},
397
+ ...typeof record.pageSize === "number" ? { pageSize: record.pageSize } : {},
398
+ ...asString(record.pageToken) ? { pageToken: asString(record.pageToken) } : {},
399
+ ...typeof record.historyLength === "number" ? { historyLength: record.historyLength } : {},
400
+ ...asString(record.statusTimestampAfter) ? { statusTimestampAfter: asString(record.statusTimestampAfter) } : {},
401
+ ...typeof record.includeArtifacts === "boolean" ? { includeArtifacts: record.includeArtifacts } : {}
402
+ };
403
+ }
404
+ if (method === "CreateTaskPushNotificationConfig") {
405
+ const config = toV03PushConfig(record);
406
+ return {
407
+ taskId: asString(record.taskId) ?? "",
408
+ ...config ? { pushNotificationConfig: config } : {}
409
+ };
410
+ }
411
+ if (method === "GetTaskPushNotificationConfig") {
412
+ return {
413
+ id: asString(record.taskId) ?? "",
414
+ ...asString(record.id) ? { pushNotificationConfigId: asString(record.id) } : {}
415
+ };
416
+ }
417
+ if (method === "ListTaskPushNotificationConfigs" || method === "DeleteTaskPushNotificationConfig") {
418
+ return {
419
+ id: asString(record.taskId) ?? "",
420
+ ...asString(record.id) ? { pushNotificationConfigId: asString(record.id) } : {}
421
+ };
422
+ }
423
+ return record;
424
+ }
425
+ function toA2A03JsonRpcRequest(body) {
426
+ const record = asRecord(body);
427
+ const method = asString(record?.method);
428
+ if (!record || !method) return body;
429
+ const mapped = V1_TO_V03_METHOD[method];
430
+ if (!mapped) return body;
431
+ return {
432
+ ...record,
433
+ method: mapped,
434
+ params: convertParams(method, record.params)
435
+ };
436
+ }
437
+ function toA2A11Result(value, method) {
438
+ const record = asRecord(value);
439
+ if (!record) return value;
440
+ if (record.kind === "task") {
441
+ const task = toV03Task(record);
442
+ return task ? toV1Task(task) : value;
443
+ }
444
+ if (record.kind === "message") {
445
+ const message = toV03Message(record);
446
+ return message ? toV1Message(message) : value;
447
+ }
448
+ if (method === "CreateTaskPushNotificationConfig" || method === "GetTaskPushNotificationConfig") {
449
+ return toV1PushConfig(record);
450
+ }
451
+ return value;
452
+ }
453
+ function toA2A11StreamResult(event) {
454
+ const record = asRecord(event);
455
+ if (!record) return event;
456
+ if (record.kind === "task") {
457
+ const task = toV03Task(record);
458
+ return task ? { task: toV1Task(task) } : event;
459
+ }
460
+ if (record.kind === "message") {
461
+ const message = toV03Message(record);
462
+ return message ? { message: toV1Message(message) } : event;
463
+ }
464
+ if (record.kind === "status-update") {
465
+ const status = toV03Status(record.status);
466
+ return {
467
+ statusUpdate: {
468
+ taskId: asString(record.taskId) ?? "",
469
+ contextId: asString(record.contextId) ?? "",
470
+ ...status ? { status: toV1Status(status) } : {},
471
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {}
472
+ }
473
+ };
474
+ }
475
+ if (record.kind === "artifact-update") {
476
+ const artifact = toV03Artifact(record.artifact);
477
+ return {
478
+ artifactUpdate: {
479
+ taskId: asString(record.taskId) ?? "",
480
+ contextId: asString(record.contextId) ?? "",
481
+ ...artifact ? { artifact: toV1Artifact(artifact) } : {},
482
+ ...record.append === true ? { append: true } : {},
483
+ ...record.lastChunk === true ? { lastChunk: true } : {},
484
+ ...typeof record.metadata === "object" && record.metadata !== null ? { metadata: record.metadata } : {}
485
+ }
486
+ };
487
+ }
488
+ return event;
489
+ }
490
+ function toA2A11ListResult(value, options = {}) {
491
+ const record = asRecord(value);
492
+ const tasks = asArray(record?.tasks) ?? [];
493
+ return {
494
+ tasks: tasks.map(toV03Task).filter((task) => task !== void 0).map((task) => {
495
+ const converted = toV1Task(task);
496
+ if (options.includeArtifacts !== true) delete converted.artifacts;
497
+ return converted;
498
+ }),
499
+ ...asString(record?.nextPageToken) ? { nextPageToken: asString(record?.nextPageToken) } : {}
500
+ };
501
+ }
502
+ function toA2A11PushListResult(value) {
503
+ const configs = asArray(value) ?? [];
504
+ return {
505
+ configs: configs.map(toV1PushConfig).filter((config) => config !== void 0)
506
+ };
507
+ }
508
+ function versionNotSupportedError(id, version) {
509
+ return {
510
+ jsonrpc: "2.0",
511
+ id: id ?? null,
512
+ error: {
513
+ code: -32009,
514
+ message: `The requested A2A protocol version '${version}' is not supported. Supported versions: 1.0`
515
+ }
516
+ };
517
+ }
518
+
519
+ // src/services/a2a/A2APushNotificationStore.ts
520
+ import { decrypt, encrypt } from "@axiom-lattice/core";
521
+
522
+ // src/services/a2a/A2APushNotificationUrl.ts
523
+ import { lookup } from "dns/promises";
524
+ import { A2AError } from "@a2a-js/sdk/server";
525
+ import { assertSafeUpstreamUrl } from "@axiom-lattice/core";
526
+ var defaultA2APushHostResolver = async (hostname) => {
527
+ const records = await lookup(hostname, { all: true });
528
+ return records.map((record) => ({
529
+ address: record.address,
530
+ family: record.family
531
+ }));
532
+ };
533
+ function normalizeHost(value) {
534
+ return value.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "").replace(/\.$/, "").split("%")[0] ?? "";
535
+ }
536
+ function ipv4Parts(hostname) {
537
+ const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
538
+ if (!match) return null;
539
+ const parts = match.slice(1).map(Number);
540
+ return parts.every((part) => part >= 0 && part <= 255) ? parts : null;
541
+ }
542
+ function isReservedIpv4(hostname) {
543
+ const parts = ipv4Parts(hostname);
544
+ if (!parts) return false;
545
+ const [a, b, c] = parts;
546
+ if (a === 0 || a === 10 || a === 127) return true;
547
+ if (a === 100 && b >= 64 && b <= 127) return true;
548
+ if (a === 169 && b === 254) return true;
549
+ if (a === 172 && b >= 16 && b <= 31) return true;
550
+ if (a === 192 && b === 0 && c === 0) return true;
551
+ if (a === 192 && b === 0 && c === 2) return true;
552
+ if (a === 192 && b === 88 && c === 99) return true;
553
+ if (a === 192 && b === 168) return true;
554
+ if (a === 198 && (b === 18 || b === 19)) return true;
555
+ if (a === 198 && b === 51 && c === 100) return true;
556
+ if (a === 203 && b === 0 && c === 113) return true;
557
+ return a >= 224;
558
+ }
559
+ function isReservedIpv6(hostname) {
560
+ const host = normalizeHost(hostname);
561
+ if (!host.includes(":")) return false;
562
+ if (host.startsWith("::")) return true;
563
+ if (host.startsWith("fc") || host.startsWith("fd")) return true;
564
+ if (/^fe[89ab]/.test(host)) return true;
565
+ if (/^fec[0-9a-f]/.test(host)) return true;
566
+ if (host.startsWith("ff")) return true;
567
+ if (host.startsWith("100:")) return true;
568
+ if (host.startsWith("2001:db8")) return true;
569
+ return false;
570
+ }
571
+ function isBlockedPushAddress(address) {
572
+ const host = normalizeHost(address);
573
+ if (ipv4Parts(host)) return isReservedIpv4(host);
574
+ if (!host.includes(":")) return true;
575
+ return isReservedIpv6(host);
576
+ }
577
+ function isBlockedHostname(hostname) {
578
+ const host = normalizeHost(hostname);
579
+ if (host === "localhost" || host === "localhost.localdomain" || host.endsWith(".localhost") || host === "ip6-localhost" || host === "ip6-loopback") {
580
+ return true;
581
+ }
582
+ if (host === "metadata" || host === "metadata.google.internal" || host === "metadata.google.com" || host === "instance-data" || host === "instance-data.ec2.internal") {
583
+ return true;
584
+ }
585
+ return isReservedIpv4(host) || isReservedIpv6(host);
586
+ }
587
+ function toHostAddress(value) {
588
+ if (typeof value === "string") {
589
+ return { address: value, family: value.includes(":") ? 6 : 4 };
590
+ }
591
+ if (typeof value !== "object" || value === null) return null;
592
+ const record = value;
593
+ if (typeof record.address !== "string") return null;
594
+ return {
595
+ address: record.address,
596
+ family: typeof record.family === "number" ? record.family : record.address.includes(":") ? 6 : 4
597
+ };
598
+ }
599
+ async function assertSafePushNotificationUrl(raw, resolveHost = defaultA2APushHostResolver) {
600
+ let url;
601
+ try {
602
+ ({ url } = await assertSafeUpstreamUrl(raw));
603
+ } catch (error) {
604
+ throw A2AError.invalidParams(
605
+ error instanceof Error ? `Push notification URL is not allowed: ${error.message}` : "Push notification URL is not allowed"
606
+ );
607
+ }
608
+ if (url.protocol !== "https:") {
609
+ throw A2AError.invalidParams("Push notification URL must use HTTPS");
610
+ }
611
+ const hostname = normalizeHost(url.hostname);
612
+ if (isBlockedHostname(hostname)) {
613
+ throw A2AError.invalidParams(
614
+ "Push notification URL must not target a private, loopback, link-local, or metadata host"
615
+ );
616
+ }
617
+ let addresses;
618
+ try {
619
+ addresses = await resolveHost(hostname);
620
+ } catch (error) {
621
+ throw A2AError.invalidParams(
622
+ error instanceof Error ? `Push notification URL could not be resolved: ${error.message}` : "Push notification URL could not be resolved"
623
+ );
624
+ }
625
+ if (addresses.length === 0) {
626
+ throw A2AError.invalidParams("Push notification URL did not resolve");
627
+ }
628
+ const normalized = [];
629
+ for (const record of addresses) {
630
+ const resolved = toHostAddress(record);
631
+ if (!resolved) {
632
+ throw A2AError.invalidParams(
633
+ "Push notification URL resolved to an invalid address"
634
+ );
635
+ }
636
+ const address = normalizeHost(resolved.address);
637
+ if (isBlockedPushAddress(address)) {
638
+ throw A2AError.invalidParams(
639
+ "Push notification URL resolves to a private, loopback, link-local, or reserved address"
640
+ );
641
+ }
642
+ normalized.push({ address, family: resolved.family });
643
+ }
644
+ return { url, addresses: normalized };
645
+ }
646
+
647
+ // src/services/a2a/A2APushNotificationStore.ts
648
+ var NAMESPACE_ROOT = "a2a-push";
649
+ var PAGE_SIZE = 100;
650
+ function isRecord(value) {
651
+ return typeof value === "object" && value !== null && !Array.isArray(value);
652
+ }
653
+ function normalizeConfigId(taskId, value) {
654
+ if (typeof value !== "string") return taskId;
655
+ return value.trim() || taskId;
656
+ }
657
+ var A2APushNotificationStore = class {
658
+ constructor(options) {
659
+ this.store = options.store;
660
+ this.scope = options.scope;
661
+ this.resolveHost = options.resolveHost ?? defaultA2APushHostResolver;
662
+ }
663
+ namespace() {
664
+ return [NAMESPACE_ROOT, this.scope.tenantId, this.scope.assistantId];
665
+ }
666
+ key(taskId, configId) {
667
+ return `${taskId}:${configId}`;
668
+ }
669
+ async save(taskId, pushNotificationConfig) {
670
+ await assertSafePushNotificationUrl(
671
+ pushNotificationConfig.url,
672
+ this.resolveHost
673
+ );
674
+ const configId = normalizeConfigId(taskId, pushNotificationConfig.id);
675
+ const value = {
676
+ taskId,
677
+ configId,
678
+ url: pushNotificationConfig.url
679
+ };
680
+ const token = pushNotificationConfig.token;
681
+ if (typeof token === "string" && token.length > 0) {
682
+ value.tokenEnc = encrypt(token);
683
+ }
684
+ const authentication = pushNotificationConfig.authentication;
685
+ if (authentication) {
686
+ const schemes = Array.isArray(authentication.schemes) ? authentication.schemes.filter((scheme) => typeof scheme === "string") : [];
687
+ const storedAuth = { schemes };
688
+ if (typeof authentication.credentials === "string" && authentication.credentials.length > 0) {
689
+ storedAuth.credentialsEnc = encrypt(authentication.credentials);
690
+ }
691
+ value.authentication = storedAuth;
692
+ }
693
+ await this.store.put(this.scope.tenantId, {
694
+ namespace: this.namespace(),
695
+ key: this.key(taskId, configId),
696
+ value
697
+ });
698
+ }
699
+ async load(taskId) {
700
+ const prefix = `${taskId}:`;
701
+ const items = await this.searchTask(taskId);
702
+ const configs = [];
703
+ for (const item of items) {
704
+ if (!item.key.startsWith(prefix)) continue;
705
+ try {
706
+ configs.push(await this.parse(item, taskId));
707
+ } catch (error) {
708
+ console.warn(
709
+ `[a2a] skipping invalid push notification config "${item.key}"`,
710
+ error
711
+ );
712
+ }
713
+ }
714
+ return configs.sort((left, right) => (left.id ?? "").localeCompare(right.id ?? ""));
715
+ }
716
+ /**
717
+ * Read every config for a task. The generic store limit is only a page size;
718
+ * this walks offsets so a task with more than 1000 configs is never truncated.
719
+ */
720
+ async searchTask(taskId) {
721
+ const items = [];
722
+ let offset = 0;
723
+ for (; ; ) {
724
+ const page = await this.store.search(this.scope.tenantId, {
725
+ namespacePrefix: this.namespace(),
726
+ filter: { taskId },
727
+ limit: PAGE_SIZE,
728
+ offset
729
+ });
730
+ items.push(...page);
731
+ if (page.length < PAGE_SIZE) return items;
732
+ offset += page.length;
733
+ }
734
+ }
735
+ async parse(item, taskId) {
736
+ const value = item.value;
737
+ if (typeof value.url !== "string") {
738
+ throw new Error("stored config has no URL");
739
+ }
740
+ await assertSafePushNotificationUrl(value.url, this.resolveHost);
741
+ const configId = normalizeConfigId(
742
+ taskId,
743
+ typeof value.configId === "string" ? value.configId : item.key.slice(taskId.length + 1)
744
+ );
745
+ const config = { id: configId, url: value.url };
746
+ if (typeof value.tokenEnc === "string" && value.tokenEnc.length > 0) {
747
+ config.token = decrypt(value.tokenEnc);
748
+ }
749
+ if (isRecord(value.authentication)) {
750
+ const schemes = Array.isArray(value.authentication.schemes) ? value.authentication.schemes.filter(
751
+ (scheme) => typeof scheme === "string"
752
+ ) : [];
753
+ const authentication = {
754
+ schemes
755
+ };
756
+ if (typeof value.authentication.credentialsEnc === "string" && value.authentication.credentialsEnc.length > 0) {
757
+ authentication.credentials = decrypt(value.authentication.credentialsEnc);
758
+ }
759
+ config.authentication = authentication;
760
+ }
761
+ return config;
762
+ }
763
+ /**
764
+ * Delete one config. The SDK always supplies a config id; when it is omitted
765
+ * the default config is `${taskId}:${taskId}` (not every config for the task).
766
+ */
767
+ async delete(taskId, configId) {
768
+ await this.store.delete(
769
+ this.scope.tenantId,
770
+ this.namespace(),
771
+ this.key(taskId, normalizeConfigId(taskId, configId))
772
+ );
773
+ }
774
+ };
775
+
776
+ // src/services/a2a/A2APushNotificationSender.ts
777
+ import { request as httpsRequest } from "https";
778
+ var DEFAULT_TIMEOUT_MS = 5e3;
779
+ var TOKEN_HEADER = "X-A2A-Notification-Token";
780
+ function withTimeout(promise, timeoutMs) {
781
+ return new Promise((resolve, reject) => {
782
+ const timer = setTimeout(() => {
783
+ reject(new Error("Push notification request timed out"));
784
+ }, timeoutMs);
785
+ promise.then(
786
+ (value) => {
787
+ clearTimeout(timer);
788
+ resolve(value);
789
+ },
790
+ (error) => {
791
+ clearTimeout(timer);
792
+ reject(error);
793
+ }
794
+ );
795
+ });
796
+ }
797
+ function createPinnedLookup(address, family) {
798
+ return (_hostname, options, callback) => {
799
+ if (options?.all) {
800
+ callback(null, [{ address, family }]);
801
+ return;
802
+ }
803
+ callback(null, address, family);
804
+ };
805
+ }
806
+ function createPinnedHttpsRequester() {
807
+ return ({ url, addresses, headers, body, timeoutMs }) => new Promise((resolve, reject) => {
808
+ const pinned = addresses[0];
809
+ if (!pinned) {
810
+ reject(new Error("No validated push notification address"));
811
+ return;
812
+ }
813
+ const parsed = new URL(url);
814
+ const family = pinned.includes(":") ? 6 : 4;
815
+ const request = httpsRequest(parsed, {
816
+ method: "POST",
817
+ agent: false,
818
+ headers: {
819
+ ...headers,
820
+ "Content-Length": Buffer.byteLength(body)
821
+ },
822
+ lookup: createPinnedLookup(pinned, family),
823
+ servername: parsed.hostname
824
+ }, (response) => {
825
+ response.resume();
826
+ resolve({ statusCode: response.statusCode ?? 0 });
827
+ });
828
+ const timeout = setTimeout(() => {
829
+ request.destroy(new Error("Push notification request timed out"));
830
+ }, timeoutMs);
831
+ request.on("close", () => clearTimeout(timeout));
832
+ request.on("error", (error) => {
833
+ clearTimeout(timeout);
834
+ reject(error);
835
+ });
836
+ request.end(body);
837
+ });
838
+ }
839
+ var A2APushNotificationSender = class {
840
+ constructor(options) {
841
+ this.store = options.store;
842
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
843
+ this.request = options.request ?? createPinnedHttpsRequester();
844
+ this.resolveHost = options.resolveHost ?? defaultA2APushHostResolver;
845
+ this.logger = options.logger;
846
+ }
847
+ async send(task) {
848
+ let configs;
849
+ try {
850
+ configs = await this.store.load(task.id);
851
+ } catch (error) {
852
+ this.logger?.error?.(
853
+ `[a2a] failed to load push notification configs for task ${task.id}`,
854
+ error
855
+ );
856
+ return;
857
+ }
858
+ if (!configs || configs.length === 0) return;
859
+ await Promise.all(configs.map(async (config) => {
860
+ await this.deliver(task, config);
861
+ }));
862
+ }
863
+ async deliver(task, config) {
864
+ let target;
865
+ try {
866
+ target = await assertSafePushNotificationUrl(config.url, this.resolveHost);
867
+ } catch (error) {
868
+ this.logger?.warn?.(
869
+ `[a2a] skipping unsafe push notification URL for task ${task.id}`,
870
+ error
871
+ );
872
+ return;
873
+ }
874
+ try {
875
+ const headers = {
876
+ "Content-Type": "application/json"
877
+ };
878
+ if (config.token) {
879
+ headers[TOKEN_HEADER] = config.token;
880
+ }
881
+ const authentication = config.authentication;
882
+ if (authentication?.credentials) {
883
+ const schemes = authentication.schemes.map((scheme) => scheme.toLowerCase());
884
+ if (schemes.includes("bearer")) {
885
+ headers.Authorization = `Bearer ${authentication.credentials}`;
886
+ } else if (schemes.includes("basic")) {
887
+ headers.Authorization = `Basic ${authentication.credentials}`;
888
+ }
889
+ }
890
+ const response = await withTimeout(this.request({
891
+ url: target.url.toString(),
892
+ addresses: target.addresses.map((record) => record.address),
893
+ headers,
894
+ body: JSON.stringify(task),
895
+ timeoutMs: this.timeoutMs
896
+ }), this.timeoutMs);
897
+ if (response.statusCode < 200 || response.statusCode >= 300) {
898
+ throw new Error(
899
+ `Push notification endpoint returned HTTP ${response.statusCode}`
900
+ );
901
+ }
902
+ } catch (error) {
903
+ this.logger?.error?.(
904
+ `[a2a] push notification failed for task ${task.id} to ${config.url}`,
905
+ error
906
+ );
907
+ }
908
+ }
909
+ };
910
+
911
+ // src/services/a2a/A2ATaskStoreAdapter.ts
912
+ import { createHash } from "crypto";
913
+ import { isDeepStrictEqual } from "util";
914
+ import {
915
+ createTaskRunBridge
916
+ } from "@axiom-lattice/core";
917
+ var TASK_STATUS_TO_A2A_STATE = {
918
+ pending: "submitted",
919
+ in_progress: "working",
920
+ review: "input-required",
921
+ interrupted: "input-required",
922
+ completed: "completed",
923
+ failed: "failed",
924
+ cancelled: "canceled"
925
+ };
926
+ var TITLE_MAX_LENGTH = 80;
927
+ var A2ATaskScopeConflictError = class extends Error {
928
+ constructor(taskId) {
929
+ super(`A2A task ${taskId} belongs to another scope`);
930
+ this.code = "A2A_TASK_SCOPE_CONFLICT";
931
+ this.name = "A2ATaskScopeConflictError";
932
+ this.taskId = taskId;
933
+ }
934
+ };
935
+ function mapTaskStatusToA2AState(status) {
936
+ return TASK_STATUS_TO_A2A_STATE[status];
937
+ }
938
+ function asString2(value) {
939
+ return typeof value === "string" ? value : void 0;
940
+ }
941
+ function asRecord2(value) {
942
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
943
+ }
944
+ function asStringArray(value) {
945
+ if (!Array.isArray(value)) return void 0;
946
+ const strings = value.filter((item) => typeof item === "string");
947
+ return strings.length > 0 ? strings : void 0;
948
+ }
949
+ function extractText(parts) {
950
+ return parts.filter((part) => part.kind === "text").map((part) => part.text).join("\n");
951
+ }
952
+ function truncate(text, maxLength) {
953
+ if (text.length <= maxLength) return text;
954
+ return `${text.slice(0, maxLength)}\u2026`;
955
+ }
956
+ function extractTitleAndDescription(task) {
957
+ const firstUserMessage = task.history?.find((message) => message.role === "user");
958
+ const text = firstUserMessage ? extractText(firstUserMessage.parts) : "";
959
+ return {
960
+ title: truncate(text, TITLE_MAX_LENGTH),
961
+ description: text ? [
962
+ "## Objective",
963
+ "",
964
+ text,
965
+ "",
966
+ "## Acceptance Criteria",
967
+ "",
968
+ "Complete the requested work and return the result through A2A."
969
+ ].join("\n") : ""
970
+ };
971
+ }
972
+ function isFileRef(value) {
973
+ return typeof value === "object" && value !== null && typeof value.uri === "string";
974
+ }
975
+ function artifactFileRefs(artifacts) {
976
+ const refs = [];
977
+ for (const artifact of artifacts ?? []) {
978
+ for (const part of artifact.parts) {
979
+ if (part.kind !== "file") continue;
980
+ const file = part.file;
981
+ if (!("uri" in file)) continue;
982
+ refs.push({
983
+ uri: file.uri,
984
+ ...file.name ? { name: file.name } : {},
985
+ ...file.mimeType ? { mimeType: file.mimeType } : {},
986
+ addedBy: "agent"
987
+ });
988
+ }
989
+ }
990
+ return refs;
991
+ }
992
+ function extractFiles(task) {
993
+ const inputFiles = task.metadata?.inputFiles;
994
+ const inputRefs = Array.isArray(inputFiles) ? inputFiles.filter(isFileRef) : [];
995
+ return [...inputRefs, ...artifactFileRefs(task.artifacts)];
996
+ }
997
+ function extractArtifactText(artifacts) {
998
+ return (artifacts ?? []).map((artifact) => extractText(artifact.parts)).filter((text) => text.length > 0).join("\n");
999
+ }
1000
+ function extractFinalTurnAgentText(history) {
1001
+ const messages = history ?? [];
1002
+ let lastUserIndex = -1;
1003
+ for (let i = 0; i < messages.length; i += 1) {
1004
+ if (messages[i].role === "user") lastUserIndex = i;
1005
+ }
1006
+ return messages.slice(lastUserIndex + 1).filter((message) => message.role === "agent").map((message) => extractText(message.parts)).filter((text) => text.length > 0).join("");
1007
+ }
1008
+ function isFailureState(state) {
1009
+ return state === "failed" || state === "rejected";
1010
+ }
1011
+ function extractFailureReason(task) {
1012
+ if (!isFailureState(task.status.state) || !task.status.message) return void 0;
1013
+ return extractText(task.status.message.parts);
1014
+ }
1015
+ function observationEventKey(task, result, failureReason) {
1016
+ const digest = createHash("sha256").update(JSON.stringify({
1017
+ state: task.status.state,
1018
+ timestamp: task.status.timestamp,
1019
+ message: task.status.message,
1020
+ result,
1021
+ failureReason
1022
+ })).digest("hex");
1023
+ return `a2a-status-observation:${task.id}:${digest}`;
1024
+ }
1025
+ function snapshotEventKey(task) {
1026
+ const digest = createHash("sha256").update(JSON.stringify(task)).digest("hex");
1027
+ return `a2a-task-snapshot:${task.id}:${digest}`;
1028
+ }
1029
+ function isGovernedObservation(state) {
1030
+ return state === "completed" || state === "failed" || state === "rejected" || state === "input-required" || state === "canceled";
1031
+ }
1032
+ function isA2ATaskSnapshot(value) {
1033
+ return typeof value === "object" && value !== null && typeof value.id === "string" && typeof value.status === "object" && value.status !== null;
1034
+ }
1035
+ function snapshotSequence(value) {
1036
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1037
+ }
1038
+ var A2ATaskStoreAdapter = class {
1039
+ constructor(options) {
1040
+ this.snapshotSequences = /* @__PURE__ */ new Map();
1041
+ this.taskStore = options.taskStore;
1042
+ this.workItemStore = options.workItemStore;
1043
+ this.lifecycle = options.lifecycle;
1044
+ this.scope = options.scope;
1045
+ this.runBridge = createTaskRunBridge({
1046
+ lifecycle: this.lifecycle,
1047
+ taskStore: this.taskStore,
1048
+ workItemStore: this.workItemStore
1049
+ });
1050
+ }
1051
+ /**
1052
+ * Persist an SDK task by projecting it onto a `TaskItem`, creating it when
1053
+ * the id is unseen or updating it otherwise. A full snapshot of the SDK task
1054
+ * is stored under `context.a2aTask` so `load` can restore it.
1055
+ */
1056
+ async save(task) {
1057
+ let lastError;
1058
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1059
+ try {
1060
+ return await this.saveOnce(task);
1061
+ } catch (error) {
1062
+ lastError = error;
1063
+ if (!isA2AConcurrentWrite(error) || attempt === 2) throw error;
1064
+ await new Promise((resolve) => setTimeout(resolve, 10 * (attempt + 1)));
1065
+ }
1066
+ }
1067
+ throw lastError;
1068
+ }
1069
+ async saveOnce(task) {
1070
+ const { tenantId } = this.scope;
1071
+ const { title, description } = extractTitleAndDescription(task);
1072
+ const files = extractFiles(task);
1073
+ const result = task.status.state === "completed" ? extractArtifactText(task.artifacts) || extractFinalTurnAgentText(task.history) : void 0;
1074
+ const failureReason = extractFailureReason(task);
1075
+ const a2aRunId = asString2(task.metadata?.a2aRunId) ?? asString2(task.metadata?.runId);
1076
+ const context = {
1077
+ a2aContextId: task.contextId,
1078
+ ...a2aRunId ? { a2aRunId } : {},
1079
+ threadId: asString2(task.metadata?.threadId),
1080
+ a2aTask: task
1081
+ };
1082
+ let existing = await this.taskStore.getById(tenantId, task.id);
1083
+ if (existing) {
1084
+ this.requireScope(existing, task.id);
1085
+ await this.recordSnapshot(task, existing);
1086
+ existing = await this.persistRunLink(existing, context, a2aRunId, task);
1087
+ } else {
1088
+ try {
1089
+ existing = await this.taskStore.create({
1090
+ id: task.id,
1091
+ tenantId,
1092
+ ownerType: "agent",
1093
+ ownerId: this.scope.assistantId,
1094
+ projectId: this.scope.projectId,
1095
+ ...this.scope.workspaceId ? { workspaceId: this.scope.workspaceId } : {},
1096
+ title,
1097
+ description,
1098
+ sourceId: "a2a",
1099
+ status: "pending",
1100
+ priority: "medium",
1101
+ dependencies: asStringArray(task.metadata?.dependencies),
1102
+ files,
1103
+ context
1104
+ });
1105
+ } catch (error) {
1106
+ existing = await this.taskStore.getById(tenantId, task.id);
1107
+ if (!existing) throw error;
1108
+ this.requireScope(existing, task.id);
1109
+ }
1110
+ await this.recordSnapshot(task, existing);
1111
+ }
1112
+ const actor = `a2a:${this.scope.assistantId}`;
1113
+ const threadId = asString2(task.metadata?.threadId);
1114
+ if (task.status.state === "working") {
1115
+ existing = await this.refresh(existing);
1116
+ const resumesInterruption = existing.status === "interrupted" && task.metadata?.a2aResume === true;
1117
+ if (existing.status === "pending" || resumesInterruption) {
1118
+ await this.requireLifecycleSuccess("start", task.id, await this.runBridge.beginRun({
1119
+ tenantId,
1120
+ task: existing,
1121
+ actor,
1122
+ ...threadId ? { threadId } : {}
1123
+ }));
1124
+ }
1125
+ return;
1126
+ }
1127
+ if (!isGovernedObservation(task.status.state)) return;
1128
+ existing = await this.refresh(existing);
1129
+ await this.recordObservation(task, existing, result, failureReason);
1130
+ if (["completed", "failed", "cancelled"].includes(existing.status)) return;
1131
+ if (task.status.state === "input-required" && this.isExactInterruptionReplay(task, existing)) return;
1132
+ if (task.status.state === "completed" && existing.status === "interrupted") {
1133
+ const resumed = await this.lifecycle.resumeInterruption({
1134
+ tenantId,
1135
+ taskId: task.id,
1136
+ actor,
1137
+ ...threadId ? { threadId } : {}
1138
+ });
1139
+ await this.requireLifecycleSuccess("resume", task.id, resumed);
1140
+ existing = await this.refresh(existing);
1141
+ }
1142
+ if (existing.status === "pending") {
1143
+ const started = await this.requireLifecycleSuccess("start", task.id, await this.runBridge.beginRun({
1144
+ tenantId,
1145
+ task: existing,
1146
+ actor,
1147
+ ...threadId ? { threadId } : {}
1148
+ }));
1149
+ existing = started.task;
1150
+ }
1151
+ await this.applyObservation(task, existing, result, failureReason);
1152
+ }
1153
+ /** Re-read the canonical task immediately before a lifecycle write. */
1154
+ async refresh(fallback) {
1155
+ const current = await this.taskStore.getById(this.scope.tenantId, fallback.id);
1156
+ if (!current) return fallback;
1157
+ this.requireScope(current, fallback.id);
1158
+ return current;
1159
+ }
1160
+ /**
1161
+ * Persist the A2A↔run link on an existing TaskItem without replacing
1162
+ * service-owned context (interruption audit, lifecycle fields, snapshots).
1163
+ */
1164
+ async persistRunLink(item, context, a2aRunId, task) {
1165
+ if (a2aRunId === void 0) return item;
1166
+ const current = item.context ?? {};
1167
+ if (current.a2aRunId === a2aRunId) return item;
1168
+ const next = {
1169
+ ...current,
1170
+ a2aContextId: context.a2aContextId,
1171
+ threadId: context.threadId,
1172
+ a2aRunId
1173
+ };
1174
+ const updated = await this.taskStore.update(this.scope.tenantId, task.id, { context: next });
1175
+ return updated ?? item;
1176
+ }
1177
+ /**
1178
+ * Complete a run result through the lifecycle, resuming an interrupted
1179
+ * TaskItem first when the run succeeded after HITL.
1180
+ *
1181
+ * @throws When the canonical task cannot transition (for example a review
1182
+ * interruption that still requires explicit approval).
1183
+ */
1184
+ async completeRun(task, result) {
1185
+ const { tenantId } = this.scope;
1186
+ const actor = `a2a:${this.scope.assistantId}`;
1187
+ const threadId = asString2(task.metadata?.threadId);
1188
+ let item = await this.taskStore.getById(tenantId, task.id);
1189
+ if (!item) {
1190
+ throw new Error(`A2A completed run cannot reconcile missing task ${task.id}`);
1191
+ }
1192
+ if (item.status === "interrupted") {
1193
+ const resumed = await this.lifecycle.resumeInterruption({
1194
+ tenantId,
1195
+ taskId: task.id,
1196
+ actor,
1197
+ ...threadId ? { threadId } : {}
1198
+ });
1199
+ if (!resumed.success) {
1200
+ throw new Error(
1201
+ `A2A completed run cannot resume interruption: ${resumed.code}: ${resumed.error}`
1202
+ );
1203
+ }
1204
+ item = await this.taskStore.getById(tenantId, task.id);
1205
+ if (!item) throw new Error(`A2A task ${task.id} disappeared after resume`);
1206
+ }
1207
+ if (item.status === "pending") {
1208
+ const started = await this.runBridge.beginRun({
1209
+ tenantId,
1210
+ task: item,
1211
+ actor,
1212
+ ...threadId ? { threadId } : {}
1213
+ });
1214
+ if (!started.success) {
1215
+ throw new Error(
1216
+ `A2A completed run cannot start task: ${started.code}: ${started.error}`
1217
+ );
1218
+ }
1219
+ item = started.task;
1220
+ }
1221
+ if (item.status !== "in_progress" && item.status !== "completed") {
1222
+ throw new Error(
1223
+ `A2A completed run cannot complete canonical ${item.status} task ${task.id}`
1224
+ );
1225
+ }
1226
+ const resultArtifact = {
1227
+ artifactId: `${task.id}:result`,
1228
+ name: "result",
1229
+ parts: [{ kind: "text", text: result.trim() || "(no output)" }]
1230
+ };
1231
+ const completed = {
1232
+ ...task,
1233
+ artifacts: [
1234
+ ...(task.artifacts ?? []).filter(
1235
+ (artifact) => artifact.artifactId !== resultArtifact.artifactId && artifact.name !== "result"
1236
+ ),
1237
+ resultArtifact
1238
+ ],
1239
+ status: { state: "completed", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
1240
+ };
1241
+ await this.save(completed);
1242
+ const loaded = await this.load(task.id);
1243
+ if (!loaded || loaded.status.state !== "completed") {
1244
+ throw new Error(`A2A task ${task.id} did not persist as completed`);
1245
+ }
1246
+ return loaded;
1247
+ }
1248
+ async recordSnapshot(task, item) {
1249
+ const incomingSequence = snapshotSequence(task.metadata?.snapshotSequence) ?? 0;
1250
+ const currentSequence = this.snapshotSequences.get(task.id) ?? 0;
1251
+ const sequence = Math.max(incomingSequence, currentSequence) + 1;
1252
+ this.snapshotSequences.set(task.id, sequence);
1253
+ const sequencedTask = {
1254
+ ...task,
1255
+ metadata: { ...task.metadata ?? {}, snapshotSequence: sequence }
1256
+ };
1257
+ const eventKey = snapshotEventKey(sequencedTask);
1258
+ const workspaceId = item.workspaceId ?? this.scope.workspaceId;
1259
+ await this.workItemStore.createIfAbsentByEventKey({
1260
+ tenantId: this.scope.tenantId,
1261
+ taskId: task.id,
1262
+ projectId: this.scope.projectId,
1263
+ action: "activity",
1264
+ actor: `a2a:${this.scope.assistantId}`,
1265
+ summary: "A2A task snapshot",
1266
+ detail: { type: "a2a_task_snapshot", snapshotSequence: sequence, eventKey, task: sequencedTask },
1267
+ eventKey,
1268
+ ...asString2(task.metadata?.threadId) ? { threadId: asString2(task.metadata?.threadId) } : {},
1269
+ ...workspaceId ? { workspaceId } : {}
1270
+ });
1271
+ }
1272
+ async recordObservation(task, item, result, failureReason) {
1273
+ const workspaceId = item.workspaceId ?? this.scope.workspaceId;
1274
+ await this.workItemStore.createIfAbsentByEventKey({
1275
+ tenantId: this.scope.tenantId,
1276
+ taskId: task.id,
1277
+ projectId: this.scope.projectId,
1278
+ action: "activity",
1279
+ actor: `a2a:${this.scope.assistantId}`,
1280
+ summary: `A2A observed ${task.status.state}`,
1281
+ detail: {
1282
+ type: "a2a_status_observation",
1283
+ state: task.status.state,
1284
+ ...result === void 0 ? {} : { result },
1285
+ ...failureReason === void 0 ? {} : { failureReason },
1286
+ ...task.status.message ? { message: extractText(task.status.message.parts) } : {}
1287
+ },
1288
+ eventKey: observationEventKey(task, result, failureReason),
1289
+ ...asString2(task.metadata?.threadId) ? { threadId: asString2(task.metadata?.threadId) } : {},
1290
+ ...workspaceId ? { workspaceId } : {}
1291
+ });
1292
+ }
1293
+ async applyObservation(task, item, result, failureReason) {
1294
+ const actor = `a2a:${this.scope.assistantId}`;
1295
+ item = await this.refresh(item);
1296
+ const threadId = asString2(task.metadata?.threadId);
1297
+ if (task.status.state === "canceled") {
1298
+ await this.requireLifecycleSuccess("cancel", task.id, await this.runBridge.settleRun({
1299
+ tenantId: this.scope.tenantId,
1300
+ task: item,
1301
+ actor,
1302
+ ...threadId ? { threadId } : {},
1303
+ outcome: { kind: "cancelled", summary: "A2A observed canceled" }
1304
+ }));
1305
+ return;
1306
+ }
1307
+ if (isFailureState(task.status.state)) {
1308
+ await this.requireLifecycleSuccess("fail", task.id, await this.runBridge.settleRun({
1309
+ tenantId: this.scope.tenantId,
1310
+ task: item,
1311
+ actor,
1312
+ ...threadId ? { threadId } : {},
1313
+ outcome: {
1314
+ kind: "failed",
1315
+ lifecycleHandled: false,
1316
+ error: failureReason?.trim() || `A2A observed ${task.status.state}`
1317
+ }
1318
+ }));
1319
+ return;
1320
+ }
1321
+ if (task.status.state === "input-required") {
1322
+ const observed = asRecord2(task.metadata?.interruption);
1323
+ const observedType = asString2(observed?.type);
1324
+ const interruptionType = observedType === "review_required" || observedType === "user_decision" ? "user_decision" : "missing_input";
1325
+ const summary = asString2(observed?.summary) || (task.status.message ? extractText(task.status.message.parts).trim() : "") || "A2A requires input";
1326
+ await this.requireLifecycleSuccess("interrupt", task.id, await this.lifecycle.interruptTask({
1327
+ tenantId: this.scope.tenantId,
1328
+ taskId: task.id,
1329
+ type: interruptionType,
1330
+ summary,
1331
+ actor,
1332
+ ...threadId ? { threadId } : {}
1333
+ }));
1334
+ if (observedType === "review_required" && observed) {
1335
+ await this.preserveObservedInterruption(task.id, observed);
1336
+ }
1337
+ return;
1338
+ }
1339
+ await this.requireLifecycleSuccess("complete", task.id, await this.lifecycle.completeAgentTask({
1340
+ tenantId: this.scope.tenantId,
1341
+ taskId: task.id,
1342
+ result: result?.trim() || "(no output)",
1343
+ beliefImpact: [],
1344
+ actor,
1345
+ ...threadId ? { threadId } : {}
1346
+ }));
1347
+ }
1348
+ /**
1349
+ * Preserve a review interruption's canonical type/metadata after the generic
1350
+ * lifecycle interrupt (which only models missing_input/user_decision).
1351
+ */
1352
+ async preserveObservedInterruption(taskId, observed) {
1353
+ const item = await this.taskStore.getById(this.scope.tenantId, taskId);
1354
+ if (!item) return;
1355
+ const context = item.context ?? {};
1356
+ const current = asRecord2(context.interruption) ?? {};
1357
+ await this.taskStore.update(this.scope.tenantId, taskId, {
1358
+ context: {
1359
+ ...context,
1360
+ interruption: { ...current, ...observed, type: "review_required" }
1361
+ }
1362
+ });
1363
+ }
1364
+ async requireLifecycleSuccess(operation, taskId, result) {
1365
+ if (result.success) {
1366
+ if (result.warnings?.length) {
1367
+ console.warn({ event: "a2a:lifecycle:warnings", operation, taskId, warnings: result.warnings });
1368
+ }
1369
+ return result;
1370
+ }
1371
+ throw new Error(`A2A lifecycle ${operation} failed: ${result.code}: ${result.error}`);
1372
+ }
1373
+ isExactInterruptionReplay(task, item) {
1374
+ const observed = task.metadata?.interruption;
1375
+ const canonical = item.context?.interruption;
1376
+ if (item.status !== "interrupted" || !canonical || typeof canonical !== "object") return false;
1377
+ if (observed && typeof observed === "object") return isDeepStrictEqual(observed, canonical);
1378
+ const observedSummary = task.status.message ? extractText(task.status.message.parts).trim() : "";
1379
+ return observedSummary.length > 0 && canonical.summary === observedSummary;
1380
+ }
1381
+ /**
1382
+ * Restore an SDK task from the stored snapshot, overlaying the current
1383
+ * `TaskItem.status` onto `task.status.state`. Returns `undefined` when the
1384
+ * task is unknown to the tenant or has no A2A snapshot.
1385
+ */
1386
+ async load(taskId) {
1387
+ const item = await this.taskStore.getById(this.scope.tenantId, taskId);
1388
+ if (!item || !this.isInScope(item)) return void 0;
1389
+ const pageSize = 100;
1390
+ let selected;
1391
+ for (let offset = 0; ; offset += pageSize) {
1392
+ const activities = await this.workItemStore.list({
1393
+ tenantId: this.scope.tenantId,
1394
+ taskId,
1395
+ action: "activity",
1396
+ order: "desc",
1397
+ limit: pageSize,
1398
+ offset
1399
+ });
1400
+ for (const activity of activities) {
1401
+ if (activity.detail?.type !== "a2a_task_snapshot" || !isA2ATaskSnapshot(activity.detail.task)) continue;
1402
+ const sequence = snapshotSequence(activity.detail.snapshotSequence) ?? snapshotSequence(activity.detail.task.metadata?.snapshotSequence);
1403
+ const candidate = {
1404
+ task: activity.detail.task,
1405
+ sequence,
1406
+ eventKey: asString2(activity.detail.eventKey) ?? activity.eventKey ?? snapshotEventKey(activity.detail.task),
1407
+ statusTimestamp: Date.parse(activity.detail.task.status.timestamp ?? ""),
1408
+ createdAt: activity.createdAt,
1409
+ id: activity.id
1410
+ };
1411
+ if (!selected || this.isLaterSnapshot(candidate, selected)) selected = candidate;
1412
+ }
1413
+ if (activities.length < pageSize) break;
1414
+ }
1415
+ const snapshot = selected?.task ?? item.context?.a2aTask;
1416
+ if (!isA2ATaskSnapshot(snapshot)) return void 0;
1417
+ const restoredSequence = selected?.sequence ?? snapshotSequence(snapshot.metadata?.snapshotSequence);
1418
+ if (restoredSequence !== void 0) {
1419
+ this.snapshotSequences.set(taskId, Math.max(this.snapshotSequences.get(taskId) ?? 0, restoredSequence));
1420
+ }
1421
+ const state = mapTaskStatusToA2AState(item.status);
1422
+ const interruption = item.context?.interruption;
1423
+ const contextRunId = asString2(item.context?.a2aRunId);
1424
+ const contextThreadId = asString2(item.context?.threadId);
1425
+ const metadata = {
1426
+ ...snapshot.metadata ?? {},
1427
+ ...contextRunId === void 0 ? {} : { a2aRunId: contextRunId },
1428
+ ...contextThreadId === void 0 ? {} : { threadId: contextThreadId },
1429
+ ...interruption && typeof interruption === "object" ? { interruption } : {}
1430
+ };
1431
+ const nonResultArtifacts = (snapshot.artifacts ?? []).filter((artifact) => artifact.name !== "result");
1432
+ const artifacts = item.status === "completed" && item.result?.trim() ? [...nonResultArtifacts, {
1433
+ artifactId: `${item.id}:result`,
1434
+ name: "result",
1435
+ parts: [{ kind: "text", text: item.result }]
1436
+ }] : nonResultArtifacts;
1437
+ const message = state === "failed" && item.failureReason?.trim() ? {
1438
+ kind: "message",
1439
+ messageId: `${item.id}:failure`,
1440
+ role: "agent",
1441
+ parts: [{ kind: "text", text: item.failureReason }]
1442
+ } : interruption && typeof interruption === "object" && typeof interruption.summary === "string" ? {
1443
+ kind: "message",
1444
+ messageId: `${item.id}:interruption`,
1445
+ role: "agent",
1446
+ parts: [{ kind: "text", text: interruption.summary }]
1447
+ } : snapshot.status.message;
1448
+ return {
1449
+ ...snapshot,
1450
+ metadata,
1451
+ ...artifacts.length > 0 ? { artifacts } : { artifacts: void 0 },
1452
+ status: {
1453
+ ...snapshot.status,
1454
+ state,
1455
+ ...message ? { message } : {}
1456
+ }
1457
+ };
1458
+ }
1459
+ isInScope(item) {
1460
+ return item.tenantId === this.scope.tenantId && item.projectId === this.scope.projectId && item.ownerType === "agent" && item.ownerId === this.scope.assistantId && item.sourceId === "a2a";
1461
+ }
1462
+ requireScope(item, taskId) {
1463
+ if (!this.isInScope(item)) throw new A2ATaskScopeConflictError(taskId);
1464
+ }
1465
+ isLaterSnapshot(candidate, selected) {
1466
+ if (candidate.sequence !== void 0 || selected.sequence !== void 0) {
1467
+ if (candidate.sequence === void 0) return false;
1468
+ if (selected.sequence === void 0) return true;
1469
+ if (candidate.sequence !== selected.sequence) return candidate.sequence > selected.sequence;
1470
+ const candidateTimestamp = Number.isNaN(candidate.statusTimestamp) ? Number.NEGATIVE_INFINITY : candidate.statusTimestamp;
1471
+ const selectedTimestamp = Number.isNaN(selected.statusTimestamp) ? Number.NEGATIVE_INFINITY : selected.statusTimestamp;
1472
+ if (candidateTimestamp !== selectedTimestamp) return candidateTimestamp > selectedTimestamp;
1473
+ return candidate.eventKey > selected.eventKey;
1474
+ }
1475
+ return candidate.createdAt.getTime() > selected.createdAt.getTime() || candidate.createdAt.getTime() === selected.createdAt.getTime() && candidate.id > selected.id;
1476
+ }
1477
+ };
1478
+ function isA2AConcurrentWrite(error) {
1479
+ const message = error instanceof Error ? error.message : String(error);
1480
+ return message.includes("TASK_STATUS_CONFLICT") || message.includes("TASK_STORE_WRITE_FAILED") || message.includes("Task changed concurrently");
1481
+ }
1482
+
1483
+ // src/services/a2a/AxiomAgentExecutor.ts
1484
+ import { v4 as uuidv4 } from "uuid";
1485
+ import { agentInstanceManager, isOfficialInterruptId } from "@axiom-lattice/core";
1486
+ import { MessageChunkTypes as MessageChunkTypes2 } from "@axiom-lattice/protocols";
1487
+ import { A2AError as A2AError2 } from "@a2a-js/sdk/server";
1488
+
1489
+ // src/services/a2a/a2aFileRefs.ts
1490
+ function filePartsToRefs(parts, addedBy) {
1491
+ const refs = [];
1492
+ for (const part of parts) {
1493
+ if (part.kind !== "file") continue;
1494
+ const file = part.file;
1495
+ if ("bytes" in file) {
1496
+ throw new Error(
1497
+ "A2A file parts with inline bytes are not supported by this pure function \u2014 bytes ingest requires filePartsToRefsWithIngest with an A2AFileIngestor; provide a file uri otherwise"
1498
+ );
1499
+ }
1500
+ refs.push({
1501
+ uri: file.uri,
1502
+ ...file.name ? { name: file.name } : {},
1503
+ ...file.mimeType ? { mimeType: file.mimeType } : {},
1504
+ addedBy
1505
+ });
1506
+ }
1507
+ return refs;
1508
+ }
1509
+ async function filePartsToRefsWithIngest(parts, addedBy, ingestor, dedupeKeyBase, projectId) {
1510
+ if (!ingestor) {
1511
+ return filePartsToRefs(parts, addedBy);
1512
+ }
1513
+ const refs = [];
1514
+ for (let index = 0; index < parts.length; index += 1) {
1515
+ const part = parts[index];
1516
+ if (part.kind !== "file") continue;
1517
+ const dedupeKey = `${dedupeKeyBase}:${index}`;
1518
+ refs.push(await ingestor.ingest(part, dedupeKey, projectId, addedBy));
1519
+ }
1520
+ return refs;
1521
+ }
1522
+
1523
+ // src/services/a2a/A2ARunService.ts
1524
+ import { randomUUID } from "crypto";
1525
+ import {
1526
+ extractAgentRunInputMessageIds
1527
+ } from "@axiom-lattice/core";
1528
+ import { MessageChunkTypes } from "@axiom-lattice/protocols";
1529
+ var TERMINAL_STREAM_CHUNK_TYPES = /* @__PURE__ */ new Set([
1530
+ MessageChunkTypes.MESSAGE_COMPLETED,
1531
+ MessageChunkTypes.MESSAGE_FAILED,
1532
+ MessageChunkTypes.INTERRUPT
1533
+ ]);
1534
+ async function* stopAtTerminalChunks(source) {
1535
+ for await (const chunk of source) {
1536
+ yield chunk;
1537
+ if (TERMINAL_STREAM_CHUNK_TYPES.has(chunk.type)) return;
1538
+ }
1539
+ }
1540
+ var A2ARunNotFoundError = class extends Error {
1541
+ constructor(runId) {
1542
+ super(`A2A run not found: ${runId}`);
1543
+ this.runId = runId;
1544
+ /** Stable error code for API/protocol mapping. */
1545
+ this.code = "A2A_RUN_NOT_FOUND";
1546
+ this.name = "A2ARunNotFoundError";
1547
+ }
1548
+ };
1549
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
1550
+ "success",
1551
+ "error",
1552
+ "interrupted",
1553
+ "cancelled",
1554
+ "timeout"
1555
+ ]);
1556
+ function isTerminal(status) {
1557
+ return TERMINAL_STATUSES.has(status);
1558
+ }
1559
+ function mapStatus(status) {
1560
+ switch (status) {
1561
+ case "pending":
1562
+ case "running":
1563
+ case "success":
1564
+ case "error":
1565
+ case "interrupted":
1566
+ case "timeout":
1567
+ return status;
1568
+ case "cancelled":
1569
+ return "interrupted";
1570
+ }
1571
+ }
1572
+ function messageIdFromRun(run) {
1573
+ if (typeof run.input?.id === "string" && run.input.id.length > 0) {
1574
+ return run.input.id;
1575
+ }
1576
+ if (run.inputMessageIds.length > 0) return run.inputMessageIds[0];
1577
+ return run.runId;
1578
+ }
1579
+ var A2ARunService = class {
1580
+ constructor(deps) {
1581
+ this.ledger = deps.ledger;
1582
+ this.chunkSource = deps.chunkSource;
1583
+ this.wake = deps.wake ?? wakeRunTaskQueue;
1584
+ this.getLatestCursor = deps.getLatestCursor;
1585
+ this.getRunCancelRouter = deps.getRunCancelRouter ?? (() => getRunCancelRouter());
1586
+ this.idFactory = deps.idFactory ?? randomUUID;
1587
+ this.now = deps.now ?? Date.now;
1588
+ this.sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1589
+ this.pollIntervalMs = deps.pollIntervalMs ?? 200;
1590
+ this.joinTimeoutMs = deps.joinTimeoutMs ?? 3e4;
1591
+ this.cancelTimeoutMs = deps.cancelTimeoutMs ?? 1e4;
1592
+ this.maxActiveRunsForTenant = deps.maxActiveRunsForTenant;
1593
+ }
1594
+ /**
1595
+ * Creates exactly one pending `run_api` row for an A2A request.
1596
+ *
1597
+ * @param input Tenant/project/agent scope, A2A ids, and message or command.
1598
+ * @returns The run id, chunk-stream anchor/cursor, and pending status.
1599
+ * @remarks The stream cursor is captured before the row is committed so
1600
+ * already-emitted chunks cannot be lost. The queue wake is best-effort and
1601
+ * never awaited: a hung wake must not delay the protocol response.
1602
+ */
1603
+ async create(input) {
1604
+ const streamCursor = await this.captureLatestCursor(
1605
+ input.threadId,
1606
+ input.assistantId
1607
+ );
1608
+ const runId = this.idFactory();
1609
+ const hasCommand = input.command !== void 0;
1610
+ const messageId = hasCommand ? runId : input.message.id?.trim() || this.idFactory();
1611
+ const runInput = hasCommand ? null : { id: messageId, message: input.message.content };
1612
+ await this.ledger.createPendingRun({
1613
+ runId,
1614
+ tenantId: input.tenantId,
1615
+ ...input.workspaceId === void 0 ? {} : { workspaceId: input.workspaceId },
1616
+ projectId: input.projectId,
1617
+ assistantId: input.assistantId,
1618
+ threadId: input.threadId,
1619
+ input: runInput,
1620
+ command: input.command ?? null,
1621
+ config: input.config ?? null,
1622
+ metadata: {
1623
+ ...input.metadata ?? {},
1624
+ a2aTaskId: input.a2aTaskId,
1625
+ a2aContextId: input.threadId
1626
+ },
1627
+ inputMessageIds: runInput ? extractAgentRunInputMessageIds(runInput) : [],
1628
+ ...this.maxActiveRunsForTenant === void 0 ? {} : { maxActiveRunsForTenant: this.maxActiveRunsForTenant }
1629
+ });
1630
+ this.wakeSafely();
1631
+ return {
1632
+ runId,
1633
+ messageId,
1634
+ ...streamCursor === void 0 ? {} : { streamCursor },
1635
+ status: "pending",
1636
+ terminal: false
1637
+ };
1638
+ }
1639
+ /**
1640
+ * Waits for a `run_api` run to reach a terminal lifecycle state.
1641
+ *
1642
+ * @param timeoutMs Blocking budget (default {@link joinTimeoutMs}).
1643
+ * @returns The terminal handle, or a non-terminal `timeout` handle when the
1644
+ * budget ends.
1645
+ * @throws {A2ARunNotFoundError} When the run is absent, legacy-sourced, or out
1646
+ * of scope.
1647
+ */
1648
+ async join(tenantId, threadId, assistantId, runId, timeoutMs) {
1649
+ const budget = timeoutMs ?? this.joinTimeoutMs;
1650
+ const deadline = this.now() + Math.max(0, budget);
1651
+ for (; ; ) {
1652
+ const run = await this.readScoped(tenantId, threadId, assistantId, runId);
1653
+ if (!run) throw new A2ARunNotFoundError(runId);
1654
+ if (isTerminal(run.status)) return this.toHandle(run);
1655
+ const remaining = deadline - this.now();
1656
+ if (remaining <= 0) {
1657
+ return { ...this.toHandle(run), status: "timeout", terminal: false };
1658
+ }
1659
+ await this.sleep(Math.max(1, Math.min(this.pollIntervalMs, remaining)));
1660
+ }
1661
+ }
1662
+ /**
1663
+ * Requests cancellation of one exact `run_api` run and waits (bounded) for
1664
+ * terminal state.
1665
+ *
1666
+ * @throws {A2ARunNotFoundError} When the run is absent, legacy-sourced, or out
1667
+ * of scope.
1668
+ */
1669
+ async cancel(tenantId, threadId, assistantId, runId) {
1670
+ const run = await this.readScoped(tenantId, threadId, assistantId, runId);
1671
+ if (!run) throw new A2ARunNotFoundError(runId);
1672
+ if (isTerminal(run.status)) return;
1673
+ const router = this.getRunCancelRouter();
1674
+ let result;
1675
+ if (router) {
1676
+ result = await router.requestCancelById({
1677
+ tenantId,
1678
+ assistantId,
1679
+ threadId,
1680
+ runId,
1681
+ reason: "a2a.cancel"
1682
+ });
1683
+ } else {
1684
+ result = await this.ledger.requestCancel(tenantId, runId);
1685
+ }
1686
+ if (result === "not_found") throw new A2ARunNotFoundError(runId);
1687
+ if (result === "terminal") return;
1688
+ await this.waitForTerminal(tenantId, threadId, assistantId, runId);
1689
+ }
1690
+ /**
1691
+ * Attaches to the chunk stream for a run's message id.
1692
+ *
1693
+ * @param cursor Thread-stream position from the run handle. When supplied and
1694
+ * supported, the cursor-backed thread stream is used instead of message-id
1695
+ * anchoring (required for command runs).
1696
+ * @param scope Optional exact `run_api` scope guard, validated on first read.
1697
+ */
1698
+ stream(threadId, assistantId, messageId, cursor, scope) {
1699
+ const useCursorStream = typeof cursor === "string" && cursor.trim() !== "" && this.chunkSource.streamThread !== void 0;
1700
+ const createSource = () => useCursorStream ? this.chunkSource.streamThread(threadId, assistantId, cursor) : this.chunkSource.stream(threadId, assistantId, messageId);
1701
+ if (!scope) return createSource();
1702
+ return this.guardStream(
1703
+ createSource,
1704
+ threadId,
1705
+ assistantId,
1706
+ scope
1707
+ );
1708
+ }
1709
+ /**
1710
+ * Reads the authoritative `run_api` ledger record.
1711
+ *
1712
+ * @param threadId Optional scope guard; legacy runs are always rejected.
1713
+ * @param assistantId Optional scope guard.
1714
+ */
1715
+ async getRun(tenantId, runId, threadId, assistantId) {
1716
+ const run = await this.ledger.getRun(tenantId, runId);
1717
+ if (!run || run.source !== "run_api") return void 0;
1718
+ if (run.tenantId !== tenantId) return void 0;
1719
+ if (threadId !== void 0 && run.threadId !== threadId) return void 0;
1720
+ if (assistantId !== void 0 && run.assistantId !== assistantId) {
1721
+ return void 0;
1722
+ }
1723
+ return run;
1724
+ }
1725
+ async waitForTerminal(tenantId, threadId, assistantId, runId) {
1726
+ const deadline = this.now() + this.cancelTimeoutMs;
1727
+ for (; ; ) {
1728
+ const run = await this.readScoped(tenantId, threadId, assistantId, runId);
1729
+ if (!run || isTerminal(run.status)) return;
1730
+ const remaining = deadline - this.now();
1731
+ if (remaining <= 0) return;
1732
+ await this.sleep(Math.max(1, Math.min(this.pollIntervalMs, remaining)));
1733
+ }
1734
+ }
1735
+ async readScoped(tenantId, threadId, assistantId, runId) {
1736
+ const run = await this.ledger.getRun(tenantId, runId);
1737
+ if (!run || run.source !== "run_api" || run.tenantId !== tenantId || run.threadId !== threadId || run.assistantId !== assistantId) {
1738
+ return void 0;
1739
+ }
1740
+ return run;
1741
+ }
1742
+ toHandle(run) {
1743
+ return {
1744
+ runId: run.runId,
1745
+ messageId: messageIdFromRun(run),
1746
+ status: mapStatus(run.status),
1747
+ terminal: isTerminal(run.status)
1748
+ };
1749
+ }
1750
+ async captureLatestCursor(threadId, assistantId) {
1751
+ if (!this.getLatestCursor) return void 0;
1752
+ try {
1753
+ const cursor = await this.getLatestCursor(threadId, assistantId);
1754
+ if (typeof cursor !== "string" || cursor.trim() === "") return void 0;
1755
+ return cursor;
1756
+ } catch {
1757
+ return void 0;
1758
+ }
1759
+ }
1760
+ /**
1761
+ * Lazily verifies the exact `run_api` scope before opening the chunk source.
1762
+ * Validation is deferred so `stream()` remains synchronous and an unused
1763
+ * stream never touches the ledger.
1764
+ */
1765
+ guardStream(createSource, threadId, assistantId, scope) {
1766
+ return {
1767
+ [Symbol.asyncIterator]: () => {
1768
+ let validation;
1769
+ let inner;
1770
+ const validate = () => {
1771
+ validation ?? (validation = (async () => {
1772
+ const run = await this.readScoped(
1773
+ scope.tenantId,
1774
+ threadId,
1775
+ assistantId,
1776
+ scope.runId
1777
+ );
1778
+ if (!run) throw new A2ARunNotFoundError(scope.runId);
1779
+ })());
1780
+ return validation;
1781
+ };
1782
+ const source = () => {
1783
+ inner ?? (inner = createSource()[Symbol.asyncIterator]());
1784
+ return inner;
1785
+ };
1786
+ const settleValidation = async () => {
1787
+ if (!validation) return;
1788
+ try {
1789
+ await validation;
1790
+ } catch {
1791
+ }
1792
+ };
1793
+ return {
1794
+ next: async () => {
1795
+ await validate();
1796
+ return source().next();
1797
+ },
1798
+ return: async (value) => {
1799
+ await settleValidation();
1800
+ if (!inner) return { done: true, value };
1801
+ return inner.return ? inner.return(value) : { done: true, value };
1802
+ },
1803
+ throw: async (error) => {
1804
+ await settleValidation();
1805
+ if (!inner) throw error;
1806
+ return inner.throw ? inner.throw(error) : Promise.reject(error);
1807
+ }
1808
+ };
1809
+ }
1810
+ };
1811
+ }
1812
+ wakeSafely() {
1813
+ try {
1814
+ void this.wake().catch(() => void 0);
1815
+ } catch {
1816
+ }
1817
+ }
1818
+ };
1819
+
1820
+ // src/services/a2a/AxiomAgentExecutor.ts
1821
+ var A2ABlockingDeadlineError = class extends Error {
1822
+ constructor() {
1823
+ super("A2A blocking execution deadline expired");
1824
+ this.name = "A2ABlockingDeadlineError";
1825
+ }
1826
+ };
1827
+ var TERMINAL_A2A_STATES = /* @__PURE__ */ new Set([
1828
+ "completed",
1829
+ "failed",
1830
+ "canceled",
1831
+ "rejected"
1832
+ ]);
1833
+ function isoNow() {
1834
+ return (/* @__PURE__ */ new Date()).toISOString();
1835
+ }
1836
+ function asString3(value) {
1837
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1838
+ }
1839
+ function asRecord3(value) {
1840
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1841
+ }
1842
+ function extractText2(parts) {
1843
+ return parts.filter((part) => part.kind === "text").map((part) => part.text).join("\n");
1844
+ }
1845
+ function dataParts(parts) {
1846
+ return parts.filter(
1847
+ (part) => part.kind === "data"
1848
+ );
1849
+ }
1850
+ function soleOfficialInterruptId(task) {
1851
+ const ids = (task?.artifacts ?? []).filter((artifact) => artifact.name === "interrupt").flatMap((artifact) => artifact.parts).filter((part) => part.kind === "data").map((part) => asString3(part.data.id)).filter((id) => id !== void 0 && isOfficialInterruptId(id));
1852
+ return ids.length === 1 ? ids[0] : void 0;
1853
+ }
1854
+ function agentMessage(text) {
1855
+ return {
1856
+ kind: "message",
1857
+ messageId: uuidv4(),
1858
+ role: "agent",
1859
+ parts: [{ kind: "text", text }]
1860
+ };
1861
+ }
1862
+ function assistantHasGovernedTask(assistant) {
1863
+ const graph = assistant.graphDefinition;
1864
+ const middleware = graph && typeof graph === "object" ? graph.middleware : void 0;
1865
+ const taskMiddleware = Array.isArray(middleware) ? middleware.find((entry) => !!entry && typeof entry === "object" && entry.type === "task" && entry.enabled === true) : void 0;
1866
+ const allowedTools = taskMiddleware?.allowedTools;
1867
+ return !!taskMiddleware && (allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task")));
1868
+ }
1869
+ var AxiomAgentExecutor = class {
1870
+ constructor(options) {
1871
+ this.scope = options.scope;
1872
+ this.assistant = options.assistant;
1873
+ this.taskStore = options.taskStore;
1874
+ this.runService = options.runService;
1875
+ this.ingestor = options.ingestor;
1876
+ this.blocking = options.blocking ?? true;
1877
+ this.getInterrupts = options.getInterrupts ?? ((threadId) => agentInstanceManager.getAgent(this.agentParams(threadId)).getInterrupts());
1878
+ this.joinTimeoutMs = options.joinTimeoutMs ?? 3e4;
1879
+ this.overallTimeoutMs = this.blocking ? options.overallTimeoutMs ?? 10 * 6e4 : Number.POSITIVE_INFINITY;
1880
+ }
1881
+ /** Execute one A2A request through the durable run service. */
1882
+ async execute(ctx, bus) {
1883
+ const existing = ctx.task;
1884
+ const threadId = asString3(existing?.metadata?.threadId) ?? ctx.contextId;
1885
+ const dependencies = ctx.referenceTasks?.map((task) => task.id) ?? [];
1886
+ const resume = this.isResume(existing);
1887
+ const text = extractText2(ctx.userMessage.parts);
1888
+ const governed = this.hasGovernedTaskCapability();
1889
+ const resumeCanonicalTask = resume && !governed;
1890
+ if (existing && !resume) {
1891
+ throw A2AError2.invalidParams(
1892
+ `Task ${existing.id} is not waiting for input and cannot be resumed`
1893
+ );
1894
+ }
1895
+ if (!this.taskStore) {
1896
+ const initialTask2 = this.taskFor(ctx, threadId, [], dependencies, {
1897
+ handle: void 0,
1898
+ resume: false
1899
+ });
1900
+ bus.publish(initialTask2);
1901
+ await this.failTask(ctx, bus, initialTask2, new Error("A2A execution requires a canonical task store"));
1902
+ return;
1903
+ }
1904
+ if (existing && TERMINAL_A2A_STATES.has(existing.status.state)) {
1905
+ throw A2AError2.invalidParams(
1906
+ `Task ${existing.id} is terminal (${existing.status.state}) and cannot be modified`
1907
+ );
1908
+ }
1909
+ const interruption = asRecord3(existing?.metadata?.interruption);
1910
+ const reviewInterruption = interruption?.type === "review_required";
1911
+ const command = resume ? this.resumeCommand(
1912
+ ctx.userMessage.parts,
1913
+ text,
1914
+ reviewInterruption,
1915
+ soleOfficialInterruptId(existing)
1916
+ ) : void 0;
1917
+ if (reviewInterruption && !command) {
1918
+ bus.publish(agentMessage("Review response must be exactly 'approve', 'reject', or 'reject: <note>'."));
1919
+ return;
1920
+ }
1921
+ if (resume && !command) {
1922
+ throw A2AError2.invalidParams(`Task ${ctx.taskId} resume requires text or one DataPart`);
1923
+ }
1924
+ let inputFiles;
1925
+ try {
1926
+ inputFiles = await filePartsToRefsWithIngest(
1927
+ ctx.userMessage.parts,
1928
+ "user",
1929
+ this.ingestor,
1930
+ ctx.taskId,
1931
+ this.scope.projectId
1932
+ );
1933
+ } catch (err) {
1934
+ const initialTask2 = this.taskFor(ctx, threadId, [], dependencies, {
1935
+ handle: void 0,
1936
+ resume: false
1937
+ });
1938
+ await this.taskStore.save(initialTask2);
1939
+ bus.publish(initialTask2);
1940
+ await this.failTask(ctx, bus, initialTask2, err);
1941
+ return;
1942
+ }
1943
+ const requestText = this.requestText(ctx.taskId, text, inputFiles, governed, resume);
1944
+ const runInput = {
1945
+ tenantId: this.scope.tenantId,
1946
+ projectId: this.scope.projectId,
1947
+ ...this.scope.workspaceId ? { workspaceId: this.scope.workspaceId } : {},
1948
+ assistantId: this.scope.assistantId,
1949
+ threadId,
1950
+ a2aTaskId: ctx.taskId,
1951
+ message: { id: ctx.userMessage.messageId, role: "user", content: requestText },
1952
+ ...command ? { command } : {},
1953
+ config: {
1954
+ taskId: ctx.taskId,
1955
+ execution: { taskId: ctx.taskId }
1956
+ }
1957
+ };
1958
+ let handle;
1959
+ try {
1960
+ handle = await this.runService.create(runInput);
1961
+ } catch (err) {
1962
+ const initialTask2 = this.taskFor(ctx, threadId, inputFiles, dependencies, {
1963
+ handle: void 0,
1964
+ resume: false
1965
+ });
1966
+ await this.taskStore.save(initialTask2);
1967
+ bus.publish(initialTask2);
1968
+ await this.failTask(ctx, bus, initialTask2, err);
1969
+ return;
1970
+ }
1971
+ const projection = {
1972
+ ctx,
1973
+ ...existing ? { existing } : {},
1974
+ threadId,
1975
+ inputFiles,
1976
+ dependencies,
1977
+ handle,
1978
+ governed,
1979
+ resume: resumeCanonicalTask
1980
+ };
1981
+ const initialTask = this.taskFor(ctx, threadId, inputFiles, dependencies, {
1982
+ handle,
1983
+ resume: resumeCanonicalTask
1984
+ });
1985
+ await this.taskStore.save(initialTask);
1986
+ bus.publish(initialTask);
1987
+ if (!this.blocking) {
1988
+ void this.projectRun(bus, projection).catch((err) => {
1989
+ console.warn({
1990
+ event: "a2a:run:background_projection_failed",
1991
+ taskId: ctx.taskId,
1992
+ runId: handle.runId,
1993
+ error: err instanceof Error ? err.message : String(err)
1994
+ });
1995
+ });
1996
+ return;
1997
+ }
1998
+ await this.projectRun(bus, projection);
1999
+ }
2000
+ async projectRun(bus, projection) {
2001
+ const { ctx, threadId, handle } = projection;
2002
+ let outputText = "";
2003
+ let streamError;
2004
+ let dropChunks = false;
2005
+ const iterator = this.runService.stream(
2006
+ threadId,
2007
+ this.scope.assistantId,
2008
+ handle.messageId,
2009
+ handle.streamCursor,
2010
+ { tenantId: this.scope.tenantId, runId: handle.runId }
2011
+ )[Symbol.asyncIterator]();
2012
+ const consume = (async () => {
2013
+ try {
2014
+ for (; ; ) {
2015
+ const next = await iterator.next();
2016
+ if (next.done) return;
2017
+ if (dropChunks) return;
2018
+ const chunk = next.value;
2019
+ const chunkText = chunk.data?.content ?? "";
2020
+ if (chunk.type === MessageChunkTypes2.AI && chunkText) {
2021
+ outputText += chunkText;
2022
+ }
2023
+ if ((chunk.type === MessageChunkTypes2.AI || chunk.type === MessageChunkTypes2.TOOL) && chunkText) {
2024
+ bus.publish({
2025
+ kind: "status-update",
2026
+ taskId: ctx.taskId,
2027
+ contextId: ctx.contextId,
2028
+ status: {
2029
+ state: "working",
2030
+ message: agentMessage(chunkText),
2031
+ timestamp: isoNow()
2032
+ },
2033
+ final: false
2034
+ });
2035
+ }
2036
+ }
2037
+ } catch (err) {
2038
+ streamError = err;
2039
+ }
2040
+ })();
2041
+ let terminal;
2042
+ try {
2043
+ terminal = await this.waitForTerminal(handle.runId, threadId);
2044
+ } catch (err) {
2045
+ await iterator.return?.();
2046
+ await consume.catch(() => void 0);
2047
+ const baseTask = this.taskFor(ctx, threadId, projection.inputFiles, projection.dependencies, projection);
2048
+ if (err instanceof A2ABlockingDeadlineError) {
2049
+ try {
2050
+ await this.runService.cancel(
2051
+ this.scope.tenantId,
2052
+ threadId,
2053
+ this.scope.assistantId,
2054
+ handle.runId
2055
+ );
2056
+ } catch (cancelError) {
2057
+ console.warn({
2058
+ event: "a2a:timeout:cancel_failed",
2059
+ taskId: ctx.taskId,
2060
+ runId: handle.runId,
2061
+ error: cancelError instanceof Error ? cancelError.message : String(cancelError)
2062
+ });
2063
+ }
2064
+ await this.failTask(ctx, bus, baseTask, new Error("A2A run timed out"));
2065
+ return;
2066
+ }
2067
+ await this.failTask(ctx, bus, baseTask, err);
2068
+ return;
2069
+ }
2070
+ const run = await this.runService.getRun(
2071
+ this.scope.tenantId,
2072
+ handle.runId,
2073
+ threadId,
2074
+ this.scope.assistantId
2075
+ ).catch(() => void 0);
2076
+ const runRecord = asRecord3(run);
2077
+ const abortStream = runRecord?.cancelRequested === true || terminal.status === "timeout";
2078
+ if (abortStream) {
2079
+ dropChunks = true;
2080
+ await iterator.return?.();
2081
+ }
2082
+ await consume.catch(() => void 0);
2083
+ if (runRecord?.cancelRequested === true) {
2084
+ await this.finishCanceled(bus, projection);
2085
+ return;
2086
+ }
2087
+ switch (terminal.status) {
2088
+ case "success":
2089
+ await this.finishCompleted(bus, projection, outputText);
2090
+ return;
2091
+ case "interrupted":
2092
+ await this.finishInputRequired(bus, projection);
2093
+ return;
2094
+ case "error": {
2095
+ const canonical = this.taskStore ? await this.taskStore.load(ctx.taskId) : void 0;
2096
+ const canonicalMessage = canonical?.status.state === "failed" && canonical.status.message ? extractText2(canonical.status.message.parts).trim() : "";
2097
+ const message = canonicalMessage || asString3(runRecord?.error) || (streamError instanceof Error ? streamError.message : void 0) || "A2A run failed";
2098
+ await this.failTask(
2099
+ ctx,
2100
+ bus,
2101
+ this.taskFor(ctx, threadId, projection.inputFiles, projection.dependencies, projection),
2102
+ new Error(message)
2103
+ );
2104
+ return;
2105
+ }
2106
+ case "timeout":
2107
+ await this.failTask(
2108
+ ctx,
2109
+ bus,
2110
+ this.taskFor(ctx, threadId, projection.inputFiles, projection.dependencies, projection),
2111
+ new Error("A2A run timed out")
2112
+ );
2113
+ return;
2114
+ default:
2115
+ await this.failTask(
2116
+ ctx,
2117
+ bus,
2118
+ this.taskFor(ctx, threadId, projection.inputFiles, projection.dependencies, projection),
2119
+ new Error(`A2A run ended in unexpected state ${terminal.status}`)
2120
+ );
2121
+ }
2122
+ }
2123
+ async waitForTerminal(runId, threadId) {
2124
+ const deadline = Date.now() + Math.max(0, this.overallTimeoutMs);
2125
+ for (; ; ) {
2126
+ const remaining = deadline - Date.now();
2127
+ if (remaining <= 0) {
2128
+ throw new A2ABlockingDeadlineError();
2129
+ }
2130
+ const handle = await this.runService.join(
2131
+ this.scope.tenantId,
2132
+ threadId,
2133
+ this.scope.assistantId,
2134
+ runId,
2135
+ Math.min(this.joinTimeoutMs, remaining)
2136
+ );
2137
+ if (handle.terminal) return handle;
2138
+ }
2139
+ }
2140
+ async finishCompleted(bus, projection, outputText) {
2141
+ const { ctx, threadId, existing } = projection;
2142
+ const canonical = this.taskStore ? await this.taskStore.load(ctx.taskId) : void 0;
2143
+ const canonicalState = canonical?.status.state;
2144
+ if (canonicalState === "failed" || canonicalState === "rejected" || canonicalState === "canceled") {
2145
+ bus.publish({
2146
+ kind: "status-update",
2147
+ taskId: ctx.taskId,
2148
+ contextId: ctx.contextId,
2149
+ status: {
2150
+ state: canonicalState === "canceled" ? "canceled" : "failed",
2151
+ ...canonical?.status.message ? { message: canonical.status.message } : {},
2152
+ timestamp: isoNow()
2153
+ },
2154
+ final: true
2155
+ });
2156
+ return;
2157
+ }
2158
+ const canonicalResult = canonical?.artifacts?.find((artifact) => artifact.name === "result");
2159
+ const canonicalText = canonicalResult ? extractText2(canonicalResult.parts).trim() : "";
2160
+ const resultText = canonicalText || outputText;
2161
+ const resultArtifact = {
2162
+ artifactId: uuidv4(),
2163
+ name: "result",
2164
+ parts: [{ kind: "text", text: resultText }]
2165
+ };
2166
+ const completed = {
2167
+ ...this.taskFor(ctx, threadId, projection.inputFiles, projection.dependencies, projection),
2168
+ artifacts: [...existing?.artifacts ?? [], resultArtifact],
2169
+ status: { state: "completed", timestamp: isoNow() }
2170
+ };
2171
+ if (this.taskStore) await this.taskStore.save(completed);
2172
+ bus.publish({
2173
+ kind: "artifact-update",
2174
+ taskId: ctx.taskId,
2175
+ contextId: ctx.contextId,
2176
+ artifact: resultArtifact
2177
+ });
2178
+ bus.publish({
2179
+ kind: "status-update",
2180
+ taskId: ctx.taskId,
2181
+ contextId: ctx.contextId,
2182
+ status: { state: "completed", timestamp: isoNow() },
2183
+ final: true
2184
+ });
2185
+ }
2186
+ async finishInputRequired(bus, projection) {
2187
+ const { ctx, threadId } = projection;
2188
+ let interrupts = [];
2189
+ try {
2190
+ interrupts = await this.getInterrupts(threadId) ?? [];
2191
+ } catch (err) {
2192
+ console.warn({
2193
+ event: "a2a:interrupts:read_failed",
2194
+ taskId: ctx.taskId,
2195
+ threadId,
2196
+ error: err instanceof Error ? err.message : String(err)
2197
+ });
2198
+ }
2199
+ const canonical = this.taskStore ? await this.taskStore.load(ctx.taskId) : void 0;
2200
+ const summary = canonical?.status.message ? extractText2(canonical.status.message.parts).trim() : "";
2201
+ const interruptArtifact = interrupts.length === 0 ? void 0 : {
2202
+ artifactId: `${ctx.taskId}:interrupt`,
2203
+ name: "interrupt",
2204
+ parts: interrupts.map((interrupt) => {
2205
+ const payload = asRecord3(interrupt.payload);
2206
+ const schema = asRecord3(payload?.response_schema) ?? payload?.response_schema;
2207
+ return {
2208
+ kind: "data",
2209
+ data: {
2210
+ id: interrupt.interruptId,
2211
+ value: interrupt.payload,
2212
+ ...schema === void 0 ? {} : { response_schema: schema }
2213
+ }
2214
+ };
2215
+ })
2216
+ };
2217
+ const baseTask = this.taskFor(
2218
+ ctx,
2219
+ threadId,
2220
+ projection.inputFiles,
2221
+ projection.dependencies,
2222
+ projection
2223
+ );
2224
+ const pending = {
2225
+ ...baseTask,
2226
+ ...interruptArtifact ? {
2227
+ artifacts: [
2228
+ ...(baseTask.artifacts ?? []).filter(
2229
+ (artifact) => artifact.artifactId !== interruptArtifact.artifactId
2230
+ ),
2231
+ interruptArtifact
2232
+ ]
2233
+ } : {},
2234
+ status: {
2235
+ state: "input-required",
2236
+ ...summary ? { message: agentMessage(summary) } : {},
2237
+ timestamp: isoNow()
2238
+ }
2239
+ };
2240
+ if (this.taskStore) await this.taskStore.save(pending);
2241
+ if (interruptArtifact) {
2242
+ bus.publish({
2243
+ kind: "artifact-update",
2244
+ taskId: ctx.taskId,
2245
+ contextId: ctx.contextId,
2246
+ artifact: interruptArtifact
2247
+ });
2248
+ }
2249
+ bus.publish({
2250
+ kind: "status-update",
2251
+ taskId: ctx.taskId,
2252
+ contextId: ctx.contextId,
2253
+ status: {
2254
+ state: "input-required",
2255
+ ...summary ? { message: agentMessage(summary) } : {},
2256
+ timestamp: isoNow()
2257
+ },
2258
+ final: true
2259
+ });
2260
+ }
2261
+ async finishCanceled(bus, projection) {
2262
+ await this.persistCanceled(projection.ctx.taskId, projection.existing, projection);
2263
+ bus.publish({
2264
+ kind: "status-update",
2265
+ taskId: projection.ctx.taskId,
2266
+ contextId: projection.ctx.contextId,
2267
+ status: { state: "canceled", timestamp: isoNow() },
2268
+ final: true
2269
+ });
2270
+ }
2271
+ async failTask(ctx, bus, baseTask, err) {
2272
+ const failedTask = {
2273
+ ...baseTask,
2274
+ status: {
2275
+ state: "failed",
2276
+ message: agentMessage(err instanceof Error ? err.message : String(err)),
2277
+ timestamp: isoNow()
2278
+ }
2279
+ };
2280
+ if (this.taskStore) {
2281
+ try {
2282
+ await this.taskStore.save(failedTask);
2283
+ } catch (saveError) {
2284
+ console.warn({
2285
+ event: "a2a:settle:save_failed",
2286
+ taskId: ctx.taskId,
2287
+ error: saveError instanceof Error ? saveError.message : String(saveError)
2288
+ });
2289
+ }
2290
+ }
2291
+ bus.publish({
2292
+ kind: "status-update",
2293
+ taskId: ctx.taskId,
2294
+ contextId: ctx.contextId,
2295
+ status: failedTask.status,
2296
+ final: true
2297
+ });
2298
+ }
2299
+ taskFor(ctx, threadId, inputFiles, dependencies, projection) {
2300
+ const existing = ctx.task;
2301
+ const metadata = {
2302
+ ...existing?.metadata ?? {},
2303
+ threadId,
2304
+ dependencies,
2305
+ inputFiles,
2306
+ ...projection?.handle ? { a2aRunId: projection.handle.runId } : {},
2307
+ ...projection?.handle?.messageId !== void 0 ? { a2aMessageId: projection.handle.messageId } : {},
2308
+ ...projection?.resume ? { a2aResume: true } : {}
2309
+ };
2310
+ if (projection?.handle) {
2311
+ if (projection.handle.streamCursor !== void 0) {
2312
+ metadata.a2aStreamCursor = projection.handle.streamCursor;
2313
+ } else {
2314
+ delete metadata.a2aStreamCursor;
2315
+ }
2316
+ }
2317
+ return {
2318
+ kind: "task",
2319
+ id: ctx.taskId,
2320
+ contextId: ctx.contextId,
2321
+ ...existing?.history ? { history: existing.history } : {},
2322
+ ...existing?.artifacts ? { artifacts: existing.artifacts } : {},
2323
+ status: { state: "working", timestamp: isoNow() },
2324
+ metadata
2325
+ };
2326
+ }
2327
+ requestText(taskId, text, inputFiles, governed, resume) {
2328
+ const requestText = inputFiles.length > 0 ? `${text}
2329
+
2330
+ ${formatFileRefsSection(inputFiles)}` : text;
2331
+ if (!governed || resume) return requestText;
2332
+ return [
2333
+ `A2A governed task instruction: get and update the existing TaskItem ${taskId}; do not create a replacement task.`,
2334
+ "Before doing the requested work, initialize its canonical description through manage_task with ## Objective, ## Acceptance Criteria, and ## Belief State sections.",
2335
+ "Keep lifecycle status truthful. Change status only through manage_task set_status; complete with a nonblank result and structured beliefImpact, and use interrupted, failed, or cancelled when appropriate. Use manage_task update for content fields only.",
2336
+ "",
2337
+ requestText
2338
+ ].join("\n");
2339
+ }
2340
+ isResume(task) {
2341
+ return task?.status.state === "input-required";
2342
+ }
2343
+ resumeCommand(parts, text, reviewInterruption, soleInterruptId) {
2344
+ if (reviewInterruption) return this.reviewCommand(text);
2345
+ const data = dataParts(parts);
2346
+ if (data.length > 0) {
2347
+ const officialIds = data.map((part) => {
2348
+ const id = asString3(part.data.id);
2349
+ return id !== void 0 && isOfficialInterruptId(id) ? id : void 0;
2350
+ });
2351
+ if (data.length > 1 && officialIds.every((id) => id !== void 0)) {
2352
+ const resume = {};
2353
+ for (let index = 0; index < data.length; index += 1) {
2354
+ resume[officialIds[index]] = data[index].data.value;
2355
+ }
2356
+ return { resume };
2357
+ }
2358
+ const sole = data[0];
2359
+ if (sole && "value" in sole.data) {
2360
+ if (data.length === 1 && officialIds[0] !== void 0) {
2361
+ return { resume: { [officialIds[0]]: sole.data.value } };
2362
+ }
2363
+ return { resume: sole.data.value };
2364
+ }
2365
+ }
2366
+ if (text.trim().length > 0) {
2367
+ return {
2368
+ resume: soleInterruptId === void 0 ? text : { [soleInterruptId]: text }
2369
+ };
2370
+ }
2371
+ return void 0;
2372
+ }
2373
+ reviewCommand(text) {
2374
+ const trimmed = text.trim();
2375
+ const normalized = trimmed.toLowerCase();
2376
+ if (normalized === "approve") return { resume: { action: "approve" } };
2377
+ if (normalized === "reject") {
2378
+ return { resume: { action: "reject", data: { note: "Changes requested." } } };
2379
+ }
2380
+ const prefixedNote = /^reject:\s*(.+)$/is.exec(trimmed)?.[1]?.trim();
2381
+ if (prefixedNote) {
2382
+ return { resume: { action: "reject", data: { note: prefixedNote } } };
2383
+ }
2384
+ return void 0;
2385
+ }
2386
+ agentParams(threadId) {
2387
+ return {
2388
+ assistant_id: this.scope.assistantId,
2389
+ thread_id: threadId,
2390
+ tenant_id: this.scope.tenantId,
2391
+ project_id: this.scope.projectId,
2392
+ ...this.scope.workspaceId ? { workspace_id: this.scope.workspaceId } : {}
2393
+ };
2394
+ }
2395
+ hasGovernedTaskCapability() {
2396
+ return assistantHasGovernedTask(this.assistant);
2397
+ }
2398
+ /**
2399
+ * Cancel the exact durable run persisted on the A2A task, then persist the
2400
+ * terminal canceled state. No agent instance is aborted directly.
2401
+ */
2402
+ async cancelTask(taskId, bus) {
2403
+ const task = this.taskStore ? await this.taskStore.load(taskId) : void 0;
2404
+ const threadId = asString3(task?.metadata?.threadId);
2405
+ const runId = asString3(task?.metadata?.a2aRunId) ?? asString3(task?.metadata?.runId);
2406
+ if (threadId && runId) {
2407
+ try {
2408
+ await this.runService.cancel(
2409
+ this.scope.tenantId,
2410
+ threadId,
2411
+ this.scope.assistantId,
2412
+ runId
2413
+ );
2414
+ } catch (err) {
2415
+ if (!(err instanceof A2ARunNotFoundError)) {
2416
+ console.warn({
2417
+ event: "a2a:cancel:failed",
2418
+ taskId,
2419
+ threadId,
2420
+ runId,
2421
+ error: err instanceof Error ? err.message : String(err)
2422
+ });
2423
+ }
2424
+ }
2425
+ }
2426
+ await this.persistCanceled(taskId, task);
2427
+ bus.publish({
2428
+ kind: "status-update",
2429
+ taskId,
2430
+ contextId: task?.contextId ?? "",
2431
+ status: { state: "canceled", timestamp: isoNow() },
2432
+ final: true
2433
+ });
2434
+ }
2435
+ async persistCanceled(taskId, task, projection) {
2436
+ const canceledTask = {
2437
+ ...task ?? {
2438
+ kind: "task",
2439
+ id: taskId,
2440
+ contextId: "",
2441
+ status: { state: "working" }
2442
+ },
2443
+ ...projection ? {
2444
+ metadata: {
2445
+ ...task?.metadata ?? {},
2446
+ threadId: projection.threadId,
2447
+ dependencies: projection.dependencies,
2448
+ inputFiles: projection.inputFiles,
2449
+ ...projection.handle ? { a2aRunId: projection.handle.runId } : {}
2450
+ }
2451
+ } : {},
2452
+ status: { state: "canceled", timestamp: isoNow() }
2453
+ };
2454
+ if (this.taskStore) {
2455
+ try {
2456
+ await this.taskStore.save(canceledTask);
2457
+ } catch (saveError) {
2458
+ console.warn({
2459
+ event: "a2a:settle:save_failed",
2460
+ taskId,
2461
+ error: saveError instanceof Error ? saveError.message : String(saveError)
2462
+ });
2463
+ }
2464
+ }
2465
+ return canceledTask;
2466
+ }
2467
+ };
2468
+
2469
+ // src/services/a2a/A2AFileIngestor.ts
2470
+ var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
2471
+ var EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
2472
+ ".exe",
2473
+ ".sh",
2474
+ ".bat",
2475
+ ".cmd",
2476
+ ".com",
2477
+ ".msi",
2478
+ ".js",
2479
+ ".mjs",
2480
+ ".cjs",
2481
+ ".ps1",
2482
+ ".vbs",
2483
+ ".dll",
2484
+ ".so",
2485
+ ".dylib"
2486
+ ]);
2487
+ var EXECUTABLE_MIME_TYPES = /* @__PURE__ */ new Set([
2488
+ "application/x-msdownload",
2489
+ "application/x-msdos-program",
2490
+ "application/x-executable",
2491
+ "application/x-sh",
2492
+ "application/x-bat",
2493
+ "application/x-shellscript",
2494
+ "text/x-shellscript",
2495
+ "application/javascript",
2496
+ "application/x-javascript",
2497
+ "text/javascript"
2498
+ ]);
2499
+ function decodeBase64(value) {
2500
+ return new Uint8Array(Buffer.from(value, "base64"));
2501
+ }
2502
+ function maxEncodedLengthFor(maxBytes) {
2503
+ return Math.ceil(maxBytes * 4 / 3) + 4;
2504
+ }
2505
+ function sanitizePathSegment(segment, fallback) {
2506
+ const cleaned = segment.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/\.{2,}/g, ".").replace(/^[.-]+|[.-]+$/g, "");
2507
+ return cleaned.length > 0 ? cleaned : fallback;
2508
+ }
2509
+ function storageLocationFor(dedupeKey, name) {
2510
+ const separatorIndex = dedupeKey.lastIndexOf(":");
2511
+ const keyBase = separatorIndex >= 0 ? dedupeKey.slice(0, separatorIndex) : dedupeKey;
2512
+ const keyIndex = separatorIndex >= 0 ? dedupeKey.slice(separatorIndex + 1) : "0";
2513
+ return {
2514
+ path: `a2a/${sanitizePathSegment(keyBase, "task")}`,
2515
+ name: `${sanitizePathSegment(keyIndex, "0")}-${sanitizePathSegment(name, "file")}`
2516
+ };
2517
+ }
2518
+ function extensionOf(name) {
2519
+ return name.includes(".") ? name.slice(name.lastIndexOf(".")).toLowerCase() : "";
2520
+ }
2521
+ function assertAllowedFileType(name, mimeType) {
2522
+ if (EXECUTABLE_EXTENSIONS.has(extensionOf(name))) {
2523
+ throw new Error(`Refusing to ingest executable file "${name}"`);
2524
+ }
2525
+ if (mimeType && EXECUTABLE_MIME_TYPES.has(mimeType.toLowerCase())) {
2526
+ throw new Error(`Refusing to ingest executable file "${name}"`);
2527
+ }
2528
+ }
2529
+ var A2AFileIngestor = class {
2530
+ constructor(options) {
2531
+ this.cache = /* @__PURE__ */ new Map();
2532
+ this.saveProjectFile = options.saveProjectFile;
2533
+ this.maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
2534
+ }
2535
+ /**
2536
+ * Project a single file part onto a {@link TaskFileRef}, ingesting bytes when
2537
+ * present. Idempotent per `dedupeKey`: a repeated key returns the cached URI
2538
+ * without re-writing storage, and even across ingestor instances the key
2539
+ * maps to a deterministic storage path (`a2a/<taskId>/<partIndex>-<name>`),
2540
+ * so request retries overwrite the same object.
2541
+ *
2542
+ * NOTE: the key is based on `taskId` + part index rather than the internal
2543
+ * message id because the executor must ingest files *before* `addMessage`
2544
+ * returns a messageId. A client retrying with a different taskId for the
2545
+ * same message writes a second copy; that is accepted and documented.
2546
+ */
2547
+ async ingest(part, dedupeKey, projectId, addedBy) {
2548
+ const file = part.file;
2549
+ if ("uri" in file) {
2550
+ return {
2551
+ uri: file.uri,
2552
+ ...file.name ? { name: file.name } : {},
2553
+ ...file.mimeType ? { mimeType: file.mimeType } : {},
2554
+ addedBy
2555
+ };
2556
+ }
2557
+ const name = file.name ?? "file";
2558
+ assertAllowedFileType(name, file.mimeType);
2559
+ if (file.bytes.length > maxEncodedLengthFor(this.maxBytes)) {
2560
+ throw new Error(
2561
+ `A2A file part exceeds the ${this.maxBytes} byte ingest limit`
2562
+ );
2563
+ }
2564
+ const bytes = decodeBase64(file.bytes);
2565
+ if (bytes.length > this.maxBytes) {
2566
+ throw new Error(
2567
+ `A2A file part exceeds the ${this.maxBytes} byte ingest limit`
2568
+ );
2569
+ }
2570
+ const cached = this.cache.get(dedupeKey);
2571
+ if (cached !== void 0) {
2572
+ return {
2573
+ uri: cached,
2574
+ ...file.name ? { name: file.name } : {},
2575
+ ...file.mimeType ? { mimeType: file.mimeType } : {},
2576
+ addedBy
2577
+ };
2578
+ }
2579
+ const location = storageLocationFor(dedupeKey, name);
2580
+ const { uri } = await this.saveProjectFile({
2581
+ projectId,
2582
+ name: location.name,
2583
+ path: location.path,
2584
+ bytes,
2585
+ ...file.mimeType ? { mimeType: file.mimeType } : {}
2586
+ });
2587
+ this.cache.set(dedupeKey, uri);
2588
+ return {
2589
+ uri,
2590
+ ...file.name ? { name: file.name } : {},
2591
+ ...file.mimeType ? { mimeType: file.mimeType } : {},
2592
+ addedBy
2593
+ };
2594
+ }
2595
+ };
2596
+
2597
+ // src/services/a2a/A2ARuntimeRegistry.ts
2598
+ import {
2599
+ DefaultExecutionEventBus
2600
+ } from "@a2a-js/sdk/server";
2601
+ var DEFAULT_MAX_SCOPES = 1e3;
2602
+ var DEFAULT_IDLE_TTL_MS = 15 * 60 * 1e3;
2603
+ var A2ARuntimeCapacityError = class extends Error {
2604
+ constructor(maxScopes) {
2605
+ super(`A2A runtime scope capacity of ${maxScopes} is exhausted`);
2606
+ this.name = "A2ARuntimeCapacityError";
2607
+ }
2608
+ };
2609
+ var ScopedExecutionEventBusManager = class {
2610
+ constructor(touch) {
2611
+ this.buses = /* @__PURE__ */ new Map();
2612
+ this.touch = touch;
2613
+ }
2614
+ createOrGetByTaskId(taskId) {
2615
+ this.touch();
2616
+ const existing = this.buses.get(taskId);
2617
+ if (existing) return existing;
2618
+ const bus = new DefaultExecutionEventBus();
2619
+ this.buses.set(taskId, bus);
2620
+ return bus;
2621
+ }
2622
+ getByTaskId(taskId) {
2623
+ this.touch();
2624
+ return this.buses.get(taskId);
2625
+ }
2626
+ cleanupByTaskId(taskId) {
2627
+ this.touch();
2628
+ const bus = this.buses.get(taskId);
2629
+ bus?.removeAllListeners();
2630
+ this.buses.delete(taskId);
2631
+ }
2632
+ get activeBusCount() {
2633
+ return this.buses.size;
2634
+ }
2635
+ close() {
2636
+ for (const bus of this.buses.values()) {
2637
+ bus.removeAllListeners();
2638
+ }
2639
+ this.buses.clear();
2640
+ }
2641
+ };
2642
+ var A2ARuntimeRegistry = class {
2643
+ constructor(options = {}) {
2644
+ this.entries = /* @__PURE__ */ new Map();
2645
+ this.closed = false;
2646
+ this.maxScopes = options.maxScopes ?? DEFAULT_MAX_SCOPES;
2647
+ this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
2648
+ this.now = options.now ?? Date.now;
2649
+ }
2650
+ /**
2651
+ * Acquire and pin the runtime manager for an authorization scope.
2652
+ *
2653
+ * @param scope Exact tenant, project, and assistant authorization scope.
2654
+ * @returns A lease whose manager is shared only within that scope.
2655
+ */
2656
+ acquire(scope) {
2657
+ if (this.closed) throw new Error("A2A runtime registry is closed");
2658
+ const now = this.now();
2659
+ this.pruneExpired(now);
2660
+ const key = JSON.stringify([
2661
+ scope.tenantId,
2662
+ scope.projectId,
2663
+ scope.assistantId
2664
+ ]);
2665
+ const existing = this.entries.get(key);
2666
+ const entry = existing ?? this.createEntry(key, now);
2667
+ entry.lastAccessedAt = now;
2668
+ entry.leaseCount += 1;
2669
+ let released = false;
2670
+ return {
2671
+ manager: entry.manager,
2672
+ release: () => {
2673
+ if (released) return;
2674
+ released = true;
2675
+ entry.leaseCount -= 1;
2676
+ entry.lastAccessedAt = this.now();
2677
+ }
2678
+ };
2679
+ }
2680
+ close() {
2681
+ if (this.closed) return;
2682
+ this.closed = true;
2683
+ for (const entry of this.entries.values()) {
2684
+ entry.manager.close();
2685
+ }
2686
+ this.entries.clear();
2687
+ }
2688
+ pruneExpired(now) {
2689
+ for (const [key, entry] of this.entries) {
2690
+ if (!this.isActive(entry) && now - entry.lastAccessedAt > this.idleTtlMs) {
2691
+ entry.manager.close();
2692
+ this.entries.delete(key);
2693
+ }
2694
+ }
2695
+ }
2696
+ makeCapacity() {
2697
+ if (this.entries.size < this.maxScopes) return;
2698
+ let oldest;
2699
+ for (const candidate of this.entries) {
2700
+ if (this.isActive(candidate[1])) continue;
2701
+ if (!oldest || candidate[1].lastAccessedAt < oldest[1].lastAccessedAt) {
2702
+ oldest = candidate;
2703
+ }
2704
+ }
2705
+ if (!oldest) throw new A2ARuntimeCapacityError(this.maxScopes);
2706
+ oldest[1].manager.close();
2707
+ this.entries.delete(oldest[0]);
2708
+ }
2709
+ createEntry(key, now) {
2710
+ this.makeCapacity();
2711
+ const entry = {
2712
+ lastAccessedAt: now,
2713
+ leaseCount: 0,
2714
+ manager: new ScopedExecutionEventBusManager(() => {
2715
+ entry.lastAccessedAt = this.now();
2716
+ })
2717
+ };
2718
+ this.entries.set(key, entry);
2719
+ return entry;
2720
+ }
2721
+ isActive(entry) {
2722
+ return entry.leaseCount > 0 || entry.manager.activeBusCount > 0;
2723
+ }
2724
+ };
2725
+
2726
+ // src/routes/a2a-standard.ts
2727
+ var TERMINAL_A2A_STATES2 = /* @__PURE__ */ new Set([
2728
+ "completed",
2729
+ "failed",
2730
+ "canceled",
2731
+ "rejected"
2732
+ ]);
2733
+ function agentParamsFor(scope, threadId, assistantId) {
2734
+ return {
2735
+ assistant_id: assistantId,
2736
+ thread_id: threadId,
2737
+ tenant_id: scope.tenantId,
2738
+ project_id: scope.projectId,
2739
+ ...scope.workspaceId ? { workspace_id: scope.workspaceId } : {}
2740
+ };
2741
+ }
2742
+ function chunksFromThreadStream(buffer, key, cursor) {
2743
+ return (async function* () {
2744
+ for await (const record of buffer.streamThread(
2745
+ key,
2746
+ cursor === void 0 ? {} : { cursor }
2747
+ )) {
2748
+ if (record.kind === "end") return;
2749
+ if (record.kind !== "chunk") continue;
2750
+ yield record.chunk;
2751
+ if (record.chunk.type === MessageChunkTypes3.MESSAGE_COMPLETED || record.chunk.type === MessageChunkTypes3.MESSAGE_FAILED || record.chunk.type === MessageChunkTypes3.INTERRUPT) {
2752
+ return;
2753
+ }
2754
+ }
2755
+ })();
2756
+ }
2757
+ function runServiceChunkSource(scope) {
2758
+ return {
2759
+ stream: (threadId, assistantId, messageId) => {
2760
+ const buffer = getChunkBuffer("default");
2761
+ if (buffer) {
2762
+ return stopAtTerminalChunks(
2763
+ chunksFromThreadStream(
2764
+ buffer,
2765
+ chunkBufferStreamKey(scope.tenantId, assistantId, threadId)
2766
+ )
2767
+ );
2768
+ }
2769
+ return stopAtTerminalChunks(
2770
+ agentInstanceManager2.getAgent(agentParamsFor(scope, threadId, assistantId)).chunkStream(
2771
+ messageId,
2772
+ [MessageChunkTypes3.MESSAGE_COMPLETED, MessageChunkTypes3.MESSAGE_FAILED]
2773
+ )
2774
+ );
2775
+ },
2776
+ streamThread: (threadId, assistantId, cursor) => {
2777
+ const buffer = getChunkBuffer("default");
2778
+ if (!buffer) return (async function* () {
2779
+ })();
2780
+ return chunksFromThreadStream(
2781
+ buffer,
2782
+ chunkBufferStreamKey(scope.tenantId, assistantId, threadId),
2783
+ cursor
2784
+ );
2785
+ }
2786
+ };
2787
+ }
2788
+ function messageText(content) {
2789
+ if (typeof content === "string") return content;
2790
+ if (!Array.isArray(content)) return "";
2791
+ return content.map((part) => {
2792
+ if (typeof part === "string") return part;
2793
+ if (typeof part !== "object" || part === null) return "";
2794
+ const record = part;
2795
+ if (typeof record.text === "string") return record.text;
2796
+ if (record.type === "text" && typeof record.value === "string") {
2797
+ return record.value;
2798
+ }
2799
+ return "";
2800
+ }).join("");
2801
+ }
2802
+ function normalizeCheckpointMessages(messages) {
2803
+ if (!Array.isArray(messages)) return [];
2804
+ const normalized = [];
2805
+ messages.forEach((message, index) => {
2806
+ if (typeof message !== "object" || message === null) return;
2807
+ const record = message;
2808
+ const type = typeof record.getType === "function" ? record.getType() : asString4(record.type) ?? asString4(record.role);
2809
+ const role = type === "human" || type === "user" ? "user" : type === "ai" || type === "assistant" ? "agent" : void 0;
2810
+ if (!role) return;
2811
+ const text = messageText(record.content);
2812
+ if (!text) return;
2813
+ normalized.push({
2814
+ kind: "message",
2815
+ messageId: asString4(record.id) ?? `a2a-checkpoint:${index}`,
2816
+ role,
2817
+ parts: [{ kind: "text", text }]
2818
+ });
2819
+ });
2820
+ return normalized;
2821
+ }
2822
+ function defaultStateResolver() {
2823
+ return {
2824
+ getCurrentMessages: async (threadId, assistantId, scope) => {
2825
+ const agent = agentInstanceManager2.getAgent(
2826
+ agentParamsFor(scope, threadId, assistantId)
2827
+ );
2828
+ const state = await agent.getCurrentState();
2829
+ return normalizeCheckpointMessages(state?.values?.messages);
2830
+ },
2831
+ getInterrupts: async (threadId, assistantId, scope) => await agentInstanceManager2.getAgent(agentParamsFor(scope, threadId, assistantId)).getInterrupts() ?? []
2832
+ };
2833
+ }
2834
+ function blockingFor(body) {
2835
+ const params = typeof body === "object" && body !== null ? body.params : void 0;
2836
+ const configuration = typeof params === "object" && params !== null ? params.configuration : void 0;
2837
+ const blocking = typeof configuration === "object" && configuration !== null ? configuration.blocking : void 0;
2838
+ return blocking !== false;
2839
+ }
2840
+ async function resumeValidationError(taskStore, body) {
2841
+ const method = typeof body === "object" && body !== null ? body.method : void 0;
2842
+ if (method !== "message/send" && method !== "message/stream") return void 0;
2843
+ const params = body.params;
2844
+ const message = typeof params === "object" && params !== null ? params.message : void 0;
2845
+ const taskId = typeof message === "object" && message !== null ? message.taskId : void 0;
2846
+ if (typeof taskId !== "string" || taskId.length === 0) return void 0;
2847
+ const task = await taskStore.load(taskId);
2848
+ if (!task) return void 0;
2849
+ if (task.status.state === "input-required") return void 0;
2850
+ if (TERMINAL_A2A_STATES2.has(task.status.state)) return void 0;
2851
+ return `Task ${taskId} is not waiting for input and cannot be resumed`;
2852
+ }
2853
+ var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set([
2854
+ "success",
2855
+ "error",
2856
+ "interrupted",
2857
+ "cancelled",
2858
+ "timeout"
2859
+ ]);
2860
+ var RESUBSCRIBE_SETTLE_TIMEOUT_MS = 1e4;
2861
+ function asString4(value) {
2862
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2863
+ }
2864
+ function requestMethod(body) {
2865
+ return typeof body === "object" && body !== null ? asString4(body.method) : void 0;
2866
+ }
2867
+ function requestTaskId(body) {
2868
+ if (typeof body !== "object" || body === null) return void 0;
2869
+ const record = body;
2870
+ const params = typeof record.params === "object" && record.params !== null ? record.params : void 0;
2871
+ if (!params) return void 0;
2872
+ if (record.method === "tasks/get" || record.method === "tasks/cancel" || record.method === "tasks/resubscribe") {
2873
+ return asString4(params.id);
2874
+ }
2875
+ if (record.method === "message/send" || record.method === "message/stream") {
2876
+ const message = typeof params.message === "object" && params.message !== null ? params.message : void 0;
2877
+ return asString4(message?.taskId);
2878
+ }
2879
+ return void 0;
2880
+ }
2881
+ function isTerminalA2AState(state) {
2882
+ return TERMINAL_A2A_STATES2.has(state);
2883
+ }
2884
+ function agentMessage2(text) {
2885
+ return {
2886
+ kind: "message",
2887
+ messageId: `a2a-run-projection:${Date.now()}`,
2888
+ role: "agent",
2889
+ parts: [{ kind: "text", text }]
2890
+ };
2891
+ }
2892
+ function projectRunState(run) {
2893
+ if (run.cancelRequested === true || run.status === "cancelled") {
2894
+ return { state: "canceled" };
2895
+ }
2896
+ switch (run.status) {
2897
+ case "success":
2898
+ return { state: "completed" };
2899
+ case "interrupted":
2900
+ return { state: "input-required" };
2901
+ case "error":
2902
+ case "timeout":
2903
+ return {
2904
+ state: "failed",
2905
+ ...asString4(run.error) ? { message: agentMessage2(asString4(run.error)) } : {}
2906
+ };
2907
+ default:
2908
+ return void 0;
2909
+ }
2910
+ }
2911
+ function resultTextFromTask(task) {
2912
+ const artifact = task.artifacts?.find((item) => item.name === "result");
2913
+ if (!artifact) return "";
2914
+ return artifact.parts.filter((part) => part.kind === "text").map((part) => part.text).join("");
2915
+ }
2916
+ function resultArtifactFor(task, result) {
2917
+ return {
2918
+ artifactId: `${task.id}:result`,
2919
+ name: "result",
2920
+ parts: [{ kind: "text", text: result.trim() || "(no output)" }]
2921
+ };
2922
+ }
2923
+ function withResult(task, result, runId, threadId) {
2924
+ const artifact = resultArtifactFor(task, result);
2925
+ return {
2926
+ ...task,
2927
+ metadata: { ...task.metadata ?? {}, a2aRunId: runId, threadId },
2928
+ artifacts: [
2929
+ ...(task.artifacts ?? []).filter(
2930
+ (item) => item.artifactId !== artifact.artifactId && item.name !== "result"
2931
+ ),
2932
+ artifact
2933
+ ],
2934
+ status: { state: "completed", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
2935
+ };
2936
+ }
2937
+ function deriveInterruption(task, interrupts) {
2938
+ const existing = task.metadata?.interruption;
2939
+ if (existing && typeof existing === "object") {
2940
+ return existing;
2941
+ }
2942
+ if (interrupts.length === 0) return void 0;
2943
+ const review = interrupts.some((interrupt) => interrupt.type === "task_review");
2944
+ const clarify = interrupts.some((interrupt) => interrupt.type === "clarify");
2945
+ const summary = task.status.message ? task.status.message.parts.filter((part) => part.kind === "text").map((part) => part.text).join("").trim() : "";
2946
+ return {
2947
+ type: review ? "review_required" : clarify ? "missing_input" : "user_decision",
2948
+ summary: summary || "A2A requires input"
2949
+ };
2950
+ }
2951
+ function interruptArtifactFor(task, interrupts) {
2952
+ if (interrupts.length === 0) return void 0;
2953
+ return {
2954
+ artifactId: `${task.id}:interrupt`,
2955
+ name: "interrupt",
2956
+ parts: interrupts.map((interrupt) => {
2957
+ const payload = interrupt.payload;
2958
+ const record = typeof payload === "object" && payload !== null && !Array.isArray(payload) ? payload : void 0;
2959
+ const schema = record?.response_schema;
2960
+ return {
2961
+ kind: "data",
2962
+ data: {
2963
+ id: interrupt.interruptId,
2964
+ value: payload,
2965
+ ...schema === void 0 ? {} : { response_schema: schema }
2966
+ }
2967
+ };
2968
+ })
2969
+ };
2970
+ }
2971
+ async function reconcileTaskFromRun(taskStore, runService, stateResolver, scope, body) {
2972
+ const taskId = requestTaskId(body);
2973
+ if (!taskId) return void 0;
2974
+ let task;
2975
+ try {
2976
+ task = await taskStore.load(taskId);
2977
+ } catch {
2978
+ return void 0;
2979
+ }
2980
+ if (!task) return void 0;
2981
+ if (isTerminalA2AState(task.status.state)) {
2982
+ if (task.status.state !== "completed") return task;
2983
+ }
2984
+ const runId = asString4(task.metadata?.a2aRunId);
2985
+ if (!runId) return task;
2986
+ const threadId = asString4(task.metadata?.threadId) ?? task.contextId;
2987
+ const run = await runService.getRun(
2988
+ scope.tenantId,
2989
+ runId,
2990
+ threadId,
2991
+ scope.assistantId
2992
+ ).catch(() => void 0);
2993
+ if (!run || !TERMINAL_RUN_STATUSES.has(run.status)) return task;
2994
+ const projection = projectRunState(run);
2995
+ if (!projection) return task;
2996
+ if (projection.state === "completed") {
2997
+ const existingResult = resultTextFromTask(task).trim();
2998
+ const missingResult = existingResult === "" || existingResult.toLowerCase() === "(no output)";
2999
+ let result = existingResult;
3000
+ if (missingResult) {
3001
+ try {
3002
+ const messages = await stateResolver.getCurrentMessages(
3003
+ threadId,
3004
+ scope.assistantId,
3005
+ scope
3006
+ );
3007
+ const finalAgent = [...messages].reverse().find((message) => message.role === "agent");
3008
+ const text = finalAgent ? finalAgent.parts.filter((part) => part.kind === "text").map((part) => part.text).join("").trim() : "";
3009
+ if (text) result = text;
3010
+ } catch {
3011
+ }
3012
+ }
3013
+ if (task.status.state === "completed" && !missingResult) return task;
3014
+ const completedTask = withResult(task, result || "(no output)", runId, threadId);
3015
+ if (taskStore.completeRun) {
3016
+ return taskStore.completeRun(completedTask, result || "(no output)");
3017
+ }
3018
+ await taskStore.save(completedTask);
3019
+ return await taskStore.load(taskId) ?? completedTask;
3020
+ }
3021
+ if (projection.state === "input-required") {
3022
+ let interrupts = [];
3023
+ try {
3024
+ const resolved = await stateResolver.getInterrupts(
3025
+ threadId,
3026
+ scope.assistantId,
3027
+ scope
3028
+ );
3029
+ interrupts = Array.isArray(resolved) ? resolved : [];
3030
+ } catch {
3031
+ }
3032
+ const interruption = deriveInterruption(task, interrupts);
3033
+ const artifact = interruptArtifactFor(task, interrupts);
3034
+ const next2 = {
3035
+ ...task,
3036
+ metadata: {
3037
+ ...task.metadata ?? {},
3038
+ a2aRunId: runId,
3039
+ threadId,
3040
+ ...interruption ? { interruption } : {}
3041
+ },
3042
+ ...artifact ? {
3043
+ artifacts: [
3044
+ ...(task.artifacts ?? []).filter(
3045
+ (item) => item.artifactId !== artifact.artifactId
3046
+ ),
3047
+ artifact
3048
+ ]
3049
+ } : {},
3050
+ status: {
3051
+ state: "input-required",
3052
+ ...task.status.message ? { message: task.status.message } : {},
3053
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3054
+ }
3055
+ };
3056
+ await taskStore.save(next2);
3057
+ return await taskStore.load(taskId) ?? next2;
3058
+ }
3059
+ if (task.status.state === projection.state) return task;
3060
+ const next = {
3061
+ ...task,
3062
+ metadata: { ...task.metadata ?? {}, a2aRunId: runId, threadId },
3063
+ status: {
3064
+ state: projection.state,
3065
+ ...projection.message ? { message: projection.message } : {},
3066
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3067
+ }
3068
+ };
3069
+ await taskStore.save(next);
3070
+ return await taskStore.load(taskId) ?? next;
3071
+ }
3072
+ async function cancelTaskFromRun(taskStore, runService, scope, body) {
3073
+ if (requestMethod(body) !== "tasks/cancel") return void 0;
3074
+ const taskId = requestTaskId(body);
3075
+ if (!taskId) return void 0;
3076
+ const task = await taskStore.load(taskId);
3077
+ if (!task || isTerminalA2AState(task.status.state)) return void 0;
3078
+ const runId = asString4(task.metadata?.a2aRunId);
3079
+ if (!runId) return void 0;
3080
+ const threadId = asString4(task.metadata?.threadId) ?? task.contextId;
3081
+ const before = await runService.getRun(
3082
+ scope.tenantId,
3083
+ runId,
3084
+ threadId,
3085
+ scope.assistantId
3086
+ );
3087
+ if (!before) throw A2AError3.taskNotCancelable(taskId);
3088
+ if (TERMINAL_RUN_STATUSES.has(before.status)) {
3089
+ if (before.status !== "interrupted" && before.cancelRequested !== true) {
3090
+ throw A2AError3.taskNotCancelable(taskId);
3091
+ }
3092
+ } else {
3093
+ await runService.cancel(scope.tenantId, threadId, scope.assistantId, runId);
3094
+ const after = await runService.getRun(
3095
+ scope.tenantId,
3096
+ runId,
3097
+ threadId,
3098
+ scope.assistantId
3099
+ );
3100
+ if (!after) throw A2AError3.taskNotCancelable(taskId);
3101
+ if (!TERMINAL_RUN_STATUSES.has(after.status)) {
3102
+ throw new Error(`Run ${runId} did not reach a terminal state after cancellation`);
3103
+ }
3104
+ if (after.status !== "interrupted" && after.status !== "cancelled" && after.cancelRequested !== true) {
3105
+ throw A2AError3.taskNotCancelable(taskId);
3106
+ }
3107
+ }
3108
+ const canceled = {
3109
+ ...task,
3110
+ metadata: { ...task.metadata ?? {}, a2aRunId: runId, threadId },
3111
+ status: { state: "canceled", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
3112
+ };
3113
+ await taskStore.save(canceled);
3114
+ return await taskStore.load(taskId) ?? canceled;
3115
+ }
3116
+ function getHeader(value) {
3117
+ if (Array.isArray(value)) return value[0];
3118
+ return value;
3119
+ }
3120
+ function getSessionUser(request) {
3121
+ return request.user;
3122
+ }
3123
+ function buildBaseUrl(request) {
3124
+ const protocol = getHeader(request.headers["x-forwarded-proto"]) ?? request.protocol;
3125
+ const forwardedHost = getHeader(request.headers["x-forwarded-host"]);
3126
+ const host = forwardedHost ? forwardedHost.split(",")[0].trim() : request.host;
3127
+ return `${protocol}://${host}`;
3128
+ }
3129
+ function makeFileIngestor(tenantId, maxFileBytes) {
3130
+ return new A2AFileIngestor({
3131
+ saveProjectFile: (params) => saveProjectFile({ tenantId, ...params }),
3132
+ ...maxFileBytes !== void 0 ? { maxBytes: maxFileBytes } : {}
3133
+ });
3134
+ }
3135
+ async function resolveWorkspaceId(getProjectStore, tenantId, projectId) {
3136
+ if (!projectId) return void 0;
3137
+ try {
3138
+ const project = await getProjectStore().getProjectById(tenantId, projectId);
3139
+ return project?.workspaceId;
3140
+ } catch {
3141
+ return void 0;
3142
+ }
3143
+ }
3144
+ function extractRequestId(body) {
3145
+ if (typeof body === "object" && body !== null && "id" in body) {
3146
+ const id = body.id;
3147
+ if (typeof id === "string" || typeof id === "number" || id === null) {
3148
+ return id;
3149
+ }
3150
+ }
3151
+ return null;
3152
+ }
3153
+ function authErrorToRpcCode(statusCode) {
3154
+ switch (statusCode) {
3155
+ case 401:
3156
+ return -32001;
3157
+ case 403:
3158
+ return -32002;
3159
+ case 404:
3160
+ return -32003;
3161
+ default:
3162
+ return -32603;
3163
+ }
3164
+ }
3165
+ function jsonRpcError(id, code, message) {
3166
+ return { jsonrpc: "2.0", id, error: { code, message } };
3167
+ }
3168
+ var DEFAULT_A2A_TASKS_PAGE_SIZE = 50;
3169
+ var MAX_A2A_TASKS_PAGE_SIZE = 200;
3170
+ var TASKS_LIST_STATUSES = {
3171
+ submitted: ["pending"],
3172
+ working: ["in_progress"],
3173
+ "input-required": ["interrupted", "review"],
3174
+ completed: ["completed"],
3175
+ failed: ["failed"],
3176
+ canceled: ["cancelled"],
3177
+ rejected: ["failed"],
3178
+ pending: ["pending"],
3179
+ in_progress: ["in_progress"],
3180
+ review: ["review"],
3181
+ interrupted: ["interrupted"],
3182
+ cancelled: ["cancelled"]
3183
+ };
3184
+ function asRecord4(value) {
3185
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
3186
+ }
3187
+ function encodePageToken(offset) {
3188
+ return Buffer.from(String(offset), "utf8").toString("base64url");
3189
+ }
3190
+ function decodePageToken(token) {
3191
+ if (!/^[A-Za-z0-9_-]+$/.test(token)) return void 0;
3192
+ const text = Buffer.from(token, "base64url").toString("utf8");
3193
+ if (!/^\d+$/.test(text)) return void 0;
3194
+ const offset = Number(text);
3195
+ return Number.isSafeInteger(offset) && offset >= 0 ? offset : void 0;
3196
+ }
3197
+ function parseTasksListQuery(body) {
3198
+ const record = asRecord4(body);
3199
+ const rawParams = record?.params;
3200
+ if (rawParams !== void 0 && rawParams !== null && asRecord4(rawParams) === void 0) {
3201
+ return { ok: false, message: "params must be an object" };
3202
+ }
3203
+ const params = asRecord4(rawParams) ?? {};
3204
+ let pageSize = DEFAULT_A2A_TASKS_PAGE_SIZE;
3205
+ if (params.pageSize !== void 0) {
3206
+ const value = params.pageSize;
3207
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_A2A_TASKS_PAGE_SIZE) {
3208
+ return {
3209
+ ok: false,
3210
+ message: `pageSize must be an integer between 1 and ${MAX_A2A_TASKS_PAGE_SIZE}`
3211
+ };
3212
+ }
3213
+ pageSize = value;
3214
+ }
3215
+ let offset = 0;
3216
+ if (params.pageToken !== void 0) {
3217
+ const token = params.pageToken;
3218
+ const decoded = typeof token === "string" ? decodePageToken(token) : void 0;
3219
+ if (decoded === void 0) {
3220
+ return { ok: false, message: "pageToken is invalid" };
3221
+ }
3222
+ offset = decoded;
3223
+ }
3224
+ let contextId;
3225
+ if (params.contextId !== void 0) {
3226
+ if (typeof params.contextId !== "string") {
3227
+ return { ok: false, message: "contextId must be a string" };
3228
+ }
3229
+ contextId = params.contextId;
3230
+ }
3231
+ let statusTimestampAfter;
3232
+ if (params.statusTimestampAfter !== void 0) {
3233
+ if (typeof params.statusTimestampAfter !== "string") {
3234
+ return { ok: false, message: "statusTimestampAfter must be a string" };
3235
+ }
3236
+ statusTimestampAfter = params.statusTimestampAfter;
3237
+ }
3238
+ let statuses;
3239
+ if (params.status !== void 0) {
3240
+ const value = params.status;
3241
+ if (typeof value !== "string" || !Object.prototype.hasOwnProperty.call(TASKS_LIST_STATUSES, value)) {
3242
+ return { ok: false, message: "status is invalid" };
3243
+ }
3244
+ statuses = TASKS_LIST_STATUSES[value];
3245
+ }
3246
+ return {
3247
+ ok: true,
3248
+ query: {
3249
+ pageSize,
3250
+ offset,
3251
+ ...statuses ? { statuses } : {},
3252
+ ...contextId ? { contextId } : {},
3253
+ ...statusTimestampAfter ? { statusTimestampAfter } : {}
3254
+ }
3255
+ };
3256
+ }
3257
+ function isA2ATaskSnapshot2(value, id) {
3258
+ return typeof value === "object" && value !== null && value.id === id && typeof value.status === "object" && value.status !== null;
3259
+ }
3260
+ function isCanonicalA2ATaskItem(item, scope) {
3261
+ return item.tenantId === scope.tenantId && item.ownerType === "agent" && item.ownerId === scope.assistantId && item.projectId === scope.projectId && item.sourceId === "a2a";
3262
+ }
3263
+ async function listCanonicalA2ATasks(internalTasks, taskStore, scope, query) {
3264
+ const statuses = query.statuses;
3265
+ const storeStatus = statuses?.length === 1 ? statuses[0] : void 0;
3266
+ const fetchSize = query.pageSize + 1;
3267
+ const tasks = [];
3268
+ const seen = /* @__PURE__ */ new Set();
3269
+ let cursor = query.offset;
3270
+ for (; ; ) {
3271
+ const rows = await internalTasks.list({
3272
+ tenantId: scope.tenantId,
3273
+ ownerType: "agent",
3274
+ ownerId: scope.assistantId,
3275
+ projectId: scope.projectId,
3276
+ sourceId: "a2a",
3277
+ ...storeStatus ? { status: storeStatus } : {},
3278
+ limit: fetchSize,
3279
+ offset: cursor
3280
+ });
3281
+ if (rows.length === 0) break;
3282
+ let nextPageToken;
3283
+ for (let index = 0; index < rows.length; index += 1) {
3284
+ const item = rows[index];
3285
+ if (!isCanonicalA2ATaskItem(item, scope)) continue;
3286
+ if (statuses && !statuses.includes(item.status)) continue;
3287
+ const restored = await taskStore.load(item.id).catch(() => void 0);
3288
+ if (!restored || !isA2ATaskSnapshot2(restored, item.id)) continue;
3289
+ if (query.contextId && restored.contextId !== query.contextId) continue;
3290
+ if (query.statusTimestampAfter && (!restored.status.timestamp || restored.status.timestamp <= query.statusTimestampAfter)) continue;
3291
+ if (seen.has(restored.id)) continue;
3292
+ if (tasks.length < query.pageSize) {
3293
+ seen.add(restored.id);
3294
+ tasks.push(restored);
3295
+ continue;
3296
+ }
3297
+ nextPageToken = encodePageToken(cursor + index);
3298
+ break;
3299
+ }
3300
+ if (nextPageToken !== void 0) {
3301
+ return { tasks, nextPageToken };
3302
+ }
3303
+ cursor += rows.length;
3304
+ if (rows.length < fetchSize) break;
3305
+ }
3306
+ return { tasks };
3307
+ }
3308
+ function isAsyncGenerator(value) {
3309
+ return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
3310
+ }
3311
+ var SSE_HEADERS = {
3312
+ "Content-Type": "text/event-stream",
3313
+ "Cache-Control": "no-cache",
3314
+ Connection: "keep-alive",
3315
+ "Access-Control-Allow-Origin": "*",
3316
+ "A2A-Version": "0.3"
3317
+ };
3318
+ function registerA2AStandardRoutes(app, deps) {
3319
+ const authService = deps.authService;
3320
+ const resolveTasks = deps.getTaskStoreLattice ?? (() => getStoreLattice("default", "task").store);
3321
+ const resolveTaskWorkItems = deps.getTaskWorkItemStore ?? (() => getStoreLattice("default", "taskWorkItem").store);
3322
+ const getAssistantStore = deps.getAssistantStore ?? (() => getStoreLattice("default", "assistant").store);
3323
+ const getProjectStore = deps.getProjectStore ?? (() => getStoreLattice("default", "project").store);
3324
+ const resolveRunLedger = deps.getAgentRunLedger ?? (() => getStoreLattice("default", "agentRun").store);
3325
+ const resolveStore = deps.getStore ?? (() => getStoreLattice("default", "store").store);
3326
+ const hasPushStore = () => {
3327
+ try {
3328
+ return Boolean(resolveStore());
3329
+ } catch {
3330
+ return false;
3331
+ }
3332
+ };
3333
+ const createPushStore = (scope) => {
3334
+ const store = resolveStore();
3335
+ if (!store) return void 0;
3336
+ return new A2APushNotificationStore({
3337
+ store,
3338
+ scope: {
3339
+ tenantId: scope.tenantId,
3340
+ assistantId: scope.assistantId
3341
+ },
3342
+ ...deps.resolvePushHost ? { resolveHost: deps.resolvePushHost } : {}
3343
+ });
3344
+ };
3345
+ const unavailableRunLedger = () => ({
3346
+ createPendingRun: async () => {
3347
+ throw new Error("A2A run ledger is not configured");
3348
+ },
3349
+ getRun: async () => void 0,
3350
+ requestCancel: async () => "not_found"
3351
+ });
3352
+ const runServiceFor = (scope) => {
3353
+ if (deps.createRunService) return deps.createRunService(scope);
3354
+ let ledger;
3355
+ try {
3356
+ ledger = resolveRunLedger();
3357
+ } catch {
3358
+ ledger = unavailableRunLedger();
3359
+ }
3360
+ const maxActiveRunsForTenant = resolveRunTaskMaxActivePerTenant();
3361
+ return new A2ARunService({
3362
+ ledger,
3363
+ chunkSource: runServiceChunkSource(scope),
3364
+ ...maxActiveRunsForTenant === void 0 ? {} : { maxActiveRunsForTenant },
3365
+ getLatestCursor: async (threadId, assistantId) => {
3366
+ const buffer = getChunkBuffer("default");
3367
+ if (!buffer) return void 0;
3368
+ const key = chunkBufferStreamKey(scope.tenantId, assistantId, threadId);
3369
+ const status = await buffer.getThreadStatus(key);
3370
+ if (status !== "active") return void 0;
3371
+ return buffer.latestCursor(key);
3372
+ }
3373
+ });
3374
+ };
3375
+ const stateResolver = deps.stateResolver ?? defaultStateResolver();
3376
+ const runtimeRegistry = deps.runtimeRegistry ?? new A2ARuntimeRegistry();
3377
+ const taskStoreFor = (scope, internalTasks, workItems) => new A2ATaskStoreAdapter({
3378
+ taskStore: internalTasks,
3379
+ workItemStore: workItems,
3380
+ lifecycle: createTaskLifecycleService({
3381
+ taskStore: internalTasks,
3382
+ workItemStore: workItems
3383
+ }),
3384
+ scope
3385
+ });
3386
+ app.addHook("onClose", async () => {
3387
+ runtimeRegistry.close();
3388
+ });
3389
+ const handleTaskResubscribe = async (request, reply, requestId, scope, taskStore, options = {}) => {
3390
+ const version = options.version ?? "0.3";
3391
+ reply.header("A2A-Version", version);
3392
+ const body = asRecord4(options.body ?? request.body);
3393
+ const params = asRecord4(body?.params);
3394
+ const taskId = asString4(params?.id);
3395
+ if (!taskId) {
3396
+ reply.send(jsonRpcError(requestId, -32602, "Invalid params: id is required"));
3397
+ return;
3398
+ }
3399
+ let task;
3400
+ try {
3401
+ task = await taskStore.load(taskId);
3402
+ } catch (err) {
3403
+ request.log.error({ err }, "a2a:jsonrpc:resubscribe");
3404
+ reply.status(500).send(jsonRpcError(requestId, -32603, "Internal error"));
3405
+ return;
3406
+ }
3407
+ if (!task) {
3408
+ reply.send({
3409
+ jsonrpc: "2.0",
3410
+ id: requestId,
3411
+ error: A2AError3.taskNotFound(taskId).toJSONRPCError()
3412
+ });
3413
+ return;
3414
+ }
3415
+ let runtimeLease;
3416
+ try {
3417
+ runtimeLease = runtimeRegistry.acquire(scope);
3418
+ } catch (err) {
3419
+ if (err instanceof A2ARuntimeCapacityError) {
3420
+ request.log.error({ err, scope }, "a2a:jsonrpc:runtime_capacity");
3421
+ reply.status(503).send(jsonRpcError(requestId, -32603, "A2A runtime capacity exhausted"));
3422
+ return;
3423
+ }
3424
+ throw err;
3425
+ }
3426
+ reply.hijack();
3427
+ reply.raw.writeHead(200, { ...SSE_HEADERS, "A2A-Version": version });
3428
+ const writeFrame = async (frame, event) => {
3429
+ if (reply.raw.writableEnded || reply.raw.destroyed) return;
3430
+ const data = `${event ? `event: ${event}
3431
+ ` : ""}data: ${JSON.stringify(frame)}
3432
+
3433
+ `;
3434
+ if (reply.raw.write(data)) return;
3435
+ await new Promise((resolve) => {
3436
+ const done = () => {
3437
+ reply.raw.off("drain", done);
3438
+ reply.raw.off("close", done);
3439
+ resolve();
3440
+ };
3441
+ reply.raw.once("drain", done);
3442
+ reply.raw.once("close", done);
3443
+ });
3444
+ };
3445
+ const resultFrame = (result) => ({
3446
+ jsonrpc: "2.0",
3447
+ id: requestId,
3448
+ result: version === "1.0" ? toA2A11StreamResult(result) : result
3449
+ });
3450
+ let disconnected = false;
3451
+ let iterator;
3452
+ const closeIterator = async () => {
3453
+ if (!iterator) return;
3454
+ const current = iterator;
3455
+ iterator = void 0;
3456
+ try {
3457
+ await current.return?.();
3458
+ } catch {
3459
+ }
3460
+ };
3461
+ const onClose = () => {
3462
+ disconnected = true;
3463
+ void closeIterator();
3464
+ };
3465
+ reply.raw.once("close", onClose);
3466
+ try {
3467
+ await writeFrame(resultFrame(task));
3468
+ if (disconnected || isTerminalA2AState(task.status.state)) return;
3469
+ const runId = asString4(task.metadata?.a2aRunId);
3470
+ if (!runId) return;
3471
+ const threadId = asString4(task.metadata?.threadId) ?? task.contextId;
3472
+ const messageId = asString4(task.metadata?.a2aMessageId);
3473
+ const cursor = asString4(task.metadata?.a2aStreamCursor);
3474
+ const runService = runServiceFor(scope);
3475
+ const reconcileCanonicalTask = async () => {
3476
+ const reconciled = await reconcileTaskFromRun(
3477
+ taskStore,
3478
+ runService,
3479
+ stateResolver,
3480
+ scope,
3481
+ options.body ?? request.body
3482
+ ).catch(() => void 0);
3483
+ return reconciled ?? taskStore.load(taskId).catch(() => void 0);
3484
+ };
3485
+ const run = await runService.getRun(scope.tenantId, runId, threadId, scope.assistantId).catch(() => void 0);
3486
+ if (!run) return;
3487
+ if (TERMINAL_RUN_STATUSES.has(run.status)) {
3488
+ const canonical2 = await reconcileCanonicalTask();
3489
+ if (canonical2 && !disconnected) await writeFrame(resultFrame(canonical2));
3490
+ return;
3491
+ }
3492
+ if (!messageId) return;
3493
+ const source = runService.stream(
3494
+ threadId,
3495
+ scope.assistantId,
3496
+ messageId,
3497
+ cursor,
3498
+ { tenantId: scope.tenantId, runId }
3499
+ );
3500
+ iterator = source[Symbol.asyncIterator]();
3501
+ try {
3502
+ for (; ; ) {
3503
+ if (disconnected) return;
3504
+ const next = await iterator.next();
3505
+ if (next.done) break;
3506
+ const chunk = next.value;
3507
+ const text = chunk.data?.content ?? "";
3508
+ if (chunk.type === MessageChunkTypes3.AI && text) {
3509
+ await writeFrame(resultFrame({
3510
+ kind: "status-update",
3511
+ taskId: task.id,
3512
+ contextId: task.contextId,
3513
+ status: {
3514
+ state: "working",
3515
+ message: agentMessage2(text),
3516
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3517
+ },
3518
+ final: false
3519
+ }));
3520
+ }
3521
+ if (chunk.type === MessageChunkTypes3.MESSAGE_COMPLETED || chunk.type === MessageChunkTypes3.MESSAGE_FAILED || chunk.type === MessageChunkTypes3.INTERRUPT) {
3522
+ break;
3523
+ }
3524
+ }
3525
+ } finally {
3526
+ await closeIterator();
3527
+ }
3528
+ if (disconnected) return;
3529
+ await runService.join(
3530
+ scope.tenantId,
3531
+ threadId,
3532
+ scope.assistantId,
3533
+ runId,
3534
+ RESUBSCRIBE_SETTLE_TIMEOUT_MS
3535
+ ).catch(() => void 0);
3536
+ if (disconnected) return;
3537
+ const canonical = await reconcileCanonicalTask();
3538
+ if (canonical) await writeFrame(resultFrame(canonical));
3539
+ } catch (err) {
3540
+ if (err instanceof ChunkCursorExpiredError) {
3541
+ await writeFrame({
3542
+ jsonrpc: "2.0",
3543
+ id: requestId,
3544
+ error: {
3545
+ code: -32603,
3546
+ message: "CURSOR_EXPIRED",
3547
+ data: { code: "CURSOR_EXPIRED" }
3548
+ }
3549
+ }, "error");
3550
+ return;
3551
+ }
3552
+ request.log.error({ err }, "a2a:jsonrpc:resubscribe:stream");
3553
+ await writeFrame({
3554
+ jsonrpc: "2.0",
3555
+ id: requestId,
3556
+ error: A2AError3.internalError(
3557
+ err instanceof Error ? err.message : "Streaming error."
3558
+ ).toJSONRPCError()
3559
+ }, "error");
3560
+ } finally {
3561
+ reply.raw.off("close", onClose);
3562
+ await closeIterator();
3563
+ runtimeLease.release();
3564
+ if (!reply.raw.writableEnded) reply.raw.end();
3565
+ }
3566
+ };
3567
+ const handleAgentCard = async (request, reply) => {
3568
+ const auth = await authService.authenticate(
3569
+ getHeader(request.headers.authorization) ?? getHeader(request.headers["x-api-key"])
3570
+ );
3571
+ if (!auth.authenticated) {
3572
+ reply.status(401).send({ error: "Unauthorized" });
3573
+ return;
3574
+ }
3575
+ let assistant;
3576
+ try {
3577
+ assistant = await authService.assertAgentAccess(
3578
+ auth,
3579
+ request.params.assistantId
3580
+ );
3581
+ } catch (err) {
3582
+ if (err instanceof A2AAuthError) {
3583
+ reply.status(err.statusCode).send({ error: err.message });
3584
+ return;
3585
+ }
3586
+ request.log.error({ err }, "a2a:agent-card:auth");
3587
+ reply.status(500).send({ error: "Internal error" });
3588
+ return;
3589
+ }
3590
+ reply.header("A2A-Version", "0.3");
3591
+ const officialAlias = request.url.split("?")[0].startsWith("/a2a/");
3592
+ reply.status(200).send(
3593
+ buildAgentCardForAssistant({
3594
+ assistant,
3595
+ baseUrl: buildBaseUrl(request),
3596
+ pushNotifications: hasPushStore(),
3597
+ ...officialAlias ? { jsonRpcPath: `/a2a/${assistant.id}` } : {}
3598
+ })
3599
+ );
3600
+ };
3601
+ app.get(
3602
+ "/api/a2a/agents/:assistantId/.well-known/agent-card.json",
3603
+ handleAgentCard
3604
+ );
3605
+ app.get(
3606
+ "/a2a/:assistantId/.well-known/agent-card.json",
3607
+ handleAgentCard
3608
+ );
3609
+ const handleJsonRpc = async (request, reply) => {
3610
+ const requestId = extractRequestId(request.body);
3611
+ const auth = await authService.authenticate(
3612
+ getHeader(request.headers.authorization) ?? getHeader(request.headers["x-api-key"])
3613
+ );
3614
+ if (!auth.authenticated) {
3615
+ reply.status(401).send(jsonRpcError(requestId, -32001, "Unauthorized"));
3616
+ return;
3617
+ }
3618
+ let assistant;
3619
+ try {
3620
+ assistant = await authService.assertAgentAccess(
3621
+ auth,
3622
+ request.params.assistantId
3623
+ );
3624
+ } catch (err) {
3625
+ if (err instanceof A2AAuthError) {
3626
+ reply.status(err.statusCode).send(
3627
+ jsonRpcError(
3628
+ requestId,
3629
+ authErrorToRpcCode(err.statusCode),
3630
+ err.message
3631
+ )
3632
+ );
3633
+ return;
3634
+ }
3635
+ request.log.error({ err }, "a2a:jsonrpc:auth");
3636
+ reply.status(500).send(jsonRpcError(requestId, -32603, "Internal error"));
3637
+ return;
3638
+ }
3639
+ const tenantId = auth.tenantId ?? "default";
3640
+ const scope = {
3641
+ tenantId,
3642
+ projectId: auth.projectId ?? "",
3643
+ workspaceId: await resolveWorkspaceId(getProjectStore, tenantId, auth.projectId),
3644
+ assistantId: assistant.id
3645
+ };
3646
+ const internalTasks = resolveTasks();
3647
+ const workItems = resolveTaskWorkItems();
3648
+ const taskStore = taskStoreFor(scope, internalTasks, workItems);
3649
+ const method = requestMethod(request.body);
3650
+ if (method === "tasks/list" || method === "agent/getExtendedAgentCard" || method === "agent/getAuthenticatedExtendedCard") {
3651
+ reply.header("A2A-Version", "0.3");
3652
+ if (method === "tasks/list") {
3653
+ const parsed = parseTasksListQuery(request.body);
3654
+ if (!parsed.ok) {
3655
+ reply.send(jsonRpcError(requestId, -32602, parsed.message));
3656
+ return;
3657
+ }
3658
+ try {
3659
+ const result2 = await listCanonicalA2ATasks(
3660
+ internalTasks,
3661
+ taskStore,
3662
+ scope,
3663
+ parsed.query
3664
+ );
3665
+ reply.send({ jsonrpc: "2.0", id: requestId, result: result2 });
3666
+ } catch (err) {
3667
+ request.log.error({ err }, "a2a:jsonrpc:tasks-list");
3668
+ reply.send(jsonRpcError(requestId, -32603, "Internal error"));
3669
+ }
3670
+ return;
3671
+ }
3672
+ const extendedCard = buildAgentCardForAssistant({
3673
+ assistant,
3674
+ baseUrl: buildBaseUrl(request),
3675
+ pushNotifications: hasPushStore(),
3676
+ ...request.url.split("?")[0].startsWith("/a2a/") ? { jsonRpcPath: `/a2a/${assistant.id}` } : {}
3677
+ });
3678
+ reply.send({ jsonrpc: "2.0", id: requestId, result: extendedCard });
3679
+ return;
3680
+ }
3681
+ if (method === "tasks/resubscribe") {
3682
+ await handleTaskResubscribe(request, reply, requestId, scope, taskStore);
3683
+ return;
3684
+ }
3685
+ const ingestor = makeFileIngestor(scope.tenantId, deps.maxFileBytes);
3686
+ const runService = runServiceFor(scope);
3687
+ const executor = new AxiomAgentExecutor({
3688
+ scope,
3689
+ assistant,
3690
+ taskStore,
3691
+ ingestor,
3692
+ runService,
3693
+ blocking: blockingFor(request.body),
3694
+ getInterrupts: (threadId) => stateResolver.getInterrupts(threadId, assistant.id, scope)
3695
+ });
3696
+ try {
3697
+ await reconcileTaskFromRun(
3698
+ taskStore,
3699
+ runService,
3700
+ stateResolver,
3701
+ scope,
3702
+ request.body
3703
+ );
3704
+ const canceled = await cancelTaskFromRun(
3705
+ taskStore,
3706
+ runService,
3707
+ scope,
3708
+ request.body
3709
+ );
3710
+ if (canceled) {
3711
+ reply.header("A2A-Version", "0.3");
3712
+ reply.send({ jsonrpc: "2.0", id: requestId, result: canceled });
3713
+ return;
3714
+ }
3715
+ } catch (err) {
3716
+ if (err instanceof A2AError3) {
3717
+ reply.send(jsonRpcError(requestId, err.code, err.message));
3718
+ return;
3719
+ }
3720
+ request.log.error({ err }, "a2a:jsonrpc:reconcile");
3721
+ reply.status(500).send(jsonRpcError(requestId, -32603, "Internal error"));
3722
+ return;
3723
+ }
3724
+ const resumeError = await resumeValidationError(taskStore, request.body).catch(() => void 0);
3725
+ if (resumeError !== void 0) {
3726
+ reply.send(jsonRpcError(requestId, -32602, resumeError));
3727
+ return;
3728
+ }
3729
+ let pushStore;
3730
+ try {
3731
+ pushStore = createPushStore(scope);
3732
+ } catch (err) {
3733
+ request.log.warn({ err }, "a2a:jsonrpc:push-store-unavailable");
3734
+ }
3735
+ const card = buildAgentCardForAssistant({
3736
+ assistant,
3737
+ baseUrl: buildBaseUrl(request),
3738
+ pushNotifications: pushStore !== void 0,
3739
+ ...request.url.split("?")[0].startsWith("/a2a/") ? { jsonRpcPath: `/a2a/${assistant.id}` } : {}
3740
+ });
3741
+ let runtimeLease;
3742
+ try {
3743
+ runtimeLease = runtimeRegistry.acquire(scope);
3744
+ } catch (err) {
3745
+ if (err instanceof A2ARuntimeCapacityError) {
3746
+ request.log.error({ err, scope }, "a2a:jsonrpc:runtime_capacity");
3747
+ reply.status(503).send(jsonRpcError(requestId, -32603, "A2A runtime capacity exhausted"));
3748
+ return;
3749
+ }
3750
+ throw err;
3751
+ }
3752
+ let result;
3753
+ try {
3754
+ const pushSender = pushStore ? deps.getPushNotificationSender?.(pushStore) ?? new A2APushNotificationSender({
3755
+ store: pushStore,
3756
+ ...deps.resolvePushHost ? { resolveHost: deps.resolvePushHost } : {}
3757
+ }) : void 0;
3758
+ const handler = new DefaultRequestHandler(
3759
+ card,
3760
+ taskStore,
3761
+ executor,
3762
+ runtimeLease.manager,
3763
+ pushStore,
3764
+ pushSender
3765
+ );
3766
+ const transport = new JsonRpcTransportHandler(handler);
3767
+ reply.header("A2A-Version", "0.3");
3768
+ result = await transport.handle(request.body);
3769
+ } catch (err) {
3770
+ runtimeLease.release();
3771
+ if (err instanceof A2AError3) {
3772
+ reply.send(jsonRpcError(requestId, err.code, err.message));
3773
+ return;
3774
+ }
3775
+ request.log.error({ err }, "a2a:jsonrpc:handler");
3776
+ reply.status(500).send(jsonRpcError(requestId, -32603, "Internal error"));
3777
+ return;
3778
+ }
3779
+ if (isAsyncGenerator(result)) {
3780
+ reply.hijack();
3781
+ reply.raw.writeHead(200, SSE_HEADERS);
3782
+ try {
3783
+ for await (const evt of result) {
3784
+ reply.raw.write(`data: ${JSON.stringify(evt)}
3785
+
3786
+ `);
3787
+ }
3788
+ } catch (err) {
3789
+ request.log.error({ err }, "a2a:jsonrpc:stream");
3790
+ const errorResponse = {
3791
+ jsonrpc: "2.0",
3792
+ id: requestId,
3793
+ error: A2AError3.internalError(
3794
+ err instanceof Error ? err.message : "Streaming error."
3795
+ ).toJSONRPCError()
3796
+ };
3797
+ reply.raw.write(`event: error
3798
+ data: ${JSON.stringify(errorResponse)}
3799
+
3800
+ `);
3801
+ } finally {
3802
+ runtimeLease.release();
3803
+ reply.raw.end();
3804
+ }
3805
+ return;
3806
+ }
3807
+ runtimeLease.release();
3808
+ reply.send(result);
3809
+ };
3810
+ app.post(
3811
+ "/api/a2a/agents/:assistantId/jsonrpc",
3812
+ handleJsonRpc
3813
+ );
3814
+ app.post(
3815
+ "/a2a/:assistantId",
3816
+ handleJsonRpc
3817
+ );
3818
+ app.post(
3819
+ "/api/a2a/agents/:assistantId/test-call",
3820
+ async (request, reply) => {
3821
+ const user = getSessionUser(request);
3822
+ if (!user?.tenantId) {
3823
+ reply.status(401).send({ success: false, error: "Unauthorized" });
3824
+ return;
3825
+ }
3826
+ const tenantId = user.tenantId;
3827
+ const body = request.body ?? {};
3828
+ const { projectId, message } = body;
3829
+ if (!projectId) {
3830
+ reply.status(400).send({ error: "projectId is required" });
3831
+ return;
3832
+ }
3833
+ if (!message || !message.trim()) {
3834
+ reply.status(400).send({ error: "message is required" });
3835
+ return;
3836
+ }
3837
+ const assistant = await getAssistantStore().getAssistantById(
3838
+ tenantId,
3839
+ request.params.assistantId
3840
+ );
3841
+ if (!assistant) {
3842
+ reply.status(404).send({ error: "Assistant not found" });
3843
+ return;
3844
+ }
3845
+ const project = await getProjectStore().getProjectById(
3846
+ tenantId,
3847
+ projectId
3848
+ );
3849
+ if (!project || project.tenantId !== tenantId) {
3850
+ reply.status(403).send({ error: "Project not found" });
3851
+ return;
3852
+ }
3853
+ const scope = {
3854
+ tenantId,
3855
+ projectId,
3856
+ workspaceId: project.workspaceId,
3857
+ assistantId: assistant.id
3858
+ };
3859
+ const ingestor = makeFileIngestor(scope.tenantId, deps.maxFileBytes);
3860
+ const internalTasks = resolveTasks();
3861
+ const workItems = resolveTaskWorkItems();
3862
+ const taskStore = taskStoreFor(scope, internalTasks, workItems);
3863
+ const executor = new AxiomAgentExecutor({
3864
+ scope,
3865
+ assistant,
3866
+ taskStore,
3867
+ ingestor,
3868
+ runService: runServiceFor(scope),
3869
+ blocking: true,
3870
+ getInterrupts: (threadId) => stateResolver.getInterrupts(threadId, assistant.id, scope)
3871
+ });
3872
+ const taskId = uuidv42();
3873
+ const contextId = uuidv42();
3874
+ const userMessage = {
3875
+ kind: "message",
3876
+ messageId: uuidv42(),
3877
+ role: "user",
3878
+ parts: [{ kind: "text", text: message }]
3879
+ };
3880
+ const requestContext = new RequestContext(userMessage, taskId, contextId);
3881
+ const bus = new DefaultExecutionEventBus2();
3882
+ reply.hijack();
3883
+ reply.raw.writeHead(200, SSE_HEADERS);
3884
+ const sendEvent = (event, data) => {
3885
+ reply.raw.write(`event: ${event}
3886
+ data: ${JSON.stringify(data)}
3887
+
3888
+ `);
3889
+ };
3890
+ bus.on("event", (event) => {
3891
+ sendEvent(event.kind, event);
3892
+ });
3893
+ try {
3894
+ await executor.execute(requestContext, bus);
3895
+ } catch (err) {
3896
+ request.log.error({ err }, "a2a:test-call:execution");
3897
+ try {
3898
+ await taskStore.save({
3899
+ kind: "task",
3900
+ id: taskId,
3901
+ contextId,
3902
+ status: { state: "failed", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
3903
+ });
3904
+ } catch (saveError) {
3905
+ request.log.error({ err: saveError }, "a2a:test-call:settle");
3906
+ }
3907
+ sendEvent("status-update", {
3908
+ kind: "status-update",
3909
+ taskId,
3910
+ contextId,
3911
+ status: { state: "failed", timestamp: (/* @__PURE__ */ new Date()).toISOString() },
3912
+ final: true
3913
+ });
3914
+ } finally {
3915
+ reply.raw.end();
3916
+ }
3917
+ }
3918
+ );
3919
+ const v1CardFor = (request, assistant, pushNotifications) => buildAgentCardForAssistantV1({
3920
+ assistant,
3921
+ baseUrl: buildBaseUrl(request),
3922
+ pushNotifications
3923
+ });
3924
+ const authenticateV1 = async (request) => {
3925
+ const auth = await authService.authenticate(
3926
+ getHeader(request.headers.authorization) ?? getHeader(request.headers["x-api-key"])
3927
+ );
3928
+ if (!auth.authenticated) {
3929
+ return { ok: false, status: 401, message: "Unauthorized" };
3930
+ }
3931
+ try {
3932
+ const assistant = await authService.assertAgentAccess(
3933
+ auth,
3934
+ request.params.assistantId
3935
+ );
3936
+ return { ok: true, assistant, auth };
3937
+ } catch (err) {
3938
+ if (err instanceof A2AAuthError) {
3939
+ return { ok: false, status: err.statusCode, message: err.message };
3940
+ }
3941
+ throw err;
3942
+ }
3943
+ };
3944
+ app.get(
3945
+ "/a2a/v1/:assistantId/.well-known/agent-card.json",
3946
+ async (request, reply) => {
3947
+ const auth = await authenticateV1(request);
3948
+ if (!auth.ok) {
3949
+ reply.status(auth.status).send({ error: auth.message });
3950
+ return;
3951
+ }
3952
+ reply.header("A2A-Version", "1.0");
3953
+ reply.status(200).send(
3954
+ AgentCardV1.toJSON(
3955
+ v1CardFor(request, auth.assistant, hasPushStore())
3956
+ )
3957
+ );
3958
+ }
3959
+ );
3960
+ app.post(
3961
+ "/a2a/v1/:assistantId",
3962
+ async (request, reply) => {
3963
+ const requestBody = asRecord4(request.body) ?? {};
3964
+ const requestId = extractRequestId(requestBody);
3965
+ reply.header("A2A-Version", "1.0");
3966
+ const requestedVersion = getHeader(request.headers["a2a-version"]);
3967
+ if (requestedVersion !== void 0 && !requestedVersion.startsWith("1.0")) {
3968
+ reply.header("A2A-Version", "1.0").send(versionNotSupportedError(requestId, requestedVersion));
3969
+ return;
3970
+ }
3971
+ const auth = await authenticateV1(request);
3972
+ if (!auth.ok) {
3973
+ reply.status(auth.status).send(jsonRpcError(requestId, authErrorToRpcCode(auth.status), auth.message));
3974
+ return;
3975
+ }
3976
+ const rawMethod = asString4(requestBody.method);
3977
+ const tenantId = auth.auth.tenantId ?? "default";
3978
+ const scope = {
3979
+ tenantId,
3980
+ projectId: auth.auth.projectId ?? "",
3981
+ workspaceId: await resolveWorkspaceId(getProjectStore, tenantId, auth.auth.projectId),
3982
+ assistantId: auth.assistant.id
3983
+ };
3984
+ const internalTasks = resolveTasks();
3985
+ const workItems = resolveTaskWorkItems();
3986
+ const taskStore = taskStoreFor(scope, internalTasks, workItems);
3987
+ if (rawMethod === "ListTasks") {
3988
+ const v03Body2 = toA2A03JsonRpcRequest(requestBody);
3989
+ const parsed = parseTasksListQuery(v03Body2);
3990
+ if (!parsed.ok) {
3991
+ reply.send(jsonRpcError(requestId, -32602, parsed.message));
3992
+ return;
3993
+ }
3994
+ try {
3995
+ const result2 = await listCanonicalA2ATasks(
3996
+ internalTasks,
3997
+ taskStore,
3998
+ scope,
3999
+ parsed.query
4000
+ );
4001
+ const listParams = asRecord4(requestBody.params);
4002
+ reply.send({
4003
+ jsonrpc: "2.0",
4004
+ id: requestId,
4005
+ result: toA2A11ListResult(result2, {
4006
+ includeArtifacts: listParams?.includeArtifacts === true
4007
+ })
4008
+ });
4009
+ } catch (err) {
4010
+ request.log.error({ err }, "a2a:v1:list");
4011
+ reply.send(jsonRpcError(requestId, -32603, "Internal error"));
4012
+ }
4013
+ return;
4014
+ }
4015
+ if (rawMethod === "GetExtendedAgentCard") {
4016
+ reply.send({
4017
+ jsonrpc: "2.0",
4018
+ id: requestId,
4019
+ result: AgentCardV1.toJSON(
4020
+ v1CardFor(request, auth.assistant, hasPushStore())
4021
+ )
4022
+ });
4023
+ return;
4024
+ }
4025
+ const v03Body = toA2A03JsonRpcRequest(requestBody);
4026
+ if (rawMethod === "SubscribeToTask") {
4027
+ await handleTaskResubscribe(
4028
+ request,
4029
+ reply,
4030
+ requestId,
4031
+ scope,
4032
+ taskStore,
4033
+ { version: "1.0", body: v03Body }
4034
+ );
4035
+ return;
4036
+ }
4037
+ const ingestor = makeFileIngestor(scope.tenantId, deps.maxFileBytes);
4038
+ const runService = runServiceFor(scope);
4039
+ const executor = new AxiomAgentExecutor({
4040
+ scope,
4041
+ assistant: auth.assistant,
4042
+ taskStore,
4043
+ ingestor,
4044
+ runService,
4045
+ blocking: blockingFor(v03Body),
4046
+ getInterrupts: (threadId) => stateResolver.getInterrupts(threadId, auth.assistant.id, scope)
4047
+ });
4048
+ try {
4049
+ await reconcileTaskFromRun(
4050
+ taskStore,
4051
+ runService,
4052
+ stateResolver,
4053
+ scope,
4054
+ v03Body
4055
+ );
4056
+ const canceled = await cancelTaskFromRun(
4057
+ taskStore,
4058
+ runService,
4059
+ scope,
4060
+ v03Body
4061
+ );
4062
+ if (canceled) {
4063
+ reply.send({
4064
+ jsonrpc: "2.0",
4065
+ id: requestId,
4066
+ result: toA2A11Result(canceled, rawMethod)
4067
+ });
4068
+ return;
4069
+ }
4070
+ } catch (err) {
4071
+ if (err instanceof A2AError3) {
4072
+ reply.send(jsonRpcError(requestId, err.code, err.message));
4073
+ return;
4074
+ }
4075
+ request.log.error({ err }, "a2a:v1:reconcile");
4076
+ reply.send(jsonRpcError(requestId, -32603, "Internal error"));
4077
+ return;
4078
+ }
4079
+ const resumeError = await resumeValidationError(taskStore, v03Body).catch(() => void 0);
4080
+ if (resumeError !== void 0) {
4081
+ reply.send(jsonRpcError(requestId, -32602, resumeError));
4082
+ return;
4083
+ }
4084
+ let pushStore;
4085
+ try {
4086
+ pushStore = createPushStore(scope);
4087
+ } catch (err) {
4088
+ request.log.warn({ err }, "a2a:v1:push-store-unavailable");
4089
+ }
4090
+ const card = buildAgentCardForAssistant({
4091
+ assistant: auth.assistant,
4092
+ baseUrl: buildBaseUrl(request),
4093
+ pushNotifications: pushStore !== void 0,
4094
+ jsonRpcPath: `/a2a/v1/${auth.assistant.id}`
4095
+ });
4096
+ let runtimeLease;
4097
+ try {
4098
+ runtimeLease = runtimeRegistry.acquire(scope);
4099
+ } catch (err) {
4100
+ if (err instanceof A2ARuntimeCapacityError) {
4101
+ request.log.error({ err, scope }, "a2a:v1:runtime_capacity");
4102
+ reply.status(503).send(jsonRpcError(requestId, -32603, "A2A runtime capacity exhausted"));
4103
+ return;
4104
+ }
4105
+ throw err;
4106
+ }
4107
+ let result;
4108
+ try {
4109
+ const pushSender = pushStore ? deps.getPushNotificationSender?.(pushStore) ?? new A2APushNotificationSender({
4110
+ store: pushStore,
4111
+ ...deps.resolvePushHost ? { resolveHost: deps.resolvePushHost } : {}
4112
+ }) : void 0;
4113
+ const handler = new DefaultRequestHandler(
4114
+ card,
4115
+ taskStore,
4116
+ executor,
4117
+ runtimeLease.manager,
4118
+ pushStore,
4119
+ pushSender
4120
+ );
4121
+ const transport = new JsonRpcTransportHandler(handler);
4122
+ result = await transport.handle(v03Body);
4123
+ } catch (err) {
4124
+ runtimeLease.release();
4125
+ if (err instanceof A2AError3) {
4126
+ reply.send(jsonRpcError(requestId, err.code, err.message));
4127
+ return;
4128
+ }
4129
+ request.log.error({ err }, "a2a:v1:handler");
4130
+ reply.send(jsonRpcError(requestId, -32603, "Internal error"));
4131
+ return;
4132
+ }
4133
+ const convertResult = (value) => {
4134
+ if (rawMethod === "ListTaskPushNotificationConfigs") {
4135
+ return toA2A11PushListResult(value);
4136
+ }
4137
+ const converted = toA2A11Result(value, rawMethod);
4138
+ if (rawMethod === "SendMessage") {
4139
+ if (value && typeof value === "object" && value.kind === "task") {
4140
+ return { task: converted };
4141
+ }
4142
+ if (value && typeof value === "object" && value.kind === "message") {
4143
+ return { message: converted };
4144
+ }
4145
+ }
4146
+ return converted;
4147
+ };
4148
+ if (isAsyncGenerator(result)) {
4149
+ reply.hijack();
4150
+ reply.raw.writeHead(200, SSE_HEADERS);
4151
+ try {
4152
+ for await (const frame of result) {
4153
+ const converted = {
4154
+ ...frame,
4155
+ ..."result" in frame ? { result: toA2A11StreamResult(frame.result) } : {}
4156
+ };
4157
+ reply.raw.write(`data: ${JSON.stringify(converted)}
4158
+
4159
+ `);
4160
+ }
4161
+ } catch (err) {
4162
+ request.log.error({ err }, "a2a:v1:stream");
4163
+ const errorResponse = jsonRpcError(
4164
+ requestId,
4165
+ -32603,
4166
+ err instanceof Error ? err.message : "Streaming error."
4167
+ );
4168
+ reply.raw.write(`event: error
4169
+ data: ${JSON.stringify(errorResponse)}
4170
+
4171
+ `);
4172
+ } finally {
4173
+ runtimeLease.release();
4174
+ reply.raw.end();
4175
+ }
4176
+ return;
4177
+ }
4178
+ runtimeLease.release();
4179
+ reply.send(
4180
+ "result" in result ? { ...result, result: convertResult(result.result) } : result
4181
+ );
4182
+ }
4183
+ );
4184
+ }
4185
+ export {
4186
+ registerA2AStandardRoutes
4187
+ };
4188
+ //# sourceMappingURL=a2a-standard-VL5I7B4W.mjs.map