@copilotkit/runtime 1.55.0-next.9 → 1.55.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 (345) hide show
  1. package/CHANGELOG.md +24 -2
  2. package/dist/agent/index.cjs +101 -12
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts.map +1 -1
  5. package/dist/agent/index.d.mts.map +1 -1
  6. package/dist/agent/index.mjs +102 -13
  7. package/dist/agent/index.mjs.map +1 -1
  8. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
  9. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts +4 -841
  10. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
  11. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts +4 -841
  12. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
  13. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
  14. package/dist/lib/runtime/copilot-runtime.cjs +3 -2
  15. package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
  16. package/dist/lib/runtime/copilot-runtime.d.cts +1 -1
  17. package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
  18. package/dist/lib/runtime/copilot-runtime.d.mts +3 -3
  19. package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
  20. package/dist/lib/runtime/copilot-runtime.mjs +3 -2
  21. package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
  22. package/dist/package.cjs +70 -47
  23. package/dist/package.mjs +70 -47
  24. package/dist/v2/express.cjs +8 -0
  25. package/dist/v2/express.d.cts +5 -0
  26. package/dist/v2/express.d.mts +5 -0
  27. package/dist/v2/express.mjs +5 -0
  28. package/dist/v2/hono.cjs +9 -0
  29. package/dist/v2/hono.d.cts +5 -0
  30. package/dist/v2/hono.d.mts +5 -0
  31. package/dist/v2/hono.mjs +5 -0
  32. package/dist/v2/index.cjs +8 -3
  33. package/dist/v2/index.d.cts +8 -5
  34. package/dist/v2/index.d.mts +8 -5
  35. package/dist/v2/index.mjs +5 -4
  36. package/dist/v2/node.cjs +8 -0
  37. package/dist/v2/node.d.cts +5 -0
  38. package/dist/v2/node.d.mts +5 -0
  39. package/dist/v2/node.mjs +5 -0
  40. package/dist/v2/runtime/core/fetch-cors.cjs +72 -0
  41. package/dist/v2/runtime/core/fetch-cors.cjs.map +1 -0
  42. package/dist/v2/runtime/core/fetch-cors.d.cts +20 -0
  43. package/dist/v2/runtime/core/fetch-cors.d.cts.map +1 -0
  44. package/dist/v2/runtime/core/fetch-cors.d.mts +20 -0
  45. package/dist/v2/runtime/core/fetch-cors.d.mts.map +1 -0
  46. package/dist/v2/runtime/core/fetch-cors.mjs +70 -0
  47. package/dist/v2/runtime/core/fetch-cors.mjs.map +1 -0
  48. package/dist/v2/runtime/core/fetch-handler.cjs +232 -0
  49. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -0
  50. package/dist/v2/runtime/core/fetch-handler.d.cts +40 -0
  51. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -0
  52. package/dist/v2/runtime/core/fetch-handler.d.mts +40 -0
  53. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -0
  54. package/dist/v2/runtime/core/fetch-handler.mjs +231 -0
  55. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -0
  56. package/dist/v2/runtime/core/fetch-router.cjs +68 -0
  57. package/dist/v2/runtime/core/fetch-router.cjs.map +1 -0
  58. package/dist/v2/runtime/core/fetch-router.mjs +67 -0
  59. package/dist/v2/runtime/core/fetch-router.mjs.map +1 -0
  60. package/dist/v2/runtime/core/hooks.cjs +29 -0
  61. package/dist/v2/runtime/core/hooks.cjs.map +1 -0
  62. package/dist/v2/runtime/core/hooks.d.cts +78 -0
  63. package/dist/v2/runtime/core/hooks.d.cts.map +1 -0
  64. package/dist/v2/runtime/core/hooks.d.mts +78 -0
  65. package/dist/v2/runtime/core/hooks.d.mts.map +1 -0
  66. package/dist/v2/runtime/core/hooks.mjs +25 -0
  67. package/dist/v2/runtime/core/hooks.mjs.map +1 -0
  68. package/dist/v2/runtime/{middleware-sse-parser.cjs → core/middleware-sse-parser.cjs} +2 -2
  69. package/dist/v2/runtime/core/middleware-sse-parser.cjs.map +1 -0
  70. package/dist/v2/runtime/{middleware-sse-parser.d.cts → core/middleware-sse-parser.d.cts} +1 -1
  71. package/dist/v2/runtime/core/middleware-sse-parser.d.cts.map +1 -0
  72. package/dist/v2/runtime/{middleware-sse-parser.d.mts → core/middleware-sse-parser.d.mts} +1 -1
  73. package/dist/v2/runtime/core/middleware-sse-parser.d.mts.map +1 -0
  74. package/dist/v2/runtime/{middleware-sse-parser.mjs → core/middleware-sse-parser.mjs} +1 -1
  75. package/dist/v2/runtime/core/middleware-sse-parser.mjs.map +1 -0
  76. package/dist/v2/runtime/{middleware.cjs → core/middleware.cjs} +2 -2
  77. package/dist/v2/runtime/core/middleware.cjs.map +1 -0
  78. package/dist/v2/runtime/{middleware.d.cts → core/middleware.d.cts} +1 -1
  79. package/dist/v2/runtime/core/middleware.d.cts.map +1 -0
  80. package/dist/v2/runtime/{middleware.d.mts → core/middleware.d.mts} +1 -1
  81. package/dist/v2/runtime/core/middleware.d.mts.map +1 -0
  82. package/dist/v2/runtime/{middleware.mjs → core/middleware.mjs} +1 -1
  83. package/dist/v2/runtime/core/middleware.mjs.map +1 -0
  84. package/dist/v2/runtime/{runtime.cjs → core/runtime.cjs} +35 -10
  85. package/dist/v2/runtime/core/runtime.cjs.map +1 -0
  86. package/dist/v2/runtime/{runtime.d.cts → core/runtime.d.cts} +41 -7
  87. package/dist/v2/runtime/core/runtime.d.cts.map +1 -0
  88. package/dist/v2/runtime/{runtime.d.mts → core/runtime.d.mts} +42 -8
  89. package/dist/v2/runtime/core/runtime.d.mts.map +1 -0
  90. package/dist/v2/runtime/{runtime.mjs → core/runtime.mjs} +36 -11
  91. package/dist/v2/runtime/core/runtime.mjs.map +1 -0
  92. package/dist/v2/runtime/endpoints/express-fetch-bridge.cjs +83 -0
  93. package/dist/v2/runtime/endpoints/express-fetch-bridge.cjs.map +1 -0
  94. package/dist/v2/runtime/endpoints/express-fetch-bridge.mjs +82 -0
  95. package/dist/v2/runtime/endpoints/express-fetch-bridge.mjs.map +1 -0
  96. package/dist/v2/runtime/endpoints/express-single.cjs +35 -181
  97. package/dist/v2/runtime/endpoints/express-single.cjs.map +1 -1
  98. package/dist/v2/runtime/endpoints/express-single.d.cts +35 -2
  99. package/dist/v2/runtime/endpoints/express-single.d.cts.map +1 -1
  100. package/dist/v2/runtime/endpoints/express-single.d.mts +35 -2
  101. package/dist/v2/runtime/endpoints/express-single.d.mts.map +1 -1
  102. package/dist/v2/runtime/endpoints/express-single.mjs +35 -178
  103. package/dist/v2/runtime/endpoints/express-single.mjs.map +1 -1
  104. package/dist/v2/runtime/endpoints/express.cjs +41 -195
  105. package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
  106. package/dist/v2/runtime/endpoints/express.d.cts +26 -4
  107. package/dist/v2/runtime/endpoints/express.d.cts.map +1 -1
  108. package/dist/v2/runtime/endpoints/express.d.mts +26 -4
  109. package/dist/v2/runtime/endpoints/express.d.mts.map +1 -1
  110. package/dist/v2/runtime/endpoints/express.mjs +41 -195
  111. package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
  112. package/dist/v2/runtime/endpoints/hono-single.cjs +11 -123
  113. package/dist/v2/runtime/endpoints/hono-single.cjs.map +1 -1
  114. package/dist/v2/runtime/endpoints/hono-single.d.cts +14 -11
  115. package/dist/v2/runtime/endpoints/hono-single.d.cts.map +1 -1
  116. package/dist/v2/runtime/endpoints/hono-single.d.mts +14 -11
  117. package/dist/v2/runtime/endpoints/hono-single.d.mts.map +1 -1
  118. package/dist/v2/runtime/endpoints/hono-single.mjs +11 -123
  119. package/dist/v2/runtime/endpoints/hono-single.mjs.map +1 -1
  120. package/dist/v2/runtime/endpoints/hono.cjs +23 -237
  121. package/dist/v2/runtime/endpoints/hono.cjs.map +1 -1
  122. package/dist/v2/runtime/endpoints/hono.d.cts +29 -120
  123. package/dist/v2/runtime/endpoints/hono.d.cts.map +1 -1
  124. package/dist/v2/runtime/endpoints/hono.d.mts +29 -120
  125. package/dist/v2/runtime/endpoints/hono.d.mts.map +1 -1
  126. package/dist/v2/runtime/endpoints/hono.mjs +22 -238
  127. package/dist/v2/runtime/endpoints/hono.mjs.map +1 -1
  128. package/dist/v2/runtime/endpoints/index.d.cts +2 -2
  129. package/dist/v2/runtime/endpoints/index.d.mts +2 -2
  130. package/dist/v2/runtime/endpoints/node-fetch-handler.cjs +26 -0
  131. package/dist/v2/runtime/endpoints/node-fetch-handler.cjs.map +1 -0
  132. package/dist/v2/runtime/endpoints/node-fetch-handler.d.cts +12 -0
  133. package/dist/v2/runtime/endpoints/node-fetch-handler.d.cts.map +1 -0
  134. package/dist/v2/runtime/endpoints/node-fetch-handler.d.mts +12 -0
  135. package/dist/v2/runtime/endpoints/node-fetch-handler.d.mts.map +1 -0
  136. package/dist/v2/runtime/endpoints/node-fetch-handler.mjs +24 -0
  137. package/dist/v2/runtime/endpoints/node-fetch-handler.mjs.map +1 -0
  138. package/dist/v2/runtime/endpoints/node.cjs +30 -0
  139. package/dist/v2/runtime/endpoints/node.cjs.map +1 -0
  140. package/dist/v2/runtime/endpoints/node.d.cts +27 -0
  141. package/dist/v2/runtime/endpoints/node.d.cts.map +1 -0
  142. package/dist/v2/runtime/endpoints/node.d.mts +27 -0
  143. package/dist/v2/runtime/endpoints/node.d.mts.map +1 -0
  144. package/dist/v2/runtime/endpoints/node.mjs +30 -0
  145. package/dist/v2/runtime/endpoints/node.mjs.map +1 -0
  146. package/dist/v2/runtime/express.d.cts +3 -0
  147. package/dist/v2/runtime/express.d.mts +3 -0
  148. package/dist/v2/runtime/handlers/get-runtime-info.cjs +2 -1
  149. package/dist/v2/runtime/handlers/get-runtime-info.cjs.map +1 -1
  150. package/dist/v2/runtime/handlers/get-runtime-info.mjs +2 -1
  151. package/dist/v2/runtime/handlers/get-runtime-info.mjs.map +1 -1
  152. package/dist/v2/runtime/handlers/handle-connect.cjs +6 -3
  153. package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
  154. package/dist/v2/runtime/handlers/handle-connect.mjs +6 -3
  155. package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
  156. package/dist/v2/runtime/handlers/handle-run.cjs +6 -3
  157. package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
  158. package/dist/v2/runtime/handlers/handle-run.mjs +6 -3
  159. package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
  160. package/dist/v2/runtime/handlers/handle-stop.cjs.map +1 -1
  161. package/dist/v2/runtime/handlers/handle-stop.mjs.map +1 -1
  162. package/dist/v2/runtime/handlers/handle-transcribe.cjs.map +1 -1
  163. package/dist/v2/runtime/handlers/handle-transcribe.mjs.map +1 -1
  164. package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
  165. package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
  166. package/dist/v2/runtime/handlers/intelligence/run.cjs +22 -1
  167. package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
  168. package/dist/v2/runtime/handlers/intelligence/run.mjs +22 -1
  169. package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
  170. package/dist/v2/runtime/handlers/intelligence/thread-names.cjs +1 -1
  171. package/dist/v2/runtime/handlers/intelligence/thread-names.cjs.map +1 -1
  172. package/dist/v2/runtime/handlers/intelligence/thread-names.mjs +1 -1
  173. package/dist/v2/runtime/handlers/intelligence/thread-names.mjs.map +1 -1
  174. package/dist/v2/runtime/handlers/shared/agent-utils.cjs +21 -6
  175. package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
  176. package/dist/v2/runtime/handlers/shared/agent-utils.mjs +21 -6
  177. package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
  178. package/dist/v2/runtime/handlers/shared/json-response.cjs +4 -1
  179. package/dist/v2/runtime/handlers/shared/json-response.cjs.map +1 -1
  180. package/dist/v2/runtime/handlers/shared/json-response.mjs +4 -1
  181. package/dist/v2/runtime/handlers/shared/json-response.mjs.map +1 -1
  182. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs.map +1 -1
  183. package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs.map +1 -1
  184. package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
  185. package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
  186. package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
  187. package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
  188. package/dist/v2/runtime/hono.d.cts +3 -0
  189. package/dist/v2/runtime/hono.d.mts +3 -0
  190. package/dist/v2/runtime/index.d.cts +16 -4
  191. package/dist/v2/runtime/index.d.cts.map +1 -0
  192. package/dist/v2/runtime/index.d.mts +16 -4
  193. package/dist/v2/runtime/index.d.mts.map +1 -0
  194. package/dist/v2/runtime/intelligence-platform/client.cjs +10 -1
  195. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  196. package/dist/v2/runtime/intelligence-platform/client.d.cts +22 -0
  197. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  198. package/dist/v2/runtime/intelligence-platform/client.d.mts +22 -0
  199. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  200. package/dist/v2/runtime/intelligence-platform/client.mjs +10 -1
  201. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  202. package/dist/v2/runtime/node.d.cts +3 -0
  203. package/dist/v2/runtime/node.d.mts +3 -0
  204. package/dist/v2/runtime/open-generative-ui-middleware.cjs +282 -0
  205. package/dist/v2/runtime/open-generative-ui-middleware.cjs.map +1 -0
  206. package/dist/v2/runtime/open-generative-ui-middleware.mjs +280 -0
  207. package/dist/v2/runtime/open-generative-ui-middleware.mjs.map +1 -0
  208. package/dist/v2/runtime/runner/intelligence.cjs +4 -4
  209. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  210. package/dist/v2/runtime/runner/intelligence.d.cts +6 -2
  211. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  212. package/dist/v2/runtime/runner/intelligence.d.mts +6 -2
  213. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  214. package/dist/v2/runtime/runner/intelligence.mjs +4 -4
  215. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  216. package/dist/v2/runtime/telemetry/telemetry-client.cjs +37 -0
  217. package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
  218. package/dist/v2/runtime/telemetry/telemetry-client.mjs +36 -0
  219. package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
  220. package/dist/v2/runtime/telemetry/utils.cjs +15 -0
  221. package/dist/v2/runtime/telemetry/utils.cjs.map +1 -0
  222. package/dist/v2/runtime/telemetry/utils.mjs +14 -0
  223. package/dist/v2/runtime/telemetry/utils.mjs.map +1 -0
  224. package/package.json +81 -48
  225. package/src/agent/__tests__/multimodal.test.ts +176 -0
  226. package/src/agent/index.ts +130 -19
  227. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +3 -3
  228. package/src/lib/runtime/copilot-runtime.ts +1 -0
  229. package/src/v2/express.ts +1 -0
  230. package/src/v2/hono.ts +1 -0
  231. package/src/v2/node.ts +1 -0
  232. package/src/v2/runtime/__tests__/backward-compat.test.ts +261 -0
  233. package/src/v2/runtime/__tests__/code-review-fixes.test.ts +500 -0
  234. package/src/v2/runtime/__tests__/cors-credentials.test.ts +2 -2
  235. package/src/v2/runtime/__tests__/express-adapter.test.ts +188 -0
  236. package/src/v2/runtime/__tests__/express-body-order.test.ts +1 -1
  237. package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +344 -0
  238. package/src/v2/runtime/__tests__/express-single-sse.test.ts +1 -1
  239. package/src/v2/runtime/__tests__/fetch-cors.test.ts +205 -0
  240. package/src/v2/runtime/__tests__/fetch-handler-validation.test.ts +372 -0
  241. package/src/v2/runtime/__tests__/fetch-handler.test.ts +456 -0
  242. package/src/v2/runtime/__tests__/fetch-router.test.ts +132 -0
  243. package/src/v2/runtime/__tests__/get-runtime-info.test.ts +4 -1
  244. package/src/v2/runtime/__tests__/handle-connect.test.ts +15 -13
  245. package/src/v2/runtime/__tests__/handle-run.test.ts +21 -17
  246. package/src/v2/runtime/__tests__/handle-threads.test.ts +1 -1
  247. package/src/v2/runtime/__tests__/handle-transcribe.test.ts +1 -1
  248. package/src/v2/runtime/__tests__/hono-adapter.test.ts +150 -0
  249. package/src/v2/runtime/__tests__/hooks-edge-cases.test.ts +457 -0
  250. package/src/v2/runtime/__tests__/hooks.test.ts +557 -0
  251. package/src/v2/runtime/__tests__/integration/bun/bun-servers.integration.test.ts +27 -0
  252. package/src/v2/runtime/__tests__/integration/bun/elysia-multi.ts +32 -0
  253. package/src/v2/runtime/__tests__/integration/bun/elysia-single.ts +33 -0
  254. package/src/v2/runtime/__tests__/integration/bun/hono-bun-multi.ts +25 -0
  255. package/src/v2/runtime/__tests__/integration/bun/hono-bun-single.ts +32 -0
  256. package/src/v2/runtime/__tests__/integration/helpers/create-test-runtime.ts +15 -0
  257. package/src/v2/runtime/__tests__/integration/helpers/sse-reader.ts +45 -0
  258. package/src/v2/runtime/__tests__/integration/helpers/test-agent.ts +58 -0
  259. package/src/v2/runtime/__tests__/integration/node-servers.integration.test.ts +39 -0
  260. package/src/v2/runtime/__tests__/integration/servers/express-multi.ts +35 -0
  261. package/src/v2/runtime/__tests__/integration/servers/express-single.ts +36 -0
  262. package/src/v2/runtime/__tests__/integration/servers/fetch-direct.ts +39 -0
  263. package/src/v2/runtime/__tests__/integration/servers/hono-multi.ts +30 -0
  264. package/src/v2/runtime/__tests__/integration/servers/hono-single.ts +37 -0
  265. package/src/v2/runtime/__tests__/integration/servers/node-multi.ts +45 -0
  266. package/src/v2/runtime/__tests__/integration/servers/node-single.ts +46 -0
  267. package/src/v2/runtime/__tests__/integration/servers/types.ts +18 -0
  268. package/src/v2/runtime/__tests__/integration/suites/multi-endpoint.suite.ts +358 -0
  269. package/src/v2/runtime/__tests__/integration/suites/single-endpoint.suite.ts +363 -0
  270. package/src/v2/runtime/__tests__/middleware-express.test.ts +1 -1
  271. package/src/v2/runtime/__tests__/middleware-single-express.test.ts +1 -1
  272. package/src/v2/runtime/__tests__/middleware-single.test.ts +1 -1
  273. package/src/v2/runtime/__tests__/middleware-sse-parser.test.ts +1 -1
  274. package/src/v2/runtime/__tests__/middleware.test.ts +1 -2
  275. package/src/v2/runtime/__tests__/node-fetch-handler.test.ts +157 -0
  276. package/src/v2/runtime/__tests__/open-generative-ui-middleware.e2e.test.ts +728 -0
  277. package/src/v2/runtime/__tests__/router-edge-cases.test.ts +217 -0
  278. package/src/v2/runtime/__tests__/routing-express.test.ts +1 -1
  279. package/src/v2/runtime/__tests__/routing-single-express.test.ts +1 -1
  280. package/src/v2/runtime/__tests__/routing-single.test.ts +1 -1
  281. package/src/v2/runtime/__tests__/routing.test.ts +1 -1
  282. package/src/v2/runtime/__tests__/runtime.test.ts +110 -1
  283. package/src/v2/runtime/__tests__/telemetry.test.ts +62 -1
  284. package/src/v2/runtime/core/fetch-cors.ts +136 -0
  285. package/src/v2/runtime/core/fetch-handler.ts +415 -0
  286. package/src/v2/runtime/core/fetch-router.ts +112 -0
  287. package/src/v2/runtime/core/hooks.ts +151 -0
  288. package/src/v2/runtime/{runtime.ts → core/runtime.ts} +79 -10
  289. package/src/v2/runtime/endpoints/express-fetch-bridge.ts +137 -0
  290. package/src/v2/runtime/endpoints/express-single.ts +42 -219
  291. package/src/v2/runtime/endpoints/express.ts +128 -230
  292. package/src/v2/runtime/endpoints/hono-single.ts +19 -171
  293. package/src/v2/runtime/endpoints/hono.ts +45 -270
  294. package/src/v2/runtime/endpoints/node-fetch-handler.ts +48 -0
  295. package/src/v2/runtime/endpoints/node.ts +28 -0
  296. package/src/v2/runtime/handlers/get-runtime-info.ts +3 -2
  297. package/src/v2/runtime/handlers/handle-connect.ts +7 -4
  298. package/src/v2/runtime/handlers/handle-run.ts +7 -4
  299. package/src/v2/runtime/handlers/handle-stop.ts +1 -1
  300. package/src/v2/runtime/handlers/handle-transcribe.ts +1 -1
  301. package/src/v2/runtime/handlers/intelligence/connect.ts +1 -1
  302. package/src/v2/runtime/handlers/intelligence/run.ts +31 -1
  303. package/src/v2/runtime/handlers/intelligence/thread-names.ts +2 -2
  304. package/src/v2/runtime/handlers/intelligence/threads.ts +1 -1
  305. package/src/v2/runtime/handlers/shared/agent-utils.ts +29 -10
  306. package/src/v2/runtime/handlers/shared/json-response.ts +4 -1
  307. package/src/v2/runtime/handlers/shared/resolve-intelligence-user.ts +1 -1
  308. package/src/v2/runtime/handlers/sse/connect.ts +1 -1
  309. package/src/v2/runtime/handlers/sse/run.ts +1 -1
  310. package/src/v2/runtime/hono.ts +2 -0
  311. package/src/v2/runtime/index.ts +27 -1
  312. package/src/v2/runtime/intelligence-platform/client.ts +50 -1
  313. package/src/v2/runtime/node.ts +6 -0
  314. package/src/v2/runtime/open-generative-ui-middleware.ts +373 -0
  315. package/src/v2/runtime/runner/intelligence.ts +14 -4
  316. package/src/v2/runtime/telemetry/telemetry-client.ts +56 -0
  317. package/src/v2/runtime/telemetry/utils.ts +15 -0
  318. package/tsdown.config.ts +8 -1
  319. package/vitest.config.mjs +2 -5
  320. package/.eslintrc.js +0 -7
  321. package/dist/v2/runtime/endpoints/express-utils.cjs +0 -119
  322. package/dist/v2/runtime/endpoints/express-utils.cjs.map +0 -1
  323. package/dist/v2/runtime/endpoints/express-utils.mjs +0 -117
  324. package/dist/v2/runtime/endpoints/express-utils.mjs.map +0 -1
  325. package/dist/v2/runtime/handlers/intelligence/threads.cjs +0 -159
  326. package/dist/v2/runtime/handlers/intelligence/threads.cjs.map +0 -1
  327. package/dist/v2/runtime/handlers/intelligence/threads.mjs +0 -154
  328. package/dist/v2/runtime/handlers/intelligence/threads.mjs.map +0 -1
  329. package/dist/v2/runtime/middleware-sse-parser.cjs.map +0 -1
  330. package/dist/v2/runtime/middleware-sse-parser.d.cts.map +0 -1
  331. package/dist/v2/runtime/middleware-sse-parser.d.mts.map +0 -1
  332. package/dist/v2/runtime/middleware-sse-parser.mjs.map +0 -1
  333. package/dist/v2/runtime/middleware.cjs.map +0 -1
  334. package/dist/v2/runtime/middleware.d.cts.map +0 -1
  335. package/dist/v2/runtime/middleware.d.mts.map +0 -1
  336. package/dist/v2/runtime/middleware.mjs.map +0 -1
  337. package/dist/v2/runtime/runtime.cjs.map +0 -1
  338. package/dist/v2/runtime/runtime.d.cts.map +0 -1
  339. package/dist/v2/runtime/runtime.d.mts.map +0 -1
  340. package/dist/v2/runtime/runtime.mjs.map +0 -1
  341. package/src/v2/runtime/__tests__/express-abort-signal.test.ts +0 -25
  342. package/src/v2/runtime/endpoints/express-utils.ts +0 -182
  343. package/src/v2/runtime/handler.ts +0 -3
  344. /package/src/v2/runtime/{middleware-sse-parser.ts → core/middleware-sse-parser.ts} +0 -0
  345. /package/src/v2/runtime/{middleware.ts → core/middleware.ts} +0 -0
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Regression tests for code review fixes.
3
+ * Each describe block maps to a specific review item.
4
+ */
5
+ import { describe, it, expect, vi } from "vitest";
6
+ import { createCopilotRuntimeHandler } from "../core/fetch-handler";
7
+ import { CopilotRuntime } from "../core/runtime";
8
+ import { matchRoute } from "../core/fetch-router";
9
+ import { handleCors, addCorsHeaders } from "../core/fetch-cors";
10
+ import type { CopilotCorsConfig } from "../core/fetch-cors";
11
+ import type { AbstractAgent } from "@ag-ui/client";
12
+
13
+ /* ------------------------------------------------------------------------------------------------
14
+ * Helpers
15
+ * --------------------------------------------------------------------------------------------- */
16
+
17
+ const createMockAgent = () => {
18
+ const agent: unknown = {
19
+ execute: vi.fn().mockResolvedValue({ events: [] }),
20
+ };
21
+ (agent as { clone: () => unknown }).clone = () => createMockAgent();
22
+ return agent as AbstractAgent;
23
+ };
24
+
25
+ const createRuntime = () =>
26
+ new CopilotRuntime({ agents: { default: createMockAgent() } });
27
+
28
+ const get = (url: string) => new Request(url, { method: "GET" });
29
+
30
+ const post = (url: string, body?: unknown) =>
31
+ new Request(url, {
32
+ method: "POST",
33
+ headers: { "Content-Type": "application/json" },
34
+ body: body ? JSON.stringify(body) : undefined,
35
+ });
36
+
37
+ /* ------------------------------------------------------------------------------------------------
38
+ * Item 1: __cpk_methodCall mutation lost if hooks replace the request
39
+ *
40
+ * In single-route mode, the parsed method call must survive even if
41
+ * onBeforeHandler returns a new Request object.
42
+ * --------------------------------------------------------------------------------------------- */
43
+
44
+ describe("Item 1: methodCall preserved when hooks replace request", () => {
45
+ it("single-route dispatch works after onBeforeHandler replaces request", async () => {
46
+ const handler = createCopilotRuntimeHandler({
47
+ runtime: createRuntime(),
48
+ basePath: "/api",
49
+ mode: "single-route",
50
+ hooks: {
51
+ onBeforeHandler: ({ request }) => {
52
+ // Return a brand-new Request with only headers modified.
53
+ // This discards any properties stashed on the old request object.
54
+ return new Request(request, {
55
+ headers: new Headers([...request.headers, ["x-replaced", "true"]]),
56
+ });
57
+ },
58
+ },
59
+ });
60
+
61
+ const response = await handler(
62
+ post("http://localhost/api", { method: "info" }),
63
+ );
64
+ // Should succeed (200) rather than crash with undefined methodCall
65
+ expect(response.status).toBe(200);
66
+ const body = await response.json();
67
+ expect(body).toHaveProperty("version");
68
+ });
69
+
70
+ it("single-route agent/run works after onBeforeHandler replaces request", async () => {
71
+ const handler = createCopilotRuntimeHandler({
72
+ runtime: createRuntime(),
73
+ basePath: "/api",
74
+ mode: "single-route",
75
+ hooks: {
76
+ onBeforeHandler: ({ request }) => {
77
+ return new Request(request.url, {
78
+ method: request.method,
79
+ headers: request.headers,
80
+ body: request.clone().body,
81
+ });
82
+ },
83
+ },
84
+ });
85
+
86
+ const response = await handler(
87
+ post("http://localhost/api", {
88
+ method: "agent/run",
89
+ params: { agentId: "default" },
90
+ body: { threadId: "t1", runId: "r1" },
91
+ }),
92
+ );
93
+ // Should not crash — the method call data is passed explicitly, not via request property
94
+ expect(response).toBeInstanceOf(Response);
95
+ });
96
+ });
97
+
98
+ /* ------------------------------------------------------------------------------------------------
99
+ * Item 2: credentials: true + wildcard origin silently produces invalid CORS
100
+ *
101
+ * When credentials is true and origin resolves to "*", we must auto-resolve
102
+ * to the request origin to comply with the Fetch spec.
103
+ * --------------------------------------------------------------------------------------------- */
104
+
105
+ describe("Item 2: credentials + wildcard CORS", () => {
106
+ it("auto-resolves wildcard to request origin when credentials enabled (preflight)", () => {
107
+ const request = new Request("http://localhost/api", {
108
+ method: "OPTIONS",
109
+ headers: { Origin: "https://app.example.com" },
110
+ });
111
+ const config: CopilotCorsConfig = { credentials: true };
112
+ const response = handleCors(request, config)!;
113
+
114
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBe(
115
+ "https://app.example.com",
116
+ );
117
+ expect(response.headers.get("Access-Control-Allow-Credentials")).toBe(
118
+ "true",
119
+ );
120
+ });
121
+
122
+ it("auto-resolves wildcard to request origin in addCorsHeaders", () => {
123
+ const response = new Response("ok", { status: 200 });
124
+ const config: CopilotCorsConfig = { credentials: true };
125
+ const result = addCorsHeaders(response, config, "https://mysite.com");
126
+
127
+ expect(result.headers.get("Access-Control-Allow-Origin")).toBe(
128
+ "https://mysite.com",
129
+ );
130
+ expect(result.headers.get("Access-Control-Allow-Credentials")).toBe("true");
131
+ });
132
+
133
+ it("skips CORS entirely when credentials enabled, wildcard origin, and no request origin", () => {
134
+ const request = new Request("http://localhost/api", {
135
+ method: "OPTIONS",
136
+ // No Origin header
137
+ });
138
+ const config: CopilotCorsConfig = { credentials: true };
139
+ const response = handleCors(request, config)!;
140
+
141
+ // Should not set any CORS origin since there's no request origin to reflect
142
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBeNull();
143
+ });
144
+
145
+ it("explicit origin string + credentials does not trigger auto-resolution", () => {
146
+ const request = new Request("http://localhost/api", {
147
+ method: "OPTIONS",
148
+ headers: { Origin: "https://app.example.com" },
149
+ });
150
+ const config: CopilotCorsConfig = {
151
+ origin: "https://specific.example.com",
152
+ credentials: true,
153
+ };
154
+ const response = handleCors(request, config)!;
155
+
156
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBe(
157
+ "https://specific.example.com",
158
+ );
159
+ });
160
+
161
+ it("end-to-end: handler CORS with credentials does not produce wildcard + credentials", async () => {
162
+ const handler = createCopilotRuntimeHandler({
163
+ runtime: createRuntime(),
164
+ basePath: "/api",
165
+ cors: { credentials: true },
166
+ });
167
+
168
+ const response = await handler(
169
+ new Request("http://localhost/api/info", {
170
+ method: "GET",
171
+ headers: { Origin: "https://app.example.com" },
172
+ }),
173
+ );
174
+
175
+ const allowOrigin = response.headers.get("Access-Control-Allow-Origin");
176
+ const allowCreds = response.headers.get("Access-Control-Allow-Credentials");
177
+
178
+ // Must NEVER be "*" + "true" — that's the invalid combo
179
+ if (allowCreds === "true") {
180
+ expect(allowOrigin).not.toBe("*");
181
+ }
182
+ expect(allowOrigin).toBe("https://app.example.com");
183
+ });
184
+ });
185
+
186
+ /* ------------------------------------------------------------------------------------------------
187
+ * Item 3: runOnError can throw unhandled
188
+ *
189
+ * If the onError hook itself throws, the handler must catch it and return
190
+ * a 500 response rather than letting the promise reject.
191
+ * --------------------------------------------------------------------------------------------- */
192
+
193
+ describe("Item 3: onError hook throwing is caught", () => {
194
+ it("returns 500 when onError throws synchronously", async () => {
195
+ const handler = createCopilotRuntimeHandler({
196
+ runtime: createRuntime(),
197
+ basePath: "/api",
198
+ hooks: {
199
+ onRequest: () => {
200
+ throw new Error("trigger error");
201
+ },
202
+ onError: () => {
203
+ throw new Error("hook exploded");
204
+ },
205
+ },
206
+ });
207
+
208
+ // Must not reject — should return a Response
209
+ const response = await handler(get("http://localhost/api/info"));
210
+ expect(response.status).toBe(500);
211
+ const body = await response.json();
212
+ expect(body.error).toBe("internal_error");
213
+ });
214
+
215
+ it("returns 500 when onError throws asynchronously", async () => {
216
+ const handler = createCopilotRuntimeHandler({
217
+ runtime: createRuntime(),
218
+ basePath: "/api",
219
+ hooks: {
220
+ onRequest: () => {
221
+ throw new Error("trigger error");
222
+ },
223
+ onError: async () => {
224
+ await Promise.resolve();
225
+ throw new Error("async hook exploded");
226
+ },
227
+ },
228
+ });
229
+
230
+ const response = await handler(get("http://localhost/api/info"));
231
+ expect(response.status).toBe(500);
232
+ });
233
+ });
234
+
235
+ /* ------------------------------------------------------------------------------------------------
236
+ * Item 4: decodeURIComponent can throw URIError on malformed URLs
237
+ *
238
+ * Malformed percent-encoding in path segments must not crash with an
239
+ * uncaught URIError; the router should return null (no match).
240
+ * --------------------------------------------------------------------------------------------- */
241
+
242
+ describe("Item 4: malformed URI encoding handled gracefully", () => {
243
+ it("returns null for malformed agentId in /agent/:agentId/run", () => {
244
+ const result = matchRoute("/api/agent/%ZZ/run", "/api");
245
+ expect(result).toBeNull();
246
+ });
247
+
248
+ it("returns null for malformed agentId in /agent/:agentId/connect", () => {
249
+ const result = matchRoute("/api/agent/%ZZ/connect", "/api");
250
+ expect(result).toBeNull();
251
+ });
252
+
253
+ it("returns null for malformed agentId in /agent/:agentId/stop/:threadId", () => {
254
+ const result = matchRoute("/api/agent/%ZZ/stop/valid-thread", "/api");
255
+ expect(result).toBeNull();
256
+ });
257
+
258
+ it("returns null for malformed threadId in /agent/:agentId/stop/:threadId", () => {
259
+ const result = matchRoute("/api/agent/valid-agent/stop/%ZZ", "/api");
260
+ expect(result).toBeNull();
261
+ });
262
+
263
+ it("still decodes valid percent-encoding correctly", () => {
264
+ const result = matchRoute("/api/agent/hello%20world/run", "/api");
265
+ expect(result).toEqual({ method: "agent/run", agentId: "hello world" });
266
+ });
267
+
268
+ it("end-to-end: malformed URL returns 404", async () => {
269
+ const handler = createCopilotRuntimeHandler({
270
+ runtime: createRuntime(),
271
+ basePath: "/api",
272
+ });
273
+
274
+ const response = await handler(
275
+ post("http://localhost/api/agent/%ZZ/run", { threadId: "t1" }),
276
+ );
277
+ expect(response.status).toBe(404);
278
+ });
279
+ });
280
+
281
+ /* ------------------------------------------------------------------------------------------------
282
+ * Item 8: Missing Allow header on 405 responses
283
+ *
284
+ * RFC 9110 §15.5.6 requires 405 responses to include an Allow header.
285
+ * --------------------------------------------------------------------------------------------- */
286
+
287
+ describe("Item 8: Allow header on 405 responses", () => {
288
+ it("multi-route: 405 for POST to /info includes Allow: GET", async () => {
289
+ const handler = createCopilotRuntimeHandler({
290
+ runtime: createRuntime(),
291
+ basePath: "/api",
292
+ });
293
+
294
+ const response = await handler(post("http://localhost/api/info"));
295
+ expect(response.status).toBe(405);
296
+ expect(response.headers.get("Allow")).toBe("GET");
297
+ });
298
+
299
+ it("multi-route: 405 for GET to /agent/:id/run includes Allow: POST", async () => {
300
+ const handler = createCopilotRuntimeHandler({
301
+ runtime: createRuntime(),
302
+ basePath: "/api",
303
+ });
304
+
305
+ const response = await handler(
306
+ get("http://localhost/api/agent/default/run"),
307
+ );
308
+ expect(response.status).toBe(405);
309
+ expect(response.headers.get("Allow")).toBe("POST");
310
+ });
311
+
312
+ it("single-route: 405 for GET includes Allow: POST", async () => {
313
+ const handler = createCopilotRuntimeHandler({
314
+ runtime: createRuntime(),
315
+ basePath: "/api",
316
+ mode: "single-route",
317
+ });
318
+
319
+ const response = await handler(get("http://localhost/api"));
320
+ expect(response.status).toBe(405);
321
+ expect(response.headers.get("Allow")).toBe("POST");
322
+ });
323
+ });
324
+
325
+ /* ------------------------------------------------------------------------------------------------
326
+ * Item 9: Error messages not leaked to clients in 500 responses
327
+ *
328
+ * The 500 response body should contain { error: "internal_error" }
329
+ * without exposing error.message which could contain file paths, DB errors, etc.
330
+ * --------------------------------------------------------------------------------------------- */
331
+
332
+ describe("Item 9: error messages not leaked to clients", () => {
333
+ it("500 response does not include error message", async () => {
334
+ const handler = createCopilotRuntimeHandler({
335
+ runtime: createRuntime(),
336
+ basePath: "/api",
337
+ hooks: {
338
+ onRequest: () => {
339
+ throw new Error("secret: /etc/passwd contents here");
340
+ },
341
+ },
342
+ });
343
+
344
+ const response = await handler(get("http://localhost/api/info"));
345
+ expect(response.status).toBe(500);
346
+ const body = await response.json();
347
+ expect(body.error).toBe("internal_error");
348
+ expect(body.message).toBeUndefined();
349
+ expect(JSON.stringify(body)).not.toContain("passwd");
350
+ });
351
+
352
+ it("non-Error throws also produce clean 500", async () => {
353
+ const handler = createCopilotRuntimeHandler({
354
+ runtime: createRuntime(),
355
+ basePath: "/api",
356
+ hooks: {
357
+ onRequest: () => {
358
+ throw { secretData: "db_password_123" };
359
+ },
360
+ },
361
+ });
362
+
363
+ const response = await handler(get("http://localhost/api/info"));
364
+ expect(response.status).toBe(500);
365
+ const body = await response.json();
366
+ expect(body.error).toBe("internal_error");
367
+ expect(body.message).toBeUndefined();
368
+ expect(JSON.stringify(body)).not.toContain("db_password");
369
+ });
370
+ });
371
+
372
+ /* ------------------------------------------------------------------------------------------------
373
+ * Item 12: Missing Vary headers on preflight
374
+ *
375
+ * Preflight responses should include Vary: Access-Control-Request-Headers
376
+ * and Vary: Access-Control-Request-Method for CDN caching correctness.
377
+ * --------------------------------------------------------------------------------------------- */
378
+
379
+ describe("Item 12: Vary headers on preflight", () => {
380
+ it("preflight includes Vary for CORS request headers and method", () => {
381
+ const request = new Request("http://localhost/api", {
382
+ method: "OPTIONS",
383
+ headers: { Origin: "https://example.com" },
384
+ });
385
+ const response = handleCors(request, {})!;
386
+
387
+ const vary = response.headers.get("Vary") ?? "";
388
+ expect(vary).toContain("Access-Control-Request-Headers");
389
+ expect(vary).toContain("Access-Control-Request-Method");
390
+ });
391
+
392
+ it("preflight includes Vary: Origin when origin is not wildcard", () => {
393
+ const request = new Request("http://localhost/api", {
394
+ method: "OPTIONS",
395
+ headers: { Origin: "https://example.com" },
396
+ });
397
+ const response = handleCors(request, {
398
+ origin: "https://example.com",
399
+ })!;
400
+
401
+ const vary = response.headers.get("Vary") ?? "";
402
+ expect(vary).toContain("Origin");
403
+ expect(vary).toContain("Access-Control-Request-Headers");
404
+ expect(vary).toContain("Access-Control-Request-Method");
405
+ });
406
+ });
407
+
408
+ /* ------------------------------------------------------------------------------------------------
409
+ * Item 7: synthesizeBody null guard uses wrong comparison
410
+ *
411
+ * Tested indirectly through express-fetch-bridge.
412
+ * The synthesizeBody function should treat null bodies as empty (not serialize "null").
413
+ * This is already mitigated by hasPreParsedBody rejecting null, but we test the
414
+ * boundary to ensure the fix is correct.
415
+ * --------------------------------------------------------------------------------------------- */
416
+
417
+ describe("Item 7: synthesizeBody null guard", () => {
418
+ // Verify that null req.body causes hasPreParsedBody to return false,
419
+ // meaning the generic (stream-based) handler runs instead of trying to
420
+ // serialize null into the string "null".
421
+ it("null req.body is treated as no pre-parsed body (falls through to generic handler)", async () => {
422
+ const { createExpressNodeHandler } =
423
+ await import("../endpoints/express-fetch-bridge");
424
+
425
+ let handlerCalled = false;
426
+ const fetchHandler = async (_req: Request) => {
427
+ handlerCalled = true;
428
+ return new Response("ok", { status: 200 });
429
+ };
430
+
431
+ const nodeHandler = createExpressNodeHandler(fetchHandler);
432
+
433
+ // Use supertest-style approach: create a real HTTP server to exercise
434
+ // the full path. Since we can't easily mock Node streams, we verify
435
+ // the hasPreParsedBody logic through the express-fetch-bridge tests.
436
+ // Here we just assert the module loads and the handler is created correctly.
437
+ expect(typeof nodeHandler).toBe("function");
438
+ expect(handlerCalled).toBe(false);
439
+ });
440
+ });
441
+
442
+ /* ------------------------------------------------------------------------------------------------
443
+ * Item 10: After-request middleware does not double-consume response body
444
+ *
445
+ * The response passed to afterRequestMiddleware should be a clone, so
446
+ * reading its body doesn't affect the response sent to the client.
447
+ * --------------------------------------------------------------------------------------------- */
448
+
449
+ describe("Item 10: after-request middleware gets cloned response", () => {
450
+ it("response body is still readable after middleware runs", async () => {
451
+ const handler = createCopilotRuntimeHandler({
452
+ runtime: createRuntime(),
453
+ basePath: "/api",
454
+ });
455
+
456
+ // Simply call the handler and verify the response body is readable
457
+ const response = await handler(get("http://localhost/api/info"));
458
+ expect(response.status).toBe(200);
459
+
460
+ // The body should be consumable — if clone() wasn't used, the middleware
461
+ // might have consumed it first
462
+ const body = await response.json();
463
+ expect(body).toHaveProperty("version");
464
+ });
465
+ });
466
+
467
+ /* ------------------------------------------------------------------------------------------------
468
+ * Breaking Change: CopilotKitRequestHandler deprecated alias
469
+ *
470
+ * The CopilotKitRequestHandler type must still be importable for backward
471
+ * compatibility, even though it's deprecated.
472
+ * --------------------------------------------------------------------------------------------- */
473
+
474
+ describe("Breaking change: CopilotKitRequestHandler type alias exists", () => {
475
+ it("CopilotKitRequestHandler is exported from the package index", async () => {
476
+ // Dynamic import to check the type exists at runtime
477
+ const exports = await import("../index");
478
+ // TypeScript types don't exist at runtime, but we can verify the module
479
+ // loads without errors. The type check is compile-time.
480
+ expect(exports).toBeDefined();
481
+ });
482
+ });
483
+
484
+ /* ------------------------------------------------------------------------------------------------
485
+ * Item 13: Forward-reference code ordering in node-fetch-handler.ts
486
+ *
487
+ * createCopilotNodeHandler should be defined before createNodeFetchHandler
488
+ * references it. This is a code quality fix — no runtime behavior change.
489
+ * --------------------------------------------------------------------------------------------- */
490
+
491
+ describe("Item 13: node-fetch-handler exports are both available", () => {
492
+ it("both createCopilotNodeHandler and createNodeFetchHandler are exported", async () => {
493
+ const { createCopilotNodeHandler, createNodeFetchHandler } =
494
+ await import("../endpoints/node-fetch-handler");
495
+ expect(typeof createCopilotNodeHandler).toBe("function");
496
+ expect(typeof createNodeFetchHandler).toBe("function");
497
+ // The deprecated alias should reference the same function
498
+ expect(createNodeFetchHandler).toBe(createCopilotNodeHandler);
499
+ });
500
+ });
@@ -1,9 +1,9 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect } from "vitest";
2
2
  import {
3
3
  createCopilotEndpoint,
4
4
  createCopilotEndpointSingleRoute,
5
5
  } from "../endpoints";
6
- import { CopilotRuntime } from "../runtime";
6
+ import { CopilotRuntime } from "../core/runtime";
7
7
  import type { AbstractAgent } from "@ag-ui/client";
8
8
 
9
9
  const createMockRuntime = (options: { beforeRequestMiddleware?: any } = {}) => {
@@ -0,0 +1,188 @@
1
+ import express from "express";
2
+ import request from "supertest";
3
+ import { describe, it, expect, vi } from "vitest";
4
+ import type { AbstractAgent } from "@ag-ui/client";
5
+ import { Observable, of } from "rxjs";
6
+
7
+ import { createCopilotEndpointExpress } from "../express";
8
+ import { createCopilotEndpointSingleRouteExpress } from "../endpoints/express-single";
9
+ import { CopilotRuntime } from "../core/runtime";
10
+
11
+ vi.mock("../handlers/handle-run", () => ({
12
+ handleRunAgent: vi
13
+ .fn()
14
+ .mockResolvedValue(new Response(null, { status: 200 })),
15
+ }));
16
+
17
+ vi.mock("../handlers/handle-connect", () => ({
18
+ handleConnectAgent: vi
19
+ .fn()
20
+ .mockResolvedValue(new Response(null, { status: 200 })),
21
+ }));
22
+
23
+ vi.mock("../handlers/handle-stop", () => ({
24
+ handleStopAgent: vi
25
+ .fn()
26
+ .mockResolvedValue(new Response(null, { status: 200 })),
27
+ }));
28
+
29
+ describe("Express adapter with hooks", () => {
30
+ const createMockRuntime = (opts?: Partial<CopilotRuntime>) => {
31
+ const createMockAgent = () => {
32
+ const agent: unknown = {
33
+ execute: async () => ({ events: [] }),
34
+ };
35
+ (agent as { clone: () => unknown }).clone = () => createMockAgent();
36
+ return agent as AbstractAgent;
37
+ };
38
+
39
+ const runner = {
40
+ run: () =>
41
+ new Observable((observer) => {
42
+ observer.next({});
43
+ observer.complete();
44
+ return () => undefined;
45
+ }),
46
+ connect: () => of({}),
47
+ stop: async () => true,
48
+ };
49
+
50
+ return new CopilotRuntime({
51
+ agents: {
52
+ default: createMockAgent(),
53
+ myAgent: createMockAgent(),
54
+ testAgent: createMockAgent(),
55
+ },
56
+ runner,
57
+ ...opts,
58
+ });
59
+ };
60
+
61
+ describe("createCopilotEndpointExpress", () => {
62
+ const createApp = (
63
+ runtimeOpts?: Partial<CopilotRuntime>,
64
+ endpointOpts?: Partial<
65
+ Omit<
66
+ Parameters<typeof createCopilotEndpointExpress>[0],
67
+ "runtime" | "basePath"
68
+ >
69
+ >,
70
+ ) => {
71
+ const runtime = createMockRuntime(runtimeOpts);
72
+ const app = express();
73
+ app.use(
74
+ createCopilotEndpointExpress({
75
+ runtime,
76
+ basePath: "/",
77
+ ...endpointOpts,
78
+ }),
79
+ );
80
+ return { app, runtime };
81
+ };
82
+
83
+ it("routes GET /info correctly", async () => {
84
+ const { app } = createApp();
85
+ const response = await request(app).get("/info");
86
+
87
+ expect(response.status).toBe(200);
88
+ expect(response.body).toHaveProperty("version");
89
+ });
90
+
91
+ it("routes POST /agent/:id/run correctly", async () => {
92
+ const { app } = createApp();
93
+ const response = await request(app)
94
+ .post("/agent/myAgent/run")
95
+ .set("Content-Type", "application/json")
96
+ .send({
97
+ messages: [],
98
+ state: {},
99
+ threadId: "thread-1",
100
+ });
101
+
102
+ expect(response.status).not.toBe(404);
103
+ });
104
+
105
+ it("forwards hooks — onRequest hook is called", async () => {
106
+ const onRequest = vi.fn();
107
+ const { app } = createApp(undefined, { hooks: { onRequest } });
108
+
109
+ await request(app).get("/info");
110
+
111
+ expect(onRequest).toHaveBeenCalledTimes(1);
112
+ expect(onRequest).toHaveBeenCalledWith(
113
+ expect.objectContaining({
114
+ request: expect.any(Request),
115
+ path: expect.stringContaining("/info"),
116
+ runtime: expect.any(CopilotRuntime),
117
+ }),
118
+ );
119
+ });
120
+
121
+ it("hooks can modify response headers via onResponse", async () => {
122
+ const { app } = createApp(undefined, {
123
+ hooks: {
124
+ onResponse: ({ response }) => {
125
+ const headers = new Headers(response.headers);
126
+ headers.set("x-custom-header", "hello-from-hook");
127
+ return new Response(response.body, {
128
+ status: response.status,
129
+ headers,
130
+ });
131
+ },
132
+ },
133
+ });
134
+
135
+ const response = await request(app).get("/info");
136
+
137
+ expect(response.status).toBe(200);
138
+ expect(response.headers["x-custom-header"]).toBe("hello-from-hook");
139
+ });
140
+
141
+ it("CORS headers are present (Access-Control-Allow-Origin: *)", async () => {
142
+ const { app } = createApp(undefined, { cors: true });
143
+ const response = await request(app)
144
+ .options("/info")
145
+ .set("Origin", "https://example.com")
146
+ .set("Access-Control-Request-Method", "GET");
147
+
148
+ expect(response.headers["access-control-allow-origin"]).toBe("*");
149
+ });
150
+ });
151
+
152
+ describe("createCopilotEndpointSingleRouteExpress", () => {
153
+ const createSingleApp = (
154
+ hooks?: Parameters<
155
+ typeof createCopilotEndpointSingleRouteExpress
156
+ >[0]["hooks"],
157
+ ) => {
158
+ const runtime = createMockRuntime();
159
+ const app = express();
160
+ app.use(
161
+ createCopilotEndpointSingleRouteExpress({
162
+ runtime,
163
+ basePath: "/rpc",
164
+ hooks,
165
+ }),
166
+ );
167
+ return { app, runtime };
168
+ };
169
+
170
+ it("dispatches single-route with method envelope", async () => {
171
+ const { app } = createSingleApp();
172
+ const response = await request(app)
173
+ .post("/rpc")
174
+ .set("Content-Type", "application/json")
175
+ .send({
176
+ method: "agent/run",
177
+ params: { agentId: "myAgent" },
178
+ body: {
179
+ messages: [],
180
+ state: {},
181
+ threadId: "thread-1",
182
+ },
183
+ });
184
+
185
+ expect(response.status).not.toBe(404);
186
+ });
187
+ });
188
+ });