@agentuity/runtime 2.0.11 → 3.0.0-alpha.1

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 (415) hide show
  1. package/dist/index.d.ts +37 -65
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +59 -61
  4. package/dist/index.js.map +1 -1
  5. package/package.json +9 -38
  6. package/src/index.ts +58 -259
  7. package/AGENTS.md +0 -116
  8. package/dist/_config.d.ts +0 -100
  9. package/dist/_config.d.ts.map +0 -1
  10. package/dist/_config.js +0 -147
  11. package/dist/_config.js.map +0 -1
  12. package/dist/_context.d.ts +0 -80
  13. package/dist/_context.d.ts.map +0 -1
  14. package/dist/_context.js +0 -160
  15. package/dist/_context.js.map +0 -1
  16. package/dist/_events.d.ts +0 -64
  17. package/dist/_events.d.ts.map +0 -1
  18. package/dist/_events.js +0 -92
  19. package/dist/_events.js.map +0 -1
  20. package/dist/_globals.d.ts +0 -58
  21. package/dist/_globals.d.ts.map +0 -1
  22. package/dist/_globals.js +0 -71
  23. package/dist/_globals.js.map +0 -1
  24. package/dist/_idle.d.ts +0 -7
  25. package/dist/_idle.d.ts.map +0 -1
  26. package/dist/_idle.js +0 -10
  27. package/dist/_idle.js.map +0 -1
  28. package/dist/_metadata.d.ts +0 -117
  29. package/dist/_metadata.d.ts.map +0 -1
  30. package/dist/_metadata.js +0 -268
  31. package/dist/_metadata.js.map +0 -1
  32. package/dist/_process-protection.d.ts +0 -27
  33. package/dist/_process-protection.d.ts.map +0 -1
  34. package/dist/_process-protection.js +0 -56
  35. package/dist/_process-protection.js.map +0 -1
  36. package/dist/_server.d.ts +0 -50
  37. package/dist/_server.d.ts.map +0 -1
  38. package/dist/_server.js +0 -89
  39. package/dist/_server.js.map +0 -1
  40. package/dist/_services.d.ts +0 -25
  41. package/dist/_services.d.ts.map +0 -1
  42. package/dist/_services.js +0 -286
  43. package/dist/_services.js.map +0 -1
  44. package/dist/_standalone.d.ts +0 -212
  45. package/dist/_standalone.d.ts.map +0 -1
  46. package/dist/_standalone.js +0 -556
  47. package/dist/_standalone.js.map +0 -1
  48. package/dist/_tokens.d.ts +0 -12
  49. package/dist/_tokens.d.ts.map +0 -1
  50. package/dist/_tokens.js +0 -97
  51. package/dist/_tokens.js.map +0 -1
  52. package/dist/_util.d.ts +0 -16
  53. package/dist/_util.d.ts.map +0 -1
  54. package/dist/_util.js +0 -54
  55. package/dist/_util.js.map +0 -1
  56. package/dist/_validation.d.ts +0 -89
  57. package/dist/_validation.d.ts.map +0 -1
  58. package/dist/_validation.js +0 -29
  59. package/dist/_validation.js.map +0 -1
  60. package/dist/_waituntil.d.ts +0 -32
  61. package/dist/_waituntil.d.ts.map +0 -1
  62. package/dist/_waituntil.js +0 -156
  63. package/dist/_waituntil.js.map +0 -1
  64. package/dist/agent.d.ts +0 -1262
  65. package/dist/agent.d.ts.map +0 -1
  66. package/dist/agent.js +0 -981
  67. package/dist/agent.js.map +0 -1
  68. package/dist/app.d.ts +0 -514
  69. package/dist/app.d.ts.map +0 -1
  70. package/dist/app.js +0 -228
  71. package/dist/app.js.map +0 -1
  72. package/dist/bootstrap.d.ts +0 -44
  73. package/dist/bootstrap.d.ts.map +0 -1
  74. package/dist/bootstrap.js +0 -259
  75. package/dist/bootstrap.js.map +0 -1
  76. package/dist/bun-s3-patch.d.ts +0 -37
  77. package/dist/bun-s3-patch.d.ts.map +0 -1
  78. package/dist/bun-s3-patch.js +0 -142
  79. package/dist/bun-s3-patch.js.map +0 -1
  80. package/dist/cors.d.ts +0 -42
  81. package/dist/cors.d.ts.map +0 -1
  82. package/dist/cors.js +0 -117
  83. package/dist/cors.js.map +0 -1
  84. package/dist/dev-patches/aisdk.d.ts +0 -17
  85. package/dist/dev-patches/aisdk.d.ts.map +0 -1
  86. package/dist/dev-patches/aisdk.js +0 -160
  87. package/dist/dev-patches/aisdk.js.map +0 -1
  88. package/dist/dev-patches/gateway.d.ts +0 -16
  89. package/dist/dev-patches/gateway.d.ts.map +0 -1
  90. package/dist/dev-patches/gateway.js +0 -54
  91. package/dist/dev-patches/gateway.js.map +0 -1
  92. package/dist/dev-patches/index.d.ts +0 -21
  93. package/dist/dev-patches/index.d.ts.map +0 -1
  94. package/dist/dev-patches/index.js +0 -33
  95. package/dist/dev-patches/index.js.map +0 -1
  96. package/dist/dev-patches/otel-llm.d.ts +0 -12
  97. package/dist/dev-patches/otel-llm.d.ts.map +0 -1
  98. package/dist/dev-patches/otel-llm.js +0 -352
  99. package/dist/dev-patches/otel-llm.js.map +0 -1
  100. package/dist/devmode.d.ts +0 -3
  101. package/dist/devmode.d.ts.map +0 -1
  102. package/dist/devmode.js +0 -167
  103. package/dist/devmode.js.map +0 -1
  104. package/dist/eval.d.ts +0 -91
  105. package/dist/eval.d.ts.map +0 -1
  106. package/dist/eval.js +0 -16
  107. package/dist/eval.js.map +0 -1
  108. package/dist/handlers/_route-meta.d.ts +0 -22
  109. package/dist/handlers/_route-meta.d.ts.map +0 -1
  110. package/dist/handlers/_route-meta.js +0 -25
  111. package/dist/handlers/_route-meta.js.map +0 -1
  112. package/dist/handlers/cron.d.ts +0 -73
  113. package/dist/handlers/cron.d.ts.map +0 -1
  114. package/dist/handlers/cron.js +0 -43
  115. package/dist/handlers/cron.js.map +0 -1
  116. package/dist/handlers/index.d.ts +0 -6
  117. package/dist/handlers/index.d.ts.map +0 -1
  118. package/dist/handlers/index.js +0 -6
  119. package/dist/handlers/index.js.map +0 -1
  120. package/dist/handlers/sse.d.ts +0 -163
  121. package/dist/handlers/sse.d.ts.map +0 -1
  122. package/dist/handlers/sse.js +0 -175
  123. package/dist/handlers/sse.js.map +0 -1
  124. package/dist/handlers/stream.d.ts +0 -52
  125. package/dist/handlers/stream.d.ts.map +0 -1
  126. package/dist/handlers/stream.js +0 -108
  127. package/dist/handlers/stream.js.map +0 -1
  128. package/dist/handlers/webrtc.d.ts +0 -49
  129. package/dist/handlers/webrtc.d.ts.map +0 -1
  130. package/dist/handlers/webrtc.js +0 -109
  131. package/dist/handlers/webrtc.js.map +0 -1
  132. package/dist/handlers/websocket.d.ts +0 -88
  133. package/dist/handlers/websocket.d.ts.map +0 -1
  134. package/dist/handlers/websocket.js +0 -161
  135. package/dist/handlers/websocket.js.map +0 -1
  136. package/dist/logger/console.d.ts +0 -70
  137. package/dist/logger/console.d.ts.map +0 -1
  138. package/dist/logger/console.js +0 -278
  139. package/dist/logger/console.js.map +0 -1
  140. package/dist/logger/index.d.ts +0 -3
  141. package/dist/logger/index.d.ts.map +0 -1
  142. package/dist/logger/index.js +0 -3
  143. package/dist/logger/index.js.map +0 -1
  144. package/dist/logger/internal.d.ts +0 -79
  145. package/dist/logger/internal.d.ts.map +0 -1
  146. package/dist/logger/internal.js +0 -133
  147. package/dist/logger/internal.js.map +0 -1
  148. package/dist/logger/logger.d.ts +0 -41
  149. package/dist/logger/logger.d.ts.map +0 -1
  150. package/dist/logger/logger.js +0 -2
  151. package/dist/logger/logger.js.map +0 -1
  152. package/dist/logger/user.d.ts +0 -8
  153. package/dist/logger/user.d.ts.map +0 -1
  154. package/dist/logger/user.js +0 -7
  155. package/dist/logger/user.js.map +0 -1
  156. package/dist/logger/util.d.ts +0 -11
  157. package/dist/logger/util.d.ts.map +0 -1
  158. package/dist/logger/util.js +0 -77
  159. package/dist/logger/util.js.map +0 -1
  160. package/dist/middleware.d.ts +0 -105
  161. package/dist/middleware.d.ts.map +0 -1
  162. package/dist/middleware.js +0 -763
  163. package/dist/middleware.js.map +0 -1
  164. package/dist/otel/config.d.ts +0 -19
  165. package/dist/otel/config.d.ts.map +0 -1
  166. package/dist/otel/config.js +0 -26
  167. package/dist/otel/config.js.map +0 -1
  168. package/dist/otel/console.d.ts +0 -33
  169. package/dist/otel/console.d.ts.map +0 -1
  170. package/dist/otel/console.js +0 -86
  171. package/dist/otel/console.js.map +0 -1
  172. package/dist/otel/exporters/index.d.ts +0 -4
  173. package/dist/otel/exporters/index.d.ts.map +0 -1
  174. package/dist/otel/exporters/index.js +0 -4
  175. package/dist/otel/exporters/index.js.map +0 -1
  176. package/dist/otel/exporters/jsonl-log-exporter.d.ts +0 -36
  177. package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +0 -1
  178. package/dist/otel/exporters/jsonl-log-exporter.js +0 -103
  179. package/dist/otel/exporters/jsonl-log-exporter.js.map +0 -1
  180. package/dist/otel/exporters/jsonl-metric-exporter.d.ts +0 -40
  181. package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +0 -1
  182. package/dist/otel/exporters/jsonl-metric-exporter.js +0 -104
  183. package/dist/otel/exporters/jsonl-metric-exporter.js.map +0 -1
  184. package/dist/otel/exporters/jsonl-trace-exporter.d.ts +0 -36
  185. package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +0 -1
  186. package/dist/otel/exporters/jsonl-trace-exporter.js +0 -111
  187. package/dist/otel/exporters/jsonl-trace-exporter.js.map +0 -1
  188. package/dist/otel/fetch.d.ts +0 -12
  189. package/dist/otel/fetch.d.ts.map +0 -1
  190. package/dist/otel/fetch.js +0 -82
  191. package/dist/otel/fetch.js.map +0 -1
  192. package/dist/otel/http.d.ts +0 -16
  193. package/dist/otel/http.d.ts.map +0 -1
  194. package/dist/otel/http.js +0 -44
  195. package/dist/otel/http.js.map +0 -1
  196. package/dist/otel/logger.d.ts +0 -37
  197. package/dist/otel/logger.d.ts.map +0 -1
  198. package/dist/otel/logger.js +0 -265
  199. package/dist/otel/logger.js.map +0 -1
  200. package/dist/otel/otel.d.ts +0 -68
  201. package/dist/otel/otel.d.ts.map +0 -1
  202. package/dist/otel/otel.js +0 -245
  203. package/dist/otel/otel.js.map +0 -1
  204. package/dist/otel/tracestate.d.ts +0 -44
  205. package/dist/otel/tracestate.d.ts.map +0 -1
  206. package/dist/otel/tracestate.js +0 -84
  207. package/dist/otel/tracestate.js.map +0 -1
  208. package/dist/router.d.ts +0 -66
  209. package/dist/router.d.ts.map +0 -1
  210. package/dist/router.js +0 -44
  211. package/dist/router.js.map +0 -1
  212. package/dist/services/evalrun/composite.d.ts +0 -21
  213. package/dist/services/evalrun/composite.d.ts.map +0 -1
  214. package/dist/services/evalrun/composite.js +0 -26
  215. package/dist/services/evalrun/composite.js.map +0 -1
  216. package/dist/services/evalrun/http.d.ts +0 -24
  217. package/dist/services/evalrun/http.d.ts.map +0 -1
  218. package/dist/services/evalrun/http.js +0 -115
  219. package/dist/services/evalrun/http.js.map +0 -1
  220. package/dist/services/evalrun/index.d.ts +0 -5
  221. package/dist/services/evalrun/index.d.ts.map +0 -1
  222. package/dist/services/evalrun/index.js +0 -5
  223. package/dist/services/evalrun/index.js.map +0 -1
  224. package/dist/services/evalrun/json.d.ts +0 -21
  225. package/dist/services/evalrun/json.d.ts.map +0 -1
  226. package/dist/services/evalrun/json.js +0 -38
  227. package/dist/services/evalrun/json.js.map +0 -1
  228. package/dist/services/evalrun/local.d.ts +0 -19
  229. package/dist/services/evalrun/local.d.ts.map +0 -1
  230. package/dist/services/evalrun/local.js +0 -22
  231. package/dist/services/evalrun/local.js.map +0 -1
  232. package/dist/services/local/_db.d.ts +0 -4
  233. package/dist/services/local/_db.d.ts.map +0 -1
  234. package/dist/services/local/_db.js +0 -281
  235. package/dist/services/local/_db.js.map +0 -1
  236. package/dist/services/local/_router.d.ts +0 -3
  237. package/dist/services/local/_router.d.ts.map +0 -1
  238. package/dist/services/local/_router.js +0 -28
  239. package/dist/services/local/_router.js.map +0 -1
  240. package/dist/services/local/_util.d.ts +0 -18
  241. package/dist/services/local/_util.d.ts.map +0 -1
  242. package/dist/services/local/_util.js +0 -44
  243. package/dist/services/local/_util.js.map +0 -1
  244. package/dist/services/local/email.d.ts +0 -24
  245. package/dist/services/local/email.d.ts.map +0 -1
  246. package/dist/services/local/email.js +0 -58
  247. package/dist/services/local/email.js.map +0 -1
  248. package/dist/services/local/index.d.ts +0 -10
  249. package/dist/services/local/index.d.ts.map +0 -1
  250. package/dist/services/local/index.js +0 -10
  251. package/dist/services/local/index.js.map +0 -1
  252. package/dist/services/local/keyvalue.d.ts +0 -17
  253. package/dist/services/local/keyvalue.d.ts.map +0 -1
  254. package/dist/services/local/keyvalue.js +0 -133
  255. package/dist/services/local/keyvalue.js.map +0 -1
  256. package/dist/services/local/queue.d.ts +0 -10
  257. package/dist/services/local/queue.d.ts.map +0 -1
  258. package/dist/services/local/queue.js +0 -96
  259. package/dist/services/local/queue.js.map +0 -1
  260. package/dist/services/local/stream.d.ts +0 -12
  261. package/dist/services/local/stream.d.ts.map +0 -1
  262. package/dist/services/local/stream.js +0 -266
  263. package/dist/services/local/stream.js.map +0 -1
  264. package/dist/services/local/task.d.ts +0 -55
  265. package/dist/services/local/task.d.ts.map +0 -1
  266. package/dist/services/local/task.js +0 -1248
  267. package/dist/services/local/task.js.map +0 -1
  268. package/dist/services/local/vector.d.ts +0 -17
  269. package/dist/services/local/vector.d.ts.map +0 -1
  270. package/dist/services/local/vector.js +0 -303
  271. package/dist/services/local/vector.js.map +0 -1
  272. package/dist/services/sandbox/http.d.ts +0 -23
  273. package/dist/services/sandbox/http.d.ts.map +0 -1
  274. package/dist/services/sandbox/http.js +0 -327
  275. package/dist/services/sandbox/http.js.map +0 -1
  276. package/dist/services/sandbox/index.d.ts +0 -2
  277. package/dist/services/sandbox/index.d.ts.map +0 -1
  278. package/dist/services/sandbox/index.js +0 -2
  279. package/dist/services/sandbox/index.js.map +0 -1
  280. package/dist/services/session/composite.d.ts +0 -21
  281. package/dist/services/session/composite.d.ts.map +0 -1
  282. package/dist/services/session/composite.js +0 -26
  283. package/dist/services/session/composite.js.map +0 -1
  284. package/dist/services/session/http.d.ts +0 -34
  285. package/dist/services/session/http.d.ts.map +0 -1
  286. package/dist/services/session/http.js +0 -124
  287. package/dist/services/session/http.js.map +0 -1
  288. package/dist/services/session/index.d.ts +0 -5
  289. package/dist/services/session/index.d.ts.map +0 -1
  290. package/dist/services/session/index.js +0 -5
  291. package/dist/services/session/index.js.map +0 -1
  292. package/dist/services/session/json.d.ts +0 -22
  293. package/dist/services/session/json.d.ts.map +0 -1
  294. package/dist/services/session/json.js +0 -35
  295. package/dist/services/session/json.js.map +0 -1
  296. package/dist/services/session/local.d.ts +0 -19
  297. package/dist/services/session/local.d.ts.map +0 -1
  298. package/dist/services/session/local.js +0 -23
  299. package/dist/services/session/local.js.map +0 -1
  300. package/dist/services/thread/local.d.ts +0 -20
  301. package/dist/services/thread/local.d.ts.map +0 -1
  302. package/dist/services/thread/local.js +0 -158
  303. package/dist/services/thread/local.js.map +0 -1
  304. package/dist/session.d.ts +0 -734
  305. package/dist/session.d.ts.map +0 -1
  306. package/dist/session.js +0 -1140
  307. package/dist/session.js.map +0 -1
  308. package/dist/signature.d.ts +0 -22
  309. package/dist/signature.d.ts.map +0 -1
  310. package/dist/signature.js +0 -63
  311. package/dist/signature.js.map +0 -1
  312. package/dist/validator.d.ts +0 -142
  313. package/dist/validator.d.ts.map +0 -1
  314. package/dist/validator.js +0 -149
  315. package/dist/validator.js.map +0 -1
  316. package/dist/version-check.d.ts +0 -20
  317. package/dist/version-check.d.ts.map +0 -1
  318. package/dist/version-check.js +0 -157
  319. package/dist/version-check.js.map +0 -1
  320. package/dist/web.d.ts +0 -8
  321. package/dist/web.d.ts.map +0 -1
  322. package/dist/web.js +0 -67
  323. package/dist/web.js.map +0 -1
  324. package/dist/webrtc-signaling.d.ts +0 -80
  325. package/dist/webrtc-signaling.d.ts.map +0 -1
  326. package/dist/webrtc-signaling.js +0 -237
  327. package/dist/webrtc-signaling.js.map +0 -1
  328. package/dist/workbench.d.ts +0 -17
  329. package/dist/workbench.d.ts.map +0 -1
  330. package/dist/workbench.js +0 -605
  331. package/dist/workbench.js.map +0 -1
  332. package/src/_config.ts +0 -163
  333. package/src/_context.ts +0 -240
  334. package/src/_events.ts +0 -142
  335. package/src/_globals.ts +0 -92
  336. package/src/_idle.ts +0 -10
  337. package/src/_metadata.ts +0 -407
  338. package/src/_process-protection.ts +0 -71
  339. package/src/_server.ts +0 -109
  340. package/src/_services.ts +0 -379
  341. package/src/_standalone.ts +0 -710
  342. package/src/_tokens.ts +0 -114
  343. package/src/_util.ts +0 -62
  344. package/src/_validation.ts +0 -119
  345. package/src/_waituntil.ts +0 -188
  346. package/src/agent.ts +0 -2739
  347. package/src/app.ts +0 -769
  348. package/src/bootstrap.ts +0 -321
  349. package/src/bun-s3-patch.ts +0 -224
  350. package/src/cors.ts +0 -137
  351. package/src/dev-patches/aisdk.ts +0 -169
  352. package/src/dev-patches/gateway.ts +0 -68
  353. package/src/dev-patches/index.ts +0 -37
  354. package/src/dev-patches/otel-llm.ts +0 -405
  355. package/src/devmode.ts +0 -171
  356. package/src/eval.ts +0 -109
  357. package/src/globals.d.ts +0 -28
  358. package/src/handlers/_route-meta.ts +0 -33
  359. package/src/handlers/cron.ts +0 -141
  360. package/src/handlers/index.ts +0 -18
  361. package/src/handlers/sse.ts +0 -358
  362. package/src/handlers/stream.ts +0 -121
  363. package/src/handlers/webrtc.ts +0 -125
  364. package/src/handlers/websocket.ts +0 -203
  365. package/src/logger/console.ts +0 -323
  366. package/src/logger/index.ts +0 -2
  367. package/src/logger/internal.ts +0 -165
  368. package/src/logger/logger.ts +0 -44
  369. package/src/logger/user.ts +0 -15
  370. package/src/logger/util.ts +0 -80
  371. package/src/middleware.ts +0 -1095
  372. package/src/otel/config.ts +0 -47
  373. package/src/otel/console.ts +0 -91
  374. package/src/otel/exporters/README.md +0 -217
  375. package/src/otel/exporters/index.ts +0 -3
  376. package/src/otel/exporters/jsonl-log-exporter.ts +0 -113
  377. package/src/otel/exporters/jsonl-metric-exporter.ts +0 -120
  378. package/src/otel/exporters/jsonl-trace-exporter.ts +0 -121
  379. package/src/otel/fetch.ts +0 -105
  380. package/src/otel/http.ts +0 -53
  381. package/src/otel/logger.ts +0 -293
  382. package/src/otel/otel.ts +0 -354
  383. package/src/otel/tracestate.ts +0 -108
  384. package/src/router.ts +0 -75
  385. package/src/services/evalrun/composite.ts +0 -34
  386. package/src/services/evalrun/http.ts +0 -167
  387. package/src/services/evalrun/index.ts +0 -4
  388. package/src/services/evalrun/json.ts +0 -46
  389. package/src/services/evalrun/local.ts +0 -28
  390. package/src/services/local/README.md +0 -1576
  391. package/src/services/local/_db.ts +0 -353
  392. package/src/services/local/_router.ts +0 -40
  393. package/src/services/local/_util.ts +0 -55
  394. package/src/services/local/email.ts +0 -91
  395. package/src/services/local/index.ts +0 -9
  396. package/src/services/local/keyvalue.ts +0 -174
  397. package/src/services/local/queue.ts +0 -145
  398. package/src/services/local/stream.ts +0 -358
  399. package/src/services/local/task.ts +0 -1711
  400. package/src/services/local/vector.ts +0 -438
  401. package/src/services/sandbox/http.ts +0 -522
  402. package/src/services/sandbox/index.ts +0 -1
  403. package/src/services/session/composite.ts +0 -33
  404. package/src/services/session/http.ts +0 -167
  405. package/src/services/session/index.ts +0 -4
  406. package/src/services/session/json.ts +0 -42
  407. package/src/services/session/local.ts +0 -33
  408. package/src/services/thread/local.ts +0 -199
  409. package/src/session.ts +0 -1960
  410. package/src/signature.ts +0 -82
  411. package/src/validator.ts +0 -283
  412. package/src/version-check.ts +0 -184
  413. package/src/web.ts +0 -76
  414. package/src/webrtc-signaling.ts +0 -288
  415. package/src/workbench.ts +0 -725
package/src/agent.ts DELETED
@@ -1,2739 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import {
3
- StructuredError,
4
- type KeyValueStorage,
5
- type StandardSchemaV1,
6
- type StreamStorage,
7
- type VectorStorage,
8
- type SandboxService,
9
- type QueueService,
10
- type EmailService,
11
- type ScheduleService,
12
- type TaskStorage,
13
- type InferInput,
14
- type InferOutput,
15
- toCamelCase,
16
- } from '@agentuity/core';
17
- import { context, SpanStatusCode, type Tracer, trace } from '@opentelemetry/api';
18
- import { enrichContextWithTraceState } from './otel/tracestate';
19
- import type { Context, MiddlewareHandler } from 'hono';
20
- import type { Handler } from 'hono/types';
21
- import { validator } from 'hono/validator';
22
- import { AGENT_RUNTIME, INTERNAL_AGENT, CURRENT_AGENT, AGENT_IDS } from './_config';
23
- import {
24
- getAgentContext,
25
- inHTTPContext,
26
- getHTTPContext,
27
- setupRequestAgentContext,
28
- getAgentAsyncLocalStorage,
29
- type RequestAgentContextArgs,
30
- } from './_context';
31
- import type { Logger } from './logger';
32
- import type { Eval, EvalHandlerResult, EvalRunResult, EvalFunction } from './eval';
33
- import { internal } from './logger/internal';
34
- import { fireEvent } from './_events';
35
- import type { Thread, Session } from './session';
36
- import { privateContext } from './_server';
37
- import { generateId } from './session';
38
- import { getEvalRunEventProvider } from './_services';
39
- import * as runtimeConfig from './_config';
40
- import type { AppState } from './index';
41
- import { validateSchema, formatValidationIssues } from './_validation';
42
- import { getAgentMetadataByName, getEvalMetadata } from './_metadata';
43
-
44
- export type AgentEventName = 'started' | 'completed' | 'errored';
45
-
46
- export type AgentEventCallback<TAgent extends Agent<any, any, any>> =
47
- | ((
48
- eventName: 'started',
49
- agent: TAgent,
50
- context: AgentContext<any, any, any>
51
- ) => Promise<void> | void)
52
- | ((
53
- eventName: 'completed',
54
- agent: TAgent,
55
- context: AgentContext<any, any, any>
56
- ) => Promise<void> | void)
57
- | ((
58
- eventName: 'errored',
59
- agent: TAgent,
60
- context: AgentContext<any, any, any>,
61
- data: Error
62
- ) => Promise<void> | void);
63
-
64
- /**
65
- * Runtime state container for agents and event listeners.
66
- * Isolates global state into context for better testing.
67
- */
68
- export interface AgentRuntimeState {
69
- agents: Map<string, Agent<any, any, any, any, any>>;
70
- agentConfigs: Map<string, unknown>;
71
- agentEventListeners: WeakMap<
72
- Agent<any, any, any, any, any>,
73
- Map<AgentEventName, Set<AgentEventCallback<any>>>
74
- >;
75
- }
76
-
77
- /**
78
- * Context object passed to every agent handler providing access to runtime services and state.
79
- *
80
- * @template TAgentRegistry - Registry of all available agents (auto-generated, strongly-typed)
81
- * @template TConfig - Agent-specific configuration type from setup function
82
- * @template TAppState - Application-wide state type from createApp
83
- *
84
- * @example
85
- * ```typescript
86
- * const agent = createAgent('my-agent', {
87
- * handler: async (ctx, input) => {
88
- * // Logging
89
- * ctx.logger.info('Processing request', { input });
90
- *
91
- * // Call another agent (import it directly)
92
- * import otherAgent from './other-agent';
93
- * const result = await otherAgent.run({ data: input });
94
- *
95
- * // Store data
96
- * await ctx.kv.set('key', { value: result });
97
- *
98
- * // Access config from setup
99
- * const cache = ctx.config.cache;
100
- *
101
- * // Background task
102
- * ctx.waitUntil(async () => {
103
- * await ctx.logger.info('Cleanup complete');
104
- * });
105
- *
106
- * return result;
107
- * }
108
- * });
109
- * ```
110
- */
111
- export interface AgentContext<
112
- _TAgentRegistry extends AgentRegistry = AgentRegistry,
113
- TConfig = unknown,
114
- TAppState = Record<string, never>,
115
- > {
116
- /**
117
- * Internal runtime state (agents, configs, event listeners).
118
- * Stored with Symbol key to prevent accidental access.
119
- * Use getAgentRuntime(ctx) to access.
120
- * @internal
121
- */
122
- [AGENT_RUNTIME]: AgentRuntimeState;
123
- /**
124
- * Schedule a background task that continues after the response is sent.
125
- * Useful for cleanup, logging, or async operations that don't block the response.
126
- *
127
- * @param promise - Promise or function that returns void or Promise<void>
128
- *
129
- * @example
130
- * ```typescript
131
- * ctx.waitUntil(async () => {
132
- * await ctx.kv.set('processed', Date.now());
133
- * ctx.logger.info('Background task complete');
134
- * });
135
- * ```
136
- */
137
- waitUntil: (promise: Promise<void> | (() => void | Promise<void>)) => void;
138
-
139
- /**
140
- * Structured logger with OpenTelemetry integration.
141
- * Logs are automatically correlated with traces.
142
- *
143
- * @example
144
- * ```typescript
145
- * ctx.logger.info('Processing started', { userId: input.id });
146
- * ctx.logger.warn('Rate limit approaching', { remaining: 10 });
147
- * ctx.logger.error('Operation failed', { error: err.message });
148
- * ```
149
- */
150
- logger: Logger;
151
-
152
- /**
153
- * Unique session identifier for this request. Consistent across agent calls in the same session.
154
- */
155
- sessionId: string;
156
-
157
- /**
158
- * OpenTelemetry tracer for creating custom spans and tracking performance.
159
- *
160
- * @example
161
- * ```typescript
162
- * const span = ctx.tracer.startSpan('database-query');
163
- * try {
164
- * const result = await database.query();
165
- * span.setStatus({ code: SpanStatusCode.OK });
166
- * return result;
167
- * } finally {
168
- * span.end();
169
- * }
170
- * ```
171
- */
172
- tracer: Tracer;
173
-
174
- /**
175
- * Key-value storage for simple data persistence.
176
- *
177
- * @example
178
- * ```typescript
179
- * await ctx.kv.set('user:123', { name: 'Alice', age: 30 });
180
- * const user = await ctx.kv.get('user:123');
181
- * await ctx.kv.delete('user:123');
182
- * const keys = await ctx.kv.list('user:*');
183
- * ```
184
- */
185
- kv: KeyValueStorage;
186
-
187
- /**
188
- * Stream storage for real-time data streams and logs.
189
- *
190
- * @example
191
- * ```typescript
192
- * const stream = await ctx.stream.create('agent-logs');
193
- * await ctx.stream.write(stream.id, 'Processing step 1');
194
- * await ctx.stream.write(stream.id, 'Processing step 2');
195
- * ```
196
- */
197
- stream: StreamStorage;
198
-
199
- /**
200
- * Vector storage for embeddings and similarity search.
201
- *
202
- * @example
203
- * ```typescript
204
- * await ctx.vector.upsert('docs', [
205
- * { id: '1', values: [0.1, 0.2, 0.3], metadata: { text: 'Hello' } }
206
- * ]);
207
- * const results = await ctx.vector.query('docs', [0.1, 0.2, 0.3], { topK: 5 });
208
- * ```
209
- */
210
- vector: VectorStorage;
211
-
212
- /**
213
- * Sandbox service for creating and running isolated code execution environments.
214
- *
215
- * @example
216
- * ```typescript
217
- * // One-shot execution
218
- * const result = await ctx.sandbox.run({
219
- * command: {
220
- * exec: ['bun', 'run', 'index.ts'],
221
- * files: [{ path: 'index.ts', content: Buffer.from('console.log("hello")') }]
222
- * }
223
- * });
224
- * console.log('Exit:', result.exitCode);
225
- *
226
- * // Interactive sandbox
227
- * const sandbox = await ctx.sandbox.create({
228
- * resources: { memory: '1Gi', cpu: '1000m' }
229
- * });
230
- * await sandbox.execute({ command: ['bun', 'init'] });
231
- * await sandbox.execute({ command: ['bun', 'add', 'zod'] });
232
- * await sandbox.destroy();
233
- * ```
234
- */
235
- sandbox: SandboxService;
236
-
237
- /**
238
- * Queue service for publishing messages to queues.
239
- *
240
- * @example
241
- * ```typescript
242
- * const result = await ctx.queue.publish('my-queue', { event: 'user.created', userId: '123' });
243
- * console.log('Message ID:', result.id);
244
- * ```
245
- */
246
- queue: QueueService;
247
-
248
- /**
249
- * Email service for managing email addresses, destinations, and sending/receiving emails.
250
- *
251
- * @example
252
- * ```typescript
253
- * // Create an email address
254
- * const address = await ctx.email.createAddress('support');
255
- *
256
- * // Send an email
257
- * const result = await ctx.email.send({
258
- * from: address.email,
259
- * to: ['user@example.com'],
260
- * subject: 'Hello!',
261
- * text: 'Welcome to our platform.',
262
- * });
263
- *
264
- * // List inbound emails
265
- * const inbound = await ctx.email.listInbound(address.id);
266
- * ```
267
- */
268
- email: EmailService;
269
-
270
- /**
271
- * Schedule service for managing cron-based scheduled tasks with
272
- * destinations and delivery tracking.
273
- *
274
- * @see https://agentuity.dev/services/schedule
275
- */
276
- schedule: ScheduleService;
277
-
278
- /**
279
- * Task service for agent-driven issue tracking.
280
- *
281
- * @example
282
- * ```typescript
283
- * const task = await ctx.task.create({
284
- * title: 'Investigate API error',
285
- * type: 'bug',
286
- * created_id: ctx.current.id,
287
- * });
288
- * await ctx.task.update(task.id, { status: 'in_progress' });
289
- * ```
290
- */
291
- task: TaskStorage;
292
-
293
- /**
294
- * In-memory state storage scoped to the current request.
295
- * Use for passing data between middleware and handlers.
296
- *
297
- * @example
298
- * ```typescript
299
- * ctx.state.set('startTime', Date.now());
300
- * const duration = Date.now() - (ctx.state.get('startTime') as number);
301
- * ```
302
- */
303
- state: Map<string, unknown>;
304
-
305
- /**
306
- * Thread information for multi-turn conversations.
307
- */
308
- thread: Thread;
309
-
310
- /**
311
- * Session information for the current request.
312
- */
313
- session: Session;
314
-
315
- /**
316
- * Agent-specific configuration returned from the setup function.
317
- * Type is inferred from your setup function's return value.
318
- *
319
- * @example
320
- * ```typescript
321
- * createAgent({
322
- * setup: async () => ({ cache: new Map(), db: await connectDB() }),
323
- * handler: async (ctx, input) => {
324
- * ctx.config.cache.set('key', 'value'); // Strongly typed!
325
- * await ctx.config.db.query('SELECT * FROM users');
326
- * }
327
- * });
328
- * ```
329
- */
330
- config: TConfig;
331
-
332
- /**
333
- * Application-wide state returned from createApp setup function.
334
- * Shared across all agents in the application.
335
- *
336
- * @example
337
- * ```typescript
338
- * const app = createApp({
339
- * setup: async () => ({ db: await connectDB(), redis: await connectRedis() })
340
- * });
341
- *
342
- * // Later in any agent:
343
- * handler: async (ctx, input) => {
344
- * await ctx.app.db.query('SELECT 1');
345
- * await ctx.app.redis.set('key', 'value');
346
- * }
347
- * ```
348
- */
349
- app: TAppState;
350
-
351
- /**
352
- * Metadata about the currently executing agent.
353
- * Provides access to the agent's id, name, and other properties.
354
- *
355
- * @example
356
- * ```typescript
357
- * handler: async (ctx, input) => {
358
- * // Use agent ID for namespaced state keys
359
- * const stateKey = `${ctx.current.id}_counter`;
360
- * await ctx.thread.state.set(stateKey, value);
361
- *
362
- * // Log agent info
363
- * ctx.logger.info('Running agent', { name: ctx.current.name });
364
- * }
365
- * ```
366
- */
367
- current: AgentMetadata;
368
-
369
- /**
370
- * Authentication context when request is authenticated.
371
- * Available when auth middleware is configured on the Hono app.
372
- *
373
- * Will be `null` for:
374
- * - Unauthenticated requests
375
- * - Cron jobs
376
- * - Agent-to-agent calls without auth propagation
377
- *
378
- * @example
379
- * ```typescript
380
- * handler: async (ctx, input) => {
381
- * if (!ctx.auth) {
382
- * return { error: 'Please sign in' };
383
- * }
384
- *
385
- * // Access user info
386
- * const user = await ctx.auth.getUser();
387
- * ctx.logger.info(`Request from ${user.email}`);
388
- *
389
- * // Check organization role
390
- * if (await ctx.auth.hasOrgRole('admin')) {
391
- * // Admin-only logic
392
- * }
393
- *
394
- * return { userId: user.id };
395
- * }
396
- * ```
397
- */
398
- auth: import('@agentuity/core').AuthInterface | null;
399
- }
400
-
401
- type InternalAgentMetadata = {
402
- /**
403
- * the unique name for the agent (user-provided).
404
- */
405
- name: string;
406
- /**
407
- * the unique identifier for this project, agent and deployment.
408
- */
409
- id: string;
410
- /**
411
- * the unique identifier for this agent across multiple deployments
412
- */
413
- agentId: string;
414
- /**
415
- * the relative path to the agent from the root project directory.
416
- */
417
- filename: string;
418
- /**
419
- * a unique version for the agent. computed as the SHA256 contents of the file.
420
- */
421
- version: string;
422
-
423
- /**
424
- * the source code for the input schema.
425
- */
426
- inputSchemaCode?: string;
427
-
428
- /**
429
- * the source code for the output schema.
430
- */
431
- outputSchemaCode?: string;
432
- };
433
-
434
- type ExternalAgentMetadata = {
435
- /**
436
- * the human readable description for the agent
437
- */
438
- description?: string;
439
- };
440
-
441
- export type AgentMetadata = InternalAgentMetadata & ExternalAgentMetadata;
442
-
443
- /**
444
- * Configuration object for creating an agent evaluation function.
445
- *
446
- * @template TInput - Input schema type from the agent
447
- * @template TOutput - Output schema type from the agent
448
- */
449
- export interface CreateEvalConfig<
450
- TInput extends StandardSchemaV1 | undefined = any,
451
- TOutput extends StandardSchemaV1 | undefined = any,
452
- > {
453
- /**
454
- * Optional description of what this evaluation does.
455
- *
456
- * @example
457
- * ```typescript
458
- * description: 'Ensures output is greater than zero'
459
- * ```
460
- */
461
- description?: string;
462
-
463
- /**
464
- * Evaluation handler function that tests the agent's behavior.
465
- * Return true if the evaluation passes, false if it fails.
466
- *
467
- * @param run - Evaluation run context containing input and metadata
468
- * @param result - The output from the agent handler
469
- * @returns Boolean indicating pass/fail, or evaluation result object
470
- *
471
- * @example
472
- * ```typescript
473
- * handler: async (run, result) => {
474
- * // Assert that output is positive
475
- * if (result <= 0) {
476
- * return false; // Evaluation failed
477
- * }
478
- * return true; // Evaluation passed
479
- * }
480
- * ```
481
- *
482
- * @example
483
- * ```typescript
484
- * // With detailed result
485
- * handler: async (run, result) => {
486
- * const passed = result.length > 5;
487
- * return {
488
- * passed,
489
- * score: passed ? 1.0 : 0.0,
490
- * message: passed ? 'Output length is valid' : 'Output too short'
491
- * };
492
- * }
493
- * ```
494
- */
495
- handler: EvalFunction<
496
- TInput extends StandardSchemaV1 ? InferOutput<TInput> : undefined,
497
- TOutput extends StandardSchemaV1 ? InferOutput<TOutput> : undefined
498
- >;
499
- }
500
-
501
- export type PresetEvalConfig<
502
- TInput extends StandardSchemaV1 | undefined = any,
503
- TOutput extends StandardSchemaV1 | undefined = any,
504
- > = CreateEvalConfig<TInput, TOutput> & { name: string };
505
-
506
- type CreateEvalMethod<
507
- TInput extends StandardSchemaV1 | undefined = any,
508
- TOutput extends StandardSchemaV1 | undefined = any,
509
- > = {
510
- (config: PresetEvalConfig<TInput, TOutput>): Eval<TInput, TOutput>;
511
- (name: string, config: CreateEvalConfig<TInput, TOutput>): Eval<TInput, TOutput>;
512
- };
513
-
514
- /**
515
- * Validator function type with method overloads for different validation scenarios.
516
- * Provides type-safe validation middleware that integrates with Hono's type system.
517
- *
518
- * This validator automatically validates incoming JSON request bodies using StandardSchema-compatible
519
- * schemas (Zod, Valibot, ArkType, etc.) and provides full TypeScript type inference for validated data
520
- * accessible via `c.req.valid('json')`.
521
- *
522
- * The validator returns 400 Bad Request with validation error details if validation fails.
523
- *
524
- * @template TInput - Agent's input schema type (StandardSchemaV1 or undefined)
525
- * @template _TOutput - Agent's output schema type (reserved for future output validation)
526
- *
527
- * @example Basic usage with agent's schema
528
- * ```typescript
529
- * router.post('/', agent.validator(), async (c) => {
530
- * const data = c.req.valid('json'); // Fully typed from agent's input schema
531
- * return c.json(data);
532
- * });
533
- * ```
534
- *
535
- * @example Override with custom input schema
536
- * ```typescript
537
- * router.post('/custom', agent.validator({ input: z.object({ id: z.string() }) }), async (c) => {
538
- * const data = c.req.valid('json'); // Typed as { id: string }
539
- * return c.json(data);
540
- * });
541
- * ```
542
- */
543
- export interface AgentValidator<
544
- TInput extends StandardSchemaV1 | undefined,
545
- _TOutput extends StandardSchemaV1 | undefined,
546
- > {
547
- /**
548
- * Validates using the agent's input schema (no override).
549
- * Returns Hono middleware handler that validates JSON request body.
550
- *
551
- * @returns Middleware handler with type inference for validated data
552
- *
553
- * @example
554
- * ```typescript
555
- * // Agent has schema: { input: z.object({ name: z.string() }) }
556
- * router.post('/', agent.validator(), async (c) => {
557
- * const data = c.req.valid('json'); // { name: string }
558
- * return c.json({ received: data.name });
559
- * });
560
- * ```
561
- */
562
- (): TInput extends StandardSchemaV1
563
- ? Handler<
564
- any,
565
- any,
566
- {
567
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
568
- in: {};
569
- out: { json: InferInput<TInput> };
570
- }
571
- >
572
- : Handler<any, any, any>;
573
-
574
- /**
575
- * Output-only validation override.
576
- * Validates only the response body (no input validation).
577
- *
578
- * Useful for GET routes or routes where input validation is handled elsewhere.
579
- * The middleware validates the JSON response body and throws 500 Internal Server Error
580
- * if validation fails.
581
- *
582
- * @template TOverrideOutput - Custom output schema type
583
- * @param override - Object containing output schema
584
- * @returns Middleware handler that validates response output
585
- *
586
- * @example GET route with output validation
587
- * ```typescript
588
- * router.get('/', agent.validator({ output: z.array(z.object({ id: z.string() })) }), async (c) => {
589
- * // Returns array of objects - validated against schema
590
- * return c.json([{ id: '123' }, { id: '456' }]);
591
- * });
592
- * ```
593
- */
594
- <TOverrideOutput extends StandardSchemaV1>(override: {
595
- output: TOverrideOutput;
596
- }): Handler<
597
- any,
598
- any,
599
- {
600
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
601
- in: {};
602
- out: { json: InferOutput<TOverrideOutput> };
603
- }
604
- >;
605
-
606
- /**
607
- * Validates with custom input and optional output schemas (POST/PUT/PATCH/DELETE).
608
- * Overrides the agent's schema for this specific route.
609
- *
610
- * @template TOverrideInput - Custom input schema type
611
- * @template TOverrideOutput - Optional custom output schema type
612
- * @param override - Object containing input (required) and output (optional) schemas
613
- * @returns Middleware handler with type inference from custom schemas
614
- *
615
- * @example Custom input schema
616
- * ```typescript
617
- * router.post('/users', agent.validator({
618
- * input: z.object({ email: z.string().email(), name: z.string() })
619
- * }), async (c) => {
620
- * const data = c.req.valid('json'); // { email: string, name: string }
621
- * return c.json({ id: '123', ...data });
622
- * });
623
- * ```
624
- *
625
- * @example Custom input and output schemas
626
- * ```typescript
627
- * router.post('/convert', agent.validator({
628
- * input: z.string(),
629
- * output: z.number()
630
- * }), async (c) => {
631
- * const data = c.req.valid('json'); // string
632
- * return c.json(123);
633
- * });
634
- * ```
635
- */
636
- <
637
- TOverrideInput extends StandardSchemaV1,
638
- TOverrideOutput extends StandardSchemaV1 | undefined = undefined,
639
- >(override: {
640
- input: TOverrideInput;
641
- output?: TOverrideOutput;
642
- }): Handler<
643
- any,
644
- any,
645
- {
646
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
647
- in: {};
648
- out: {
649
- json: InferInput<TOverrideInput>;
650
- };
651
- }
652
- >;
653
- }
654
-
655
- /**
656
- * Agent instance type returned by createAgent().
657
- * Represents a fully configured agent with metadata, handler, lifecycle hooks, and event listeners.
658
- *
659
- * @template TInput - Input schema type (StandardSchemaV1 or undefined)
660
- * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
661
- * @template TStream - Whether the agent returns a stream (true/false)
662
- * @template TConfig - Agent-specific configuration type from setup function
663
- * @template TAppState - Application state type from createApp
664
- *
665
- * @example
666
- * ```typescript
667
- * const agent = createAgent({
668
- * metadata: { name: 'My Agent' },
669
- * schema: { input: z.string(), output: z.number() },
670
- * handler: async (ctx, input) => input.length
671
- * });
672
- *
673
- * // Access agent properties
674
- * console.log(agent.metadata.name); // "My Agent"
675
- *
676
- * // Add event listeners
677
- * agent.addEventListener('started', (eventName, agent, ctx) => {
678
- * console.log('Agent started:', ctx.sessionId);
679
- * });
680
- *
681
- * // Create evals for testing
682
- * const eval1 = agent.createEval('check-positive', {
683
- * description: 'Ensures result is greater than 5',
684
- * handler: async (run, result) => {
685
- * return result > 5; // Assert output is greater than 5
686
- * }
687
- * });
688
- * ```
689
- */
690
- export type Agent<
691
- TInput extends StandardSchemaV1 | undefined = any,
692
- TOutput extends StandardSchemaV1 | undefined = any,
693
- TStream extends boolean = false,
694
- TConfig = unknown,
695
- TAppState = Record<string, never>,
696
- > = {
697
- /**
698
- * Agent metadata including name, description, id, version, and filename.
699
- */
700
- metadata: AgentMetadata;
701
-
702
- /**
703
- * The main handler function that processes agent requests.
704
- * Gets AgentContext from AsyncLocalStorage, receives validated input, returns output or stream.
705
- */
706
- handler: (...args: any[]) => any | Promise<any>;
707
-
708
- /**
709
- * Creates a type-safe validation middleware for routes using StandardSchema validation.
710
- *
711
- * This method validates incoming JSON request bodies against the agent's **input schema**
712
- * and optionally validates outgoing JSON responses against the **output schema**.
713
- * Provides full TypeScript type inference for validated input data accessible via `c.req.valid('json')`.
714
- *
715
- * **Validation behavior:**
716
- * - **Input**: Validates request JSON body, returns 400 Bad Request on failure
717
- * - **Output**: Validates response JSON body (if output schema provided), throws 500 on failure
718
- * - Passes validated input data to handler via `c.req.valid('json')`
719
- * - Full TypeScript type inference for validated input data
720
- *
721
- * **Supported schema libraries:**
722
- * - Zod (z.object, z.string, etc.)
723
- * - Valibot (v.object, v.string, etc.)
724
- * - ArkType (type({ ... }))
725
- * - Any StandardSchema-compatible library
726
- *
727
- * **Method overloads:**
728
- * - `agent.validator()` - Validates using agent's input/output schemas
729
- * - `agent.validator({ output: schema })` - Output-only validation (no input validation)
730
- * - `agent.validator({ input: schema })` - Custom input schema override (skips agent's output validation)
731
- * - `agent.validator({ input: schema, output: schema })` - Both input and output validated
732
- *
733
- * @returns Hono middleware handler with proper type inference
734
- *
735
- * @example Automatic validation using agent's schema
736
- * ```typescript
737
- * // Agent defined with: schema: { input: z.object({ name: z.string(), age: z.number() }) }
738
- * router.post('/', agent.validator(), async (c) => {
739
- * const data = c.req.valid('json'); // Fully typed: { name: string, age: number }
740
- * return c.json({ greeting: `Hello ${data.name}, age ${data.age}` });
741
- * });
742
- * ```
743
- *
744
- * @example Override with custom schema per-route
745
- * ```typescript
746
- * router.post('/email', agent.validator({
747
- * input: z.object({ email: z.string().email() })
748
- * }), async (c) => {
749
- * const data = c.req.valid('json'); // Typed as { email: string }
750
- * return c.json({ sent: data.email });
751
- * });
752
- * ```
753
- *
754
- * @example Works with any StandardSchema library
755
- * ```typescript
756
- * import * as v from 'valibot';
757
- *
758
- * router.post('/valibot', agent.validator({
759
- * input: v.object({ count: v.number() })
760
- * }), async (c) => {
761
- * const data = c.req.valid('json'); // Typed correctly
762
- * return c.json({ count: data.count });
763
- * });
764
- * ```
765
- *
766
- * @example Validation error response (400)
767
- * ```typescript
768
- * // Request: { "name": "Bob" } (missing 'age')
769
- * // Response: {
770
- * // "error": "Validation failed",
771
- * // "message": "age: Invalid input: expected number, received undefined",
772
- * // "issues": [{ "message": "...", "path": ["age"] }]
773
- * // }
774
- * ```
775
- */
776
- validator: AgentValidator<TInput, TOutput>;
777
-
778
- /**
779
- * Array of evaluation functions created via agent.createEval().
780
- * Used for testing and validating agent behavior.
781
- */
782
- evals?: Eval[];
783
-
784
- /**
785
- * Create an evaluation function for testing this agent.
786
- * Evals can assert correctness of agent input/output during test runs.
787
- *
788
- * @param config - Eval configuration
789
- * @param config.metadata - Optional eval metadata (name, description)
790
- * @param config.handler - Eval handler function receiving run context and result
791
- *
792
- * @example
793
- * ```typescript
794
- * const agent = createAgent({
795
- * schema: { input: z.string(), output: z.number() },
796
- * handler: async (ctx, input) => input.length
797
- * });
798
- *
799
- * // Create eval to validate output
800
- * agent.createEval('check-positive', {
801
- * description: 'Ensures output is a positive number',
802
- * handler: async (run, result) => {
803
- * return result > 0; // Assert output is positive
804
- * }
805
- * });
806
- * ```
807
- */
808
- createEval: CreateEvalMethod<TInput, TOutput>;
809
-
810
- /**
811
- * Optional setup function called once when app starts.
812
- * Returns agent-specific configuration available via ctx.config.
813
- */
814
- setup?: (app: TAppState) => Promise<TConfig> | TConfig;
815
-
816
- /**
817
- * Optional shutdown function called when app stops.
818
- * Receives app state and agent config for cleanup.
819
- */
820
- shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
821
-
822
- /**
823
- * Register an event listener for when the agent starts execution.
824
- *
825
- * @param eventName - Must be 'started'
826
- * @param callback - Function called when agent execution begins
827
- *
828
- * @example
829
- * ```typescript
830
- * agent.addEventListener('started', (eventName, agent, ctx) => {
831
- * console.log(`${agent.metadata.name} started at ${new Date()}`);
832
- * });
833
- * ```
834
- */
835
- addEventListener(
836
- eventName: 'started',
837
- callback: (
838
- eventName: 'started',
839
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
840
- context: AgentContext<any, TConfig, TAppState>
841
- ) => Promise<void> | void
842
- ): void;
843
-
844
- /**
845
- * Register an event listener for when the agent completes successfully.
846
- *
847
- * @param eventName - Must be 'completed'
848
- * @param callback - Function called when agent execution completes
849
- *
850
- * @example
851
- * ```typescript
852
- * agent.addEventListener('completed', (eventName, agent, ctx) => {
853
- * console.log(`${agent.metadata.name} completed successfully`);
854
- * });
855
- * ```
856
- */
857
- addEventListener(
858
- eventName: 'completed',
859
- callback: (
860
- eventName: 'completed',
861
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
862
- context: AgentContext<any, TConfig, TAppState>
863
- ) => Promise<void> | void
864
- ): void;
865
-
866
- /**
867
- * Register an event listener for when the agent throws an error.
868
- *
869
- * @param eventName - Must be 'errored'
870
- * @param callback - Function called when agent execution fails
871
- *
872
- * @example
873
- * ```typescript
874
- * agent.addEventListener('errored', (eventName, agent, ctx, error) => {
875
- * console.error(`${agent.metadata.name} failed:`, error.message);
876
- * });
877
- * ```
878
- */
879
- addEventListener(
880
- eventName: 'errored',
881
- callback: (
882
- eventName: 'errored',
883
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
884
- context: AgentContext<any, TConfig, TAppState>,
885
- data: Error
886
- ) => Promise<void> | void
887
- ): void;
888
-
889
- /**
890
- * Remove a previously registered 'started' event listener.
891
- *
892
- * @param eventName - Must be 'started'
893
- * @param callback - The callback function to remove
894
- */
895
- removeEventListener(
896
- eventName: 'started',
897
- callback: (
898
- eventName: 'started',
899
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
900
- context: AgentContext<any, TConfig, TAppState>
901
- ) => Promise<void> | void
902
- ): void;
903
-
904
- /**
905
- * Remove a previously registered 'completed' event listener.
906
- *
907
- * @param eventName - Must be 'completed'
908
- * @param callback - The callback function to remove
909
- */
910
- removeEventListener(
911
- eventName: 'completed',
912
- callback: (
913
- eventName: 'completed',
914
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
915
- context: AgentContext<any, TConfig, TAppState>
916
- ) => Promise<void> | void
917
- ): void;
918
-
919
- /**
920
- * Remove a previously registered 'errored' event listener.
921
- *
922
- * @param eventName - Must be 'errored'
923
- * @param callback - The callback function to remove
924
- */
925
- removeEventListener(
926
- eventName: 'errored',
927
- callback: (
928
- eventName: 'errored',
929
- agent: Agent<TInput, TOutput, TStream, TConfig, TAppState>,
930
- context: AgentContext<any, TConfig, TAppState>,
931
- data: Error
932
- ) => Promise<void> | void
933
- ): void;
934
- } & (TInput extends StandardSchemaV1 ? { inputSchema: TInput } : { inputSchema?: never }) &
935
- (TOutput extends StandardSchemaV1 ? { outputSchema: TOutput } : { outputSchema?: never }) &
936
- (TStream extends true ? { stream: true } : { stream?: false });
937
-
938
- type InferSchemaInput<T> = T extends StandardSchemaV1 ? InferOutput<T> : never;
939
-
940
- type InferStreamOutput<TOutput, TStream extends boolean> = TStream extends true
941
- ? TOutput extends StandardSchemaV1
942
- ? ReadableStream<InferOutput<TOutput>>
943
- : ReadableStream<unknown>
944
- : TOutput extends StandardSchemaV1
945
- ? InferOutput<TOutput>
946
- : void;
947
-
948
- type SchemaInput<TSchema> = TSchema extends { input: infer I } ? I : undefined;
949
- type SchemaOutput<TSchema> = TSchema extends { output: infer O } ? O : undefined;
950
- type SchemaStream<TSchema> = TSchema extends { stream: infer S }
951
- ? S extends boolean
952
- ? S
953
- : false
954
- : false;
955
-
956
- type SchemaHandlerReturn<TSchema> =
957
- SchemaStream<TSchema> extends true
958
- ? SchemaOutput<TSchema> extends StandardSchemaV1
959
- ? ReadableStream<InferOutput<SchemaOutput<TSchema>>>
960
- : ReadableStream<unknown>
961
- : SchemaOutput<TSchema> extends StandardSchemaV1
962
- ? InferOutput<SchemaOutput<TSchema>>
963
- : void;
964
-
965
- // Handler signature based on schema + setup result (no self-reference)
966
- type AgentHandlerFromConfig<TSchema, TSetupReturn, TAppState = AppState> =
967
- SchemaInput<TSchema> extends infer I
968
- ? I extends StandardSchemaV1
969
- ? (
970
- ctx: AgentContext<any, TSetupReturn, TAppState>,
971
- input: InferOutput<I>
972
- ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
973
- : (
974
- ctx: AgentContext<any, TSetupReturn, TAppState>
975
- ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>
976
- : (
977
- ctx: AgentContext<any, TSetupReturn, TAppState>
978
- ) => Promise<SchemaHandlerReturn<TSchema>> | SchemaHandlerReturn<TSchema>;
979
-
980
- /**
981
- * Configuration object for creating an agent with automatic type inference.
982
- *
983
- * Passed as the second parameter to createAgent(name, config).
984
- *
985
- * @template TSchema - Schema definition object containing optional input, output, and stream properties
986
- * @template TConfig - Function type that returns agent-specific configuration from setup
987
- *
988
- * @example
989
- * ```typescript
990
- * const agent = createAgent('greeting', {
991
- * description: 'Generates personalized greetings',
992
- * schema: {
993
- * input: z.object({ name: z.string(), age: z.number() }),
994
- * output: z.string()
995
- * },
996
- * handler: async (ctx, { name, age }) => {
997
- * return `Hello, ${name}! You are ${age} years old.`;
998
- * }
999
- * });
1000
- * ```
1001
- */
1002
- export interface CreateAgentConfig<
1003
- TSchema extends
1004
- | {
1005
- input?: StandardSchemaV1;
1006
- output?: StandardSchemaV1;
1007
- stream?: boolean;
1008
- }
1009
- | undefined = undefined,
1010
- TConfig extends (app: AppState) => any = any,
1011
- > {
1012
- /**
1013
- * Optional schema validation using Zod or any StandardSchemaV1 compatible library.
1014
- *
1015
- * @example
1016
- * ```typescript
1017
- * schema: {
1018
- * input: z.object({ name: z.string(), age: z.number() }),
1019
- * output: z.string(),
1020
- * stream: false
1021
- * }
1022
- * ```
1023
- */
1024
- schema?: TSchema;
1025
-
1026
- /**
1027
- * Optional description of what this agent does, visible in the Agentuity platform.
1028
- *
1029
- * @example
1030
- * ```typescript
1031
- * description: 'Returns personalized greetings'
1032
- * ```
1033
- */
1034
- description?: string;
1035
-
1036
- /**
1037
- * Optional metadata object (typically injected by build plugin during compilation).
1038
- * Contains agent identification and versioning information.
1039
- *
1040
- * @internal - Usually populated by build tooling, not manually set
1041
- */
1042
- metadata?: Partial<AgentMetadata>;
1043
-
1044
- /**
1045
- * Optional async function called once on app startup to initialize agent-specific resources.
1046
- * The returned value is available in the handler via `ctx.config`.
1047
- *
1048
- * @param app - Application state from createApp setup function
1049
- * @returns Agent-specific configuration object
1050
- *
1051
- * @example
1052
- * ```typescript
1053
- * setup: async (app) => {
1054
- * const cache = new Map();
1055
- * const db = await connectDB();
1056
- * return { cache, db };
1057
- * }
1058
- * ```
1059
- */
1060
- setup?: TConfig;
1061
-
1062
- /**
1063
- * The main agent logic that processes requests.
1064
- * Receives AgentContext and validated input (if schema.input is defined), returns output or stream.
1065
- *
1066
- * @param ctx - Agent context with logger, storage, and other runtime services
1067
- * @param input - Validated input (only present if schema.input is defined)
1068
- * @returns Output matching schema.output type, or ReadableStream if schema.stream is true
1069
- *
1070
- * @example
1071
- * ```typescript
1072
- * handler: async (ctx, { name, age }) => {
1073
- * ctx.logger.info(`Processing for ${name}`);
1074
- * await ctx.kv.set('lastUser', name);
1075
- * return `Hello, ${name}! You are ${age} years old.`;
1076
- * }
1077
- * ```
1078
- */
1079
- handler: AgentHandlerFromConfig<
1080
- TSchema,
1081
- TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined,
1082
- AppState
1083
- >;
1084
-
1085
- /**
1086
- * Optional async cleanup function called on app shutdown.
1087
- * Use this to close connections, flush buffers, etc.
1088
- *
1089
- * @param app - Application state from createApp
1090
- * @param config - Agent config returned from setup function
1091
- *
1092
- * @example
1093
- * ```typescript
1094
- * shutdown: async (app, config) => {
1095
- * await config.db.close();
1096
- * config.cache.clear();
1097
- * }
1098
- * ```
1099
- */
1100
- shutdown?: (
1101
- app: AppState,
1102
- config: TConfig extends (app: AppState) => infer R ? Awaited<R> : undefined
1103
- ) => Promise<void> | void;
1104
- }
1105
-
1106
- /**
1107
- * The public interface returned by createAgent().
1108
- * Provides methods to run the agent, create evaluations, and manage event listeners.
1109
- *
1110
- * @template TInput - Input schema type (StandardSchemaV1 or undefined if no input)
1111
- * @template TOutput - Output schema type (StandardSchemaV1 or undefined if no output)
1112
- * @template TStream - Whether the agent returns a stream (true/false)
1113
- *
1114
- * @example
1115
- * ```typescript
1116
- * const agent = createAgent('greeting', {
1117
- * schema: {
1118
- * input: z.object({ name: z.string() }),
1119
- * output: z.string()
1120
- * },
1121
- * handler: async (ctx, { name }) => `Hello, ${name}!`
1122
- * });
1123
- *
1124
- * // Run the agent
1125
- * const result = await agent.run({ name: 'Alice' });
1126
- *
1127
- * // Create evaluation
1128
- * const evalDef = agent.createEval('greeting-accuracy', {
1129
- * description: 'Checks if greeting includes the user name',
1130
- * handler: async (ctx, input, output) => {
1131
- * return { score: output.includes(input.name) ? 1 : 0 };
1132
- * }
1133
- * });
1134
- *
1135
- * // Listen to events
1136
- * agent.addEventListener('completed', async (eventName, agent, context) => {
1137
- * console.log('Agent completed successfully');
1138
- * });
1139
- * ```
1140
- */
1141
- export interface AgentRunner<
1142
- TInput extends StandardSchemaV1 | undefined = any,
1143
- TOutput extends StandardSchemaV1 | undefined = any,
1144
- TStream extends boolean = false,
1145
- > {
1146
- /** Agent metadata (id, name, description, etc.) */
1147
- metadata: AgentMetadata;
1148
-
1149
- /**
1150
- * Execute the agent with validated input.
1151
- * If agent has no input schema, call with no arguments.
1152
- * If agent has input schema, pass validated input object.
1153
- *
1154
- * @example
1155
- * ```typescript
1156
- * // Agent with input
1157
- * const result = await agent.run({ name: 'Alice' });
1158
- *
1159
- * // Agent without input
1160
- * const result = await agent.run();
1161
- * ```
1162
- */
1163
- run: undefined extends TInput
1164
- ? () => Promise<InferStreamOutput<Exclude<TOutput, undefined>, TStream>>
1165
- : (
1166
- input: InferSchemaInput<Exclude<TInput, undefined>>
1167
- ) => Promise<InferStreamOutput<Exclude<TOutput, undefined>, TStream>>;
1168
-
1169
- /**
1170
- * Create Hono validator middleware for this agent.
1171
- * Automatically validates request input against the agent's schema.
1172
- *
1173
- * @example
1174
- * ```typescript
1175
- * import myAgent from './my-agent';
1176
- * router.post('/', myAgent.validator(), async (c) => {
1177
- * const data = c.req.valid('json'); // Fully typed!
1178
- * return c.json(await myAgent.run(data));
1179
- * });
1180
- * ```
1181
- */
1182
- validator: AgentValidator<TInput, TOutput>;
1183
-
1184
- /** Input schema (if defined) */
1185
- inputSchema?: TInput;
1186
-
1187
- /** Output schema (if defined) */
1188
- outputSchema?: TOutput;
1189
-
1190
- /** Whether agent returns a stream */
1191
- stream?: TStream;
1192
-
1193
- /**
1194
- * Create an evaluation for this agent.
1195
- * Evaluations run automatically after the agent completes.
1196
- *
1197
- * @example
1198
- * ```typescript
1199
- * const accuracyEval = agent.createEval('accuracy', {
1200
- * description: 'Validates output length is non-zero',
1201
- * handler: async (ctx, input, output) => ({
1202
- * score: output.length > 0 ? 1 : 0,
1203
- * metadata: { outputLength: output.length }
1204
- * })
1205
- * });
1206
- * ```
1207
- */
1208
- createEval: CreateEvalMethod<TInput, TOutput>;
1209
-
1210
- /**
1211
- * Add event listener for 'started' or 'completed' events.
1212
- * Listeners fire sequentially in the order they were added.
1213
- *
1214
- * @param eventName - 'started' or 'completed'
1215
- * @param callback - Function to call when event fires
1216
- *
1217
- * @example
1218
- * ```typescript
1219
- * agent.addEventListener('started', async (eventName, agent, context) => {
1220
- * context.logger.info('Agent execution started');
1221
- * });
1222
- * ```
1223
- */
1224
- addEventListener(
1225
- eventName: 'started' | 'completed',
1226
- callback: (
1227
- eventName: 'started' | 'completed',
1228
- agent: Agent<TInput, TOutput, TStream, any, any>,
1229
- context: AgentContext<any, any, any>
1230
- ) => Promise<void> | void
1231
- ): void;
1232
-
1233
- /**
1234
- * Add event listener for 'errored' event.
1235
- * Fires when agent handler throws an error.
1236
- *
1237
- * @param eventName - 'errored'
1238
- * @param callback - Function to call when error occurs
1239
- *
1240
- * @example
1241
- * ```typescript
1242
- * agent.addEventListener('errored', async (eventName, agent, context, error) => {
1243
- * context.logger.error('Agent failed', { error: error.message });
1244
- * });
1245
- * ```
1246
- */
1247
- addEventListener(
1248
- eventName: 'errored',
1249
- callback: (
1250
- eventName: 'errored',
1251
- agent: Agent<TInput, TOutput, TStream, any, any>,
1252
- context: AgentContext<any, any, any>,
1253
- error: Error
1254
- ) => Promise<void> | void
1255
- ): void;
1256
-
1257
- /**
1258
- * Remove event listener for 'started' or 'completed' events.
1259
- *
1260
- * @param eventName - 'started' or 'completed'
1261
- * @param callback - The same callback function that was added
1262
- */
1263
- removeEventListener(
1264
- eventName: 'started' | 'completed',
1265
- callback: (
1266
- eventName: 'started' | 'completed',
1267
- agent: Agent<TInput, TOutput, TStream, any, any>,
1268
- context: AgentContext<any, any, any>
1269
- ) => Promise<void> | void
1270
- ): void;
1271
-
1272
- /**
1273
- * Remove event listener for 'errored' event.
1274
- *
1275
- * @param eventName - 'errored'
1276
- * @param callback - The same callback function that was added
1277
- */
1278
- removeEventListener(
1279
- eventName: 'errored',
1280
- callback: (
1281
- eventName: 'errored',
1282
- agent: Agent<TInput, TOutput, TStream, any, any>,
1283
- context: AgentContext<any, any, any>,
1284
- error: Error
1285
- ) => Promise<void> | void
1286
- ): void;
1287
- }
1288
-
1289
- // Will be populated at runtime with strongly typed agents
1290
- const agents = new Map<string, Agent<any, any, any, any, any>>();
1291
-
1292
- // WeakMap to store event listeners for each agent instance (truly private)
1293
- const agentEventListeners = new WeakMap<
1294
- Agent<any, any, any, any, any>,
1295
- Map<AgentEventName, Set<AgentEventCallback<any>>>
1296
- >();
1297
-
1298
- // Map to store agent configs returned from setup (keyed by agent name)
1299
- const agentConfigs = new Map<string, unknown>();
1300
-
1301
- /**
1302
- * Get the global runtime state (for production use).
1303
- * In tests, use TestAgentContext which has isolated runtime state.
1304
- */
1305
- export function getGlobalRuntimeState(): AgentRuntimeState {
1306
- return {
1307
- agents,
1308
- agentConfigs,
1309
- agentEventListeners,
1310
- };
1311
- }
1312
-
1313
- /**
1314
- * Get the runtime state from an AgentContext.
1315
- * @internal
1316
- */
1317
- export function getAgentRuntime(ctx: AgentContext<any, any, any>): AgentRuntimeState {
1318
- return ctx[AGENT_RUNTIME];
1319
- }
1320
-
1321
- // Helper to fire event listeners sequentially, abort on first error
1322
- async function fireAgentEvent(
1323
- runtime: AgentRuntimeState,
1324
- agent: Agent<any, any, any, any, any>,
1325
- eventName: 'started' | 'completed',
1326
- context: AgentContext<any, any, any>
1327
- ): Promise<void>;
1328
- async function fireAgentEvent(
1329
- runtime: AgentRuntimeState,
1330
- agent: Agent<any, any, any, any, any>,
1331
- eventName: 'errored',
1332
- context: AgentContext<any, any, any>,
1333
- data: Error
1334
- ): Promise<void>;
1335
- async function fireAgentEvent(
1336
- runtime: AgentRuntimeState,
1337
- agent: Agent<any, any, any, any, any>,
1338
- eventName: AgentEventName,
1339
- context: AgentContext<any, any, any>,
1340
- data?: Error
1341
- ): Promise<void> {
1342
- // Fire agent-level listeners
1343
- const listeners = runtime.agentEventListeners.get(agent);
1344
- if (listeners) {
1345
- const callbacks = listeners.get(eventName);
1346
- if (callbacks && callbacks.size > 0) {
1347
- for (const callback of callbacks) {
1348
- try {
1349
- if (eventName === 'errored' && data) {
1350
- await (callback as any)(eventName, agent, context, data);
1351
- } else if (eventName === 'started' || eventName === 'completed') {
1352
- await (callback as any)(eventName, agent, context);
1353
- }
1354
- } catch (error) {
1355
- // Log but don't re-throw - event listener errors should not crash the server
1356
- internal.error(`Error in agent event listener for '${eventName}':`, error);
1357
- }
1358
- }
1359
- }
1360
- }
1361
-
1362
- // Fire global app-level events
1363
- if (eventName === 'errored' && data) {
1364
- await fireEvent('agent.errored', agent, context, data);
1365
- } else if (eventName === 'started') {
1366
- await fireEvent('agent.started', agent, context);
1367
- } else if (eventName === 'completed') {
1368
- await fireEvent('agent.completed', agent, context);
1369
- }
1370
- }
1371
-
1372
- /**
1373
- * Union type of all registered agent names.
1374
- * Falls back to `string` when no agents are registered (before augmentation).
1375
- * After augmentation, this becomes a strict union of agent names for full type safety.
1376
- */
1377
- export type AgentName = keyof AgentRegistry extends never ? string : keyof AgentRegistry;
1378
-
1379
- /**
1380
- * Agent registry interface.
1381
- * This interface is augmented by generated code to provide strongly-typed agent access.
1382
- */
1383
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
1384
- export interface AgentRegistry {}
1385
-
1386
- export const registerAgent = (name: AgentName, agent: Agent<any, any, any, any, any>): void => {
1387
- agents.set(name, agent);
1388
- };
1389
-
1390
- export const setAgentConfig = (name: AgentName, config: unknown): void => {
1391
- agentConfigs.set(name, config);
1392
- };
1393
-
1394
- export const getAgentConfig = (name: AgentName): unknown => {
1395
- return agentConfigs.get(name);
1396
- };
1397
-
1398
- const ValidationError = StructuredError('ValidationError')<{
1399
- issues: readonly StandardSchemaV1.Issue[];
1400
- }>();
1401
-
1402
- /**
1403
- * Configuration object for creating an agent with explicit type parameters.
1404
- *
1405
- * @template TInput - Input schema type (StandardSchemaV1 or undefined)
1406
- * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
1407
- * @template TStream - Whether agent returns a stream (true/false)
1408
- * @template TConfig - Type returned by setup function
1409
- * @template TAppState - Custom app state type from createApp
1410
- */
1411
- export interface CreateAgentConfigExplicit<
1412
- TInput extends StandardSchemaV1 | undefined = undefined,
1413
- TOutput extends StandardSchemaV1 | undefined = undefined,
1414
- TStream extends boolean = false,
1415
- TConfig = unknown,
1416
- TAppState = AppState,
1417
- > {
1418
- /**
1419
- * Optional schema validation.
1420
- *
1421
- * @example
1422
- * ```typescript
1423
- * schema: {
1424
- * input: z.object({ name: z.string() }),
1425
- * output: z.string(),
1426
- * stream: false,
1427
- * }
1428
- * ```
1429
- */
1430
- schema?: {
1431
- /** Input validation schema */
1432
- input?: TInput;
1433
- /** Output validation schema */
1434
- output?: TOutput;
1435
- /** Whether the agent returns a ReadableStream */
1436
- stream?: TStream;
1437
- };
1438
-
1439
- /**
1440
- * Optional description of what this agent does.
1441
- *
1442
- * @example
1443
- * ```typescript
1444
- * description: 'Does something useful'
1445
- * ```
1446
- */
1447
- description?: string;
1448
-
1449
- /**
1450
- * Optional metadata object (typically injected by build plugin during compilation).
1451
- * Contains agent identification and versioning information.
1452
- *
1453
- * @internal - Usually populated by build tooling, not manually set
1454
- */
1455
- metadata?: Partial<AgentMetadata>;
1456
-
1457
- /**
1458
- * Optional setup function receiving app state, returns agent config.
1459
- * The returned value is available in the handler via `ctx.config`.
1460
- *
1461
- * @param app - Application state from createApp
1462
- * @returns Agent-specific configuration
1463
- *
1464
- * @example
1465
- * ```typescript
1466
- * setup: async (app) => ({ cache: new Map() })
1467
- * ```
1468
- */
1469
- setup?: (app: TAppState) => Promise<TConfig> | TConfig;
1470
-
1471
- /**
1472
- * Optional cleanup function called on app shutdown.
1473
- *
1474
- * @param app - Application state from createApp
1475
- * @param config - Agent config returned from setup
1476
- *
1477
- * @example
1478
- * ```typescript
1479
- * shutdown: async (app, config) => {
1480
- * config.cache.clear();
1481
- * }
1482
- * ```
1483
- */
1484
- shutdown?: (app: TAppState, config: TConfig) => Promise<void> | void;
1485
-
1486
- /**
1487
- * Agent handler function.
1488
- * Type is automatically inferred based on schema definitions.
1489
- *
1490
- * @param ctx - Agent context
1491
- * @param input - Validated input (only present if schema.input is defined)
1492
- * @returns Output or ReadableStream based on schema
1493
- *
1494
- * @example
1495
- * ```typescript
1496
- * handler: async (ctx, input) => {
1497
- * return `Hello, ${input.name}!`;
1498
- * }
1499
- * ```
1500
- */
1501
- handler: TInput extends StandardSchemaV1
1502
- ? TStream extends true
1503
- ? TOutput extends StandardSchemaV1
1504
- ? (
1505
- c: AgentContext<any, TConfig, TAppState>,
1506
- input: InferOutput<TInput>
1507
- ) =>
1508
- | Promise<ReadableStream<InferOutput<TOutput>>>
1509
- | ReadableStream<InferOutput<TOutput>>
1510
- : (
1511
- c: AgentContext<any, TConfig, TAppState>,
1512
- input: InferOutput<TInput>
1513
- ) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
1514
- : TOutput extends StandardSchemaV1
1515
- ? (
1516
- c: AgentContext<any, TConfig, TAppState>,
1517
- input: InferOutput<TInput>
1518
- ) => Promise<InferOutput<TOutput>> | InferOutput<TOutput>
1519
- : (
1520
- c: AgentContext<any, TConfig, TAppState>,
1521
- input: InferOutput<TInput>
1522
- ) => Promise<void> | void
1523
- : TStream extends true
1524
- ? TOutput extends StandardSchemaV1
1525
- ? (
1526
- c: AgentContext<any, TConfig, TAppState>
1527
- ) =>
1528
- | Promise<ReadableStream<InferOutput<TOutput>>>
1529
- | ReadableStream<InferOutput<TOutput>>
1530
- : (
1531
- c: AgentContext<any, TConfig, TAppState>
1532
- ) => Promise<ReadableStream<unknown>> | ReadableStream<unknown>
1533
- : TOutput extends StandardSchemaV1
1534
- ? (
1535
- c: AgentContext<any, TConfig, TAppState>
1536
- ) => Promise<InferOutput<TOutput>> | InferOutput<TOutput>
1537
- : (c: AgentContext<any, TConfig, TAppState>) => Promise<void> | void;
1538
- }
1539
-
1540
- /**
1541
- * Creates an agent with schema validation and lifecycle hooks.
1542
- *
1543
- * This is the recommended way to create agents with automatic type inference from schemas.
1544
- *
1545
- * @template TSchema - Schema definition object containing optional input, output, and stream properties
1546
- * @template TConfig - Function type that returns agent-specific configuration from setup
1547
- *
1548
- * @param name - Unique agent name (must be unique within the project)
1549
- * @param config - Agent configuration object
1550
- *
1551
- * @returns AgentRunner with a run method for executing the agent
1552
- *
1553
- * @example
1554
- * ```typescript
1555
- * const agent = createAgent('greeting-agent', {
1556
- * description: 'Returns personalized greetings',
1557
- * schema: {
1558
- * input: z.object({ name: z.string(), age: z.number() }),
1559
- * output: z.string()
1560
- * },
1561
- * handler: async (ctx, { name, age }) => {
1562
- * ctx.logger.info(`Processing greeting for ${name}`);
1563
- * return `Hello, ${name}! You are ${age} years old.`;
1564
- * }
1565
- * });
1566
- *
1567
- * // Call the agent directly
1568
- * const result = await agent.run({ name: 'Alice', age: 30 });
1569
- * ```
1570
- */
1571
- export function createAgent<
1572
- TSchema extends
1573
- | {
1574
- input?: StandardSchemaV1;
1575
- output?: StandardSchemaV1;
1576
- stream?: boolean;
1577
- }
1578
- | undefined = undefined,
1579
- TConfig extends (app: AppState) => any = any,
1580
- >(
1581
- name: string,
1582
- config: CreateAgentConfig<TSchema, TConfig>
1583
- ): AgentRunner<SchemaInput<TSchema>, SchemaOutput<TSchema>, SchemaStream<TSchema>>;
1584
-
1585
- /**
1586
- * Creates an agent with explicit generic type parameters.
1587
- *
1588
- * Use this overload when you need explicit control over types or working with custom app state.
1589
- *
1590
- * @template TInput - Input schema type (StandardSchemaV1 or undefined)
1591
- * @template TOutput - Output schema type (StandardSchemaV1 or undefined)
1592
- * @template TStream - Whether agent returns a stream (true/false)
1593
- * @template TConfig - Type returned by setup function
1594
- * @template TAppState - Custom app state type from createApp
1595
- *
1596
- * @param name - Unique agent name (must be unique within the project)
1597
- * @param config - Agent configuration object
1598
- *
1599
- * @returns AgentRunner with explicit types and a run method
1600
- *
1601
- * @example
1602
- * ```typescript
1603
- * interface MyAppState { db: Database }
1604
- * interface MyConfig { cache: Map<string, any> }
1605
- *
1606
- * const agent = createAgent<
1607
- * z.ZodObject<any>, // TInput
1608
- * z.ZodString, // TOutput
1609
- * false // TStream
1610
- * >('custom-agent', {
1611
- * setup: async (app) => ({ cache: new Map() }),
1612
- * handler: async (ctx, input) => {
1613
- * const db = ctx.app.db;
1614
- * const cache = ctx.config.cache;
1615
- * return 'result';
1616
- * }
1617
- * });
1618
- * ```
1619
- */
1620
- export function createAgent<
1621
- TInput extends StandardSchemaV1 | undefined = undefined,
1622
- TOutput extends StandardSchemaV1 | undefined = undefined,
1623
- TStream extends boolean = false,
1624
- TConfig = unknown,
1625
- TAppState = AppState,
1626
- >(
1627
- name: string,
1628
- config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
1629
- ): AgentRunner<TInput, TOutput, TStream>;
1630
-
1631
- // Implementation
1632
- export function createAgent<
1633
- TInput extends StandardSchemaV1 | undefined = undefined,
1634
- TOutput extends StandardSchemaV1 | undefined = undefined,
1635
- TStream extends boolean = false,
1636
- TConfig = unknown,
1637
- TAppState = AppState,
1638
- >(
1639
- name: string,
1640
- config: CreateAgentConfigExplicit<TInput, TOutput, TStream, TConfig, TAppState>
1641
- ): AgentRunner<TInput, TOutput, TStream> {
1642
- const inputSchema = config.schema?.input;
1643
- const outputSchema = config.schema?.output;
1644
-
1645
- // Initialize evals array before handler so it can be captured in closure
1646
- // Evals should only be added via agent.createEval() after agent creation
1647
- const evalsArray: Eval[] = [];
1648
-
1649
- const handler = async (input?: any) => {
1650
- let validatedInput: any = undefined;
1651
-
1652
- if (inputSchema) {
1653
- const inputResult = await inputSchema['~standard'].validate(input);
1654
- if (inputResult.issues) {
1655
- throw new ValidationError({
1656
- issues: inputResult.issues,
1657
- message: `Input validation failed: ${inputResult.issues.map((i: any) => i.message).join(', ')}`,
1658
- });
1659
- }
1660
- validatedInput = inputResult.value;
1661
- }
1662
-
1663
- const agentCtx = getAgentContext() as AgentContext<any, TConfig, TAppState>;
1664
-
1665
- // Store current agent for telemetry (using Symbol to keep it internal)
1666
- (agentCtx as any)[CURRENT_AGENT] = agent;
1667
-
1668
- // Expose current agent metadata on the context
1669
- agentCtx.current = agent.metadata;
1670
-
1671
- // Update ctx.config with this agent's setup() return value.
1672
- // This ensures correct config when:
1673
- // - Agent is called via user router (createAgentMiddleware(''))
1674
- // - Agent A calls Agent B (agentB.run() inside handler)
1675
- // - Agent is called via runInAgentContext() in tests
1676
- const agentConfig = getAgentConfig(agent.metadata.name as AgentName);
1677
- if (agentConfig !== undefined) {
1678
- agentCtx.config = agentConfig as TConfig;
1679
- }
1680
-
1681
- const attrs = {
1682
- '@agentuity/agentId': agent.metadata.agentId, // stable ID (agent_*) - consistent across deployments
1683
- '@agentuity/agentInstanceId': agent.metadata.id, // deployment-specific ID (agentid_*) - changes per deployment
1684
- '@agentuity/agentDescription': agent.metadata.description,
1685
- '@agentuity/agentName': agent.metadata.name,
1686
- '@agentuity/threadId': agentCtx.thread.id,
1687
- };
1688
-
1689
- // Set agent attributes on the current active span
1690
- const activeSpan = trace.getActiveSpan();
1691
- if (activeSpan) {
1692
- activeSpan.setAttributes(attrs);
1693
- }
1694
-
1695
- if (inHTTPContext()) {
1696
- const honoCtx = privateContext(getHTTPContext());
1697
- if (honoCtx.var.agentIds) {
1698
- if (agent.metadata.id) honoCtx.var.agentIds.add(agent.metadata.id);
1699
- if (agent.metadata.agentId) honoCtx.var.agentIds.add(agent.metadata.agentId);
1700
- }
1701
- } else {
1702
- // For standalone contexts, check for AGENT_IDS symbol
1703
- const agentIds = (agentCtx as any)[AGENT_IDS] as Set<string> | undefined;
1704
- if (agentIds) {
1705
- if (agent.metadata.id) agentIds.add(agent.metadata.id);
1706
- if (agent.metadata.agentId) agentIds.add(agent.metadata.agentId);
1707
- }
1708
- }
1709
-
1710
- agentCtx.logger = agentCtx.logger.child(attrs);
1711
-
1712
- // Get the agent instance from the runtime state to fire events
1713
- const runtime = getAgentRuntime(agentCtx);
1714
-
1715
- // Fire 'started' event
1716
- await fireAgentEvent(runtime, agent as Agent, 'started', agentCtx);
1717
-
1718
- try {
1719
- // Execute the handler directly - span creation is handled by the caller (AgentRunner.run)
1720
- // This avoids duplicate spans when agents call other agents
1721
- const result = await (async () => {
1722
- if (agent.metadata.id && !inHTTPContext()) {
1723
- // For standalone contexts, wrap with agent context to set aid in trace state
1724
- return runWithAgentContext(agent.metadata.id, () =>
1725
- inputSchema
1726
- ? (config.handler as any)(agentCtx, validatedInput)
1727
- : (config.handler as any)(agentCtx)
1728
- );
1729
- } else {
1730
- // HTTP context or no agent ID - invoke handler directly
1731
- // Span is created by AgentRunner.run or createAgentRunner
1732
- return inputSchema
1733
- ? (config.handler as any)(agentCtx, validatedInput)
1734
- : (config.handler as any)(agentCtx);
1735
- }
1736
- })();
1737
-
1738
- let validatedOutput: any = result;
1739
- // Skip output validation for streaming agents (they return ReadableStream)
1740
- if (outputSchema && !config.schema?.stream) {
1741
- const outputResult = await outputSchema['~standard'].validate(result);
1742
- if (outputResult.issues) {
1743
- throw new ValidationError({
1744
- issues: outputResult.issues,
1745
- message: `Output validation failed: ${outputResult.issues.map((i: any) => i.message).join(', ')}`,
1746
- });
1747
- }
1748
- validatedOutput = outputResult.value;
1749
- }
1750
-
1751
- // Store validated input/output in context state for event listeners
1752
- agentCtx.state.set('_evalInput', validatedInput);
1753
- agentCtx.state.set('_evalOutput', validatedOutput);
1754
-
1755
- // Fire 'completed' event - evals will run via event listener
1756
- await fireAgentEvent(runtime, agent as Agent, 'completed', agentCtx);
1757
-
1758
- return validatedOutput;
1759
- } catch (error) {
1760
- // Fire 'errored' event
1761
- await fireAgentEvent(runtime, agent as Agent, 'errored', agentCtx, error as Error);
1762
- throw error;
1763
- }
1764
- };
1765
-
1766
- // Infer input/output types from agent schema
1767
- type AgentInput = TInput extends StandardSchemaV1 ? InferOutput<TInput> : undefined;
1768
- type AgentOutput = TOutput extends StandardSchemaV1 ? InferOutput<TOutput> : undefined;
1769
-
1770
- // Create createEval method that infers types from agent and automatically adds to agent
1771
- const createEval: CreateEvalMethod<TInput, TOutput> = ((
1772
- evalNameOrConfig: string | PresetEvalConfig<TInput, TOutput>,
1773
- evalConfig?: {
1774
- description?: string;
1775
- handler: EvalFunction<AgentInput, AgentOutput>;
1776
- metadata?: {
1777
- id?: string;
1778
- identifier?: string;
1779
- version?: string;
1780
- filename?: string;
1781
- };
1782
- }
1783
- ): Eval<TInput, TOutput> => {
1784
- // Handle preset eval config (single argument with name property)
1785
- if (typeof evalNameOrConfig !== 'string' && 'name' in evalNameOrConfig) {
1786
- const presetConfig = evalNameOrConfig as PresetEvalConfig<TInput, TOutput>;
1787
- const evalName = presetConfig.name;
1788
-
1789
- internal.debug(
1790
- `createEval called for agent "${name || 'unknown'}": registering preset eval "${evalName}"`
1791
- );
1792
-
1793
- const evalType: any = {
1794
- metadata: {
1795
- identifier: evalName,
1796
- name: evalName,
1797
- description: presetConfig.description || '',
1798
- },
1799
- handler: presetConfig.handler,
1800
- };
1801
-
1802
- if (inputSchema) {
1803
- evalType.inputSchema = inputSchema;
1804
- }
1805
-
1806
- if (outputSchema) {
1807
- evalType.outputSchema = outputSchema;
1808
- }
1809
-
1810
- evalsArray.push(evalType);
1811
- internal.debug(
1812
- `Added preset eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`
1813
- );
1814
-
1815
- return evalType as Eval<TInput, TOutput>;
1816
- }
1817
-
1818
- // Handle custom eval config (name + config)
1819
- if (typeof evalNameOrConfig !== 'string' || !evalConfig) {
1820
- throw new Error(
1821
- 'Invalid arguments: expected (name: string, config) or (config: PresetEvalConfig)'
1822
- );
1823
- }
1824
-
1825
- const evalName = evalNameOrConfig;
1826
-
1827
- // Trace log to verify evals file is imported
1828
- internal.debug(
1829
- `createEval called for agent "${name || 'unknown'}": registering eval "${evalName}"`
1830
- );
1831
-
1832
- // Use build-time injected metadata if available (same pattern as agents)
1833
- const evalMetadata = evalConfig.metadata || {};
1834
-
1835
- // Build eval metadata - merge injected metadata with defaults
1836
- const evalType: any = {
1837
- metadata: {
1838
- // Use build-time injected metadata if available, otherwise fallback to empty/undefined
1839
- id: evalMetadata.id || undefined,
1840
- identifier: evalMetadata.identifier || undefined,
1841
- version: evalMetadata.version || undefined,
1842
- filename: evalMetadata.filename || '',
1843
- name: evalName,
1844
- description: evalConfig.description || '',
1845
- },
1846
- handler: evalConfig.handler,
1847
- };
1848
-
1849
- if (inputSchema) {
1850
- evalType.inputSchema = inputSchema;
1851
- }
1852
-
1853
- if (outputSchema) {
1854
- evalType.outputSchema = outputSchema;
1855
- }
1856
-
1857
- // Automatically add eval to agent's evals array
1858
- evalsArray.push(evalType);
1859
- internal.debug(
1860
- `Added eval "${evalName}" to agent "${name || 'unknown'}". Total evals: ${evalsArray.length}`
1861
- );
1862
-
1863
- return evalType as Eval<TInput, TOutput>;
1864
- }) as CreateEvalMethod<TInput, TOutput>;
1865
-
1866
- // Build metadata - merge user-provided metadata with defaults
1867
- // The build plugin injects metadata via config.metadata during AST transformation
1868
- let metadata: Partial<AgentMetadata> = {
1869
- // Defaults (used when running without build, e.g., dev mode)
1870
- name,
1871
- description: config.description,
1872
- id: '',
1873
- agentId: '',
1874
- filename: '',
1875
- version: '',
1876
- inputSchemaCode: '',
1877
- outputSchemaCode: '',
1878
- // Merge in build-time injected metadata (overrides defaults)
1879
- ...config.metadata,
1880
- };
1881
-
1882
- // If id/agentId are empty, try to load from agentuity.metadata.json
1883
- if (!metadata.id || !metadata.agentId) {
1884
- const fileMetadata = getAgentMetadataByName(name);
1885
- if (fileMetadata) {
1886
- internal.info(
1887
- '[agent] loaded metadata for "%s" from file: id=%s, agentId=%s',
1888
- name,
1889
- fileMetadata.id,
1890
- fileMetadata.agentId
1891
- );
1892
- metadata = {
1893
- ...metadata,
1894
- id: fileMetadata.id || metadata.id,
1895
- agentId: fileMetadata.agentId || metadata.agentId,
1896
- filename: fileMetadata.filename || metadata.filename,
1897
- version: fileMetadata.version || metadata.version,
1898
- };
1899
- }
1900
- }
1901
-
1902
- // Error if agent has no metadata IDs in production - this causes agent_ids to be empty in sessions
1903
- // which affects analytics, billing attribution, and session filtering
1904
- // Only enforce in production (when AGENTUITY_CLOUD_PROJECT_ID is set) to allow dev/test without metadata
1905
- if (!metadata.id && !metadata.agentId && runtimeConfig.getProjectId()) {
1906
- throw new Error(
1907
- `Agent "${name}" has no metadata IDs (id and agentId are empty). ` +
1908
- `This will result in empty agent_ids in session events. ` +
1909
- `Ensure agentuity.metadata.json exists in the runtime directory ` +
1910
- `(checked: ${process.cwd()}/agentuity.metadata.json and ${process.cwd()}/.agentuity/agentuity.metadata.json). ` +
1911
- `Run 'agentuity build' to generate the metadata file.`
1912
- );
1913
- }
1914
-
1915
- const agent: any = {
1916
- handler,
1917
- metadata,
1918
- evals: evalsArray,
1919
- createEval,
1920
- setup: config.setup,
1921
- shutdown: config.shutdown,
1922
- };
1923
-
1924
- // Add event listener methods
1925
- agent.addEventListener = (eventName: AgentEventName, callback: any): void => {
1926
- const agentForListeners = agent as any as Agent<any, any, any>;
1927
- const callbackForListeners = callback as any as AgentEventCallback<any>;
1928
- let listeners = agentEventListeners.get(agentForListeners);
1929
- if (!listeners) {
1930
- listeners = new Map();
1931
- agentEventListeners.set(agentForListeners, listeners);
1932
- }
1933
- let callbacks = listeners.get(eventName);
1934
- if (!callbacks) {
1935
- callbacks = new Set();
1936
- listeners.set(eventName, callbacks);
1937
- }
1938
- callbacks.add(callbackForListeners);
1939
- };
1940
-
1941
- // Automatically add event listener for 'completed' event to run evals
1942
- (agent as Agent).addEventListener('completed', async (_event, _agent, ctx) => {
1943
- // Use the agent instance passed to event listener to access its evals array
1944
- // This ensures we get evals that were added via agent.createEval() after agent creation
1945
- const agentEvals = _agent?.evals || evalsArray;
1946
-
1947
- internal.debug(
1948
- `Checking evals: agent=${_agent.metadata?.name}, evalsArray.length=${evalsArray?.length || 0}, agent.evals.length=${_agent?.evals?.length || 0}`
1949
- );
1950
-
1951
- if (agentEvals && agentEvals.length > 0) {
1952
- internal.info(`Executing ${agentEvals.length} evaluations after agent run`);
1953
-
1954
- // Get validated input/output from context state
1955
- const validatedInput = ctx.state.get('_evalInput');
1956
- const validatedOutput = ctx.state.get('_evalOutput');
1957
-
1958
- // Capture agentRunSpanId synchronously before waitUntil (which may run outside AsyncLocalStorage)
1959
- let agentRunSpanId: string | undefined;
1960
- try {
1961
- const httpCtx = getHTTPContext();
1962
- const _httpCtx = privateContext(httpCtx);
1963
- agentRunSpanId = _httpCtx.var.agentRunSpanId;
1964
- } catch {
1965
- // HTTP context may not be available, spanId will be undefined
1966
- }
1967
-
1968
- // Capture the agent span context so eval spans are parented to the agent
1969
- const agentSpanContext = context.active();
1970
-
1971
- // Execute each eval using waitUntil to avoid blocking the response
1972
- for (const evalItem of agentEvals) {
1973
- const evalName = evalItem.metadata.name || 'unnamed';
1974
- const agentName = _agent?.metadata?.name || name;
1975
- const evalRunId = generateId('evalrun');
1976
-
1977
- // Look up eval metadata synchronously before async execution
1978
- const evalMeta = getEvalMetadata(agentName, evalName);
1979
- const evalId = evalMeta?.id || '';
1980
- const evalIdentifier = evalMeta?.identifier || '';
1981
-
1982
- // Create eval span FIRST, parented to agent, then call waitUntil inside it
1983
- // This makes waitUntil a child of the eval span
1984
- const tracer = ctx.tracer;
1985
- if (tracer) {
1986
- const evalSpan = tracer.startSpan(evalName, {}, agentSpanContext);
1987
- evalSpan.setAttributes({
1988
- '@agentuity/evalId': evalId,
1989
- '@agentuity/evalIdentifier': evalIdentifier,
1990
- '@agentuity/evalName': evalName,
1991
- '@agentuity/evalRunId': evalRunId,
1992
- '@agentuity/agentName': agentName,
1993
- '@agentuity/evalDescription':
1994
- evalMeta?.description || evalItem.metadata.description || '',
1995
- '@agentuity/evalFilename': evalMeta?.filename || evalItem.metadata.filename || '',
1996
- });
1997
-
1998
- const evalSpanContext = trace.setSpan(agentSpanContext, evalSpan);
1999
-
2000
- // Run waitUntil INSIDE the eval span context - this makes waitUntil a child of eval
2001
- // Pass a function (not an already-executing promise) so waitUntil executes it
2002
- // AFTER setting up its span context, making operations children of waitUntil
2003
- context.with(evalSpanContext, () => {
2004
- ctx.waitUntil(async () => {
2005
- const orgId = runtimeConfig.getOrganizationId();
2006
- const projectId = runtimeConfig.getProjectId();
2007
- const devMode = runtimeConfig.isDevMode() ?? false;
2008
- const evalRunEventProvider = getEvalRunEventProvider();
2009
-
2010
- const shouldSendEvalRunEvents =
2011
- orgId && projectId && evalId !== '' && evalIdentifier !== '';
2012
-
2013
- try {
2014
- internal.info(`[EVALRUN] Starting eval run tracking for '${evalName}'`);
2015
-
2016
- // Send eval run start event
2017
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2018
- try {
2019
- const deploymentId = runtimeConfig.getDeploymentId();
2020
- await evalRunEventProvider.start({
2021
- id: evalRunId,
2022
- sessionId: ctx.sessionId,
2023
- evalId,
2024
- evalIdentifier,
2025
- orgId: orgId!,
2026
- projectId: projectId!,
2027
- devmode: Boolean(devMode),
2028
- deploymentId: deploymentId || undefined,
2029
- spanId: agentRunSpanId,
2030
- });
2031
- } catch (error) {
2032
- internal.error(
2033
- `[EVALRUN] Error sending start event for '${evalName}'`,
2034
- { error }
2035
- );
2036
- }
2037
- }
2038
-
2039
- // Validate eval input/output if schemas exist
2040
- let evalValidatedInput: any = validatedInput;
2041
- let evalValidatedOutput: any = validatedOutput;
2042
-
2043
- if (evalItem.inputSchema) {
2044
- const result =
2045
- await evalItem.inputSchema['~standard'].validate(validatedInput);
2046
- if (result.issues) {
2047
- throw new ValidationError({
2048
- issues: result.issues,
2049
- message: `Eval input validation failed`,
2050
- });
2051
- }
2052
- evalValidatedInput = result.value;
2053
- }
2054
-
2055
- if (evalItem.outputSchema) {
2056
- const result =
2057
- await evalItem.outputSchema['~standard'].validate(validatedOutput);
2058
- if (result.issues) {
2059
- throw new ValidationError({
2060
- issues: result.issues,
2061
- message: `Eval output validation failed`,
2062
- });
2063
- }
2064
- evalValidatedOutput = result.value;
2065
- }
2066
-
2067
- // Execute the eval handler
2068
- let handlerResult: EvalHandlerResult;
2069
- if (inputSchema && outputSchema) {
2070
- handlerResult = await (evalItem.handler as any)(
2071
- ctx,
2072
- evalValidatedInput,
2073
- evalValidatedOutput
2074
- );
2075
- } else if (inputSchema) {
2076
- handlerResult = await (evalItem.handler as any)(ctx, evalValidatedInput);
2077
- } else if (outputSchema) {
2078
- handlerResult = await (evalItem.handler as any)(
2079
- ctx,
2080
- evalValidatedOutput
2081
- );
2082
- } else {
2083
- handlerResult = await (evalItem.handler as any)(ctx);
2084
- }
2085
-
2086
- const result: EvalRunResult = { success: true, ...handlerResult };
2087
-
2088
- // Send eval run complete event
2089
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2090
- try {
2091
- await evalRunEventProvider.complete({ id: evalRunId, result });
2092
- } catch (error) {
2093
- internal.error(
2094
- `[EVALRUN] Error sending complete event for '${evalName}'`,
2095
- { error }
2096
- );
2097
- }
2098
- }
2099
-
2100
- internal.debug(`Eval '${evalName}' completed successfully`);
2101
- } catch (error) {
2102
- const errorMessage = error instanceof Error ? error.message : String(error);
2103
- evalSpan.recordException(error as Error);
2104
- evalSpan.setStatus({
2105
- code: SpanStatusCode.ERROR,
2106
- message: errorMessage,
2107
- });
2108
- internal.error(`Error executing eval '${evalName}'`, { error });
2109
-
2110
- // Send error event
2111
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2112
- try {
2113
- await evalRunEventProvider.complete({
2114
- id: evalRunId,
2115
- error: errorMessage,
2116
- result: {
2117
- success: false,
2118
- passed: false,
2119
- error: errorMessage,
2120
- metadata: {},
2121
- },
2122
- });
2123
- } catch (e) {
2124
- internal.debug('Failed to send eval run complete event', {
2125
- evalRunId,
2126
- errorMessage,
2127
- error: e instanceof Error ? e.message : String(e),
2128
- });
2129
- }
2130
- }
2131
- } finally {
2132
- evalSpan.end();
2133
- }
2134
- });
2135
- });
2136
- } else {
2137
- // No tracer - execute without span
2138
- ctx.waitUntil(async () => {
2139
- const orgId = runtimeConfig.getOrganizationId();
2140
- const projectId = runtimeConfig.getProjectId();
2141
- const devMode = runtimeConfig.isDevMode() ?? false;
2142
- const evalRunEventProvider = getEvalRunEventProvider();
2143
- const shouldSendEvalRunEvents =
2144
- orgId && projectId && evalId !== '' && evalIdentifier !== '';
2145
-
2146
- try {
2147
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2148
- try {
2149
- await evalRunEventProvider.start({
2150
- id: evalRunId,
2151
- sessionId: ctx.sessionId,
2152
- evalId,
2153
- evalIdentifier,
2154
- orgId: orgId!,
2155
- projectId: projectId!,
2156
- devmode: Boolean(devMode),
2157
- deploymentId: runtimeConfig.getDeploymentId() || undefined,
2158
- spanId: agentRunSpanId,
2159
- });
2160
- } catch (e) {
2161
- internal.debug('Failed to send eval run start event', {
2162
- evalRunId,
2163
- evalId,
2164
- evalIdentifier,
2165
- sessionId: ctx.sessionId,
2166
- error: e instanceof Error ? e.message : String(e),
2167
- });
2168
- }
2169
- }
2170
-
2171
- let evalValidatedInput: any = validatedInput;
2172
- let evalValidatedOutput: any = validatedOutput;
2173
-
2174
- if (evalItem.inputSchema) {
2175
- const result =
2176
- await evalItem.inputSchema['~standard'].validate(validatedInput);
2177
- if (result.issues) {
2178
- throw new ValidationError({
2179
- issues: result.issues,
2180
- message: `Eval input validation failed`,
2181
- });
2182
- }
2183
- evalValidatedInput = result.value;
2184
- }
2185
- if (evalItem.outputSchema) {
2186
- const result =
2187
- await evalItem.outputSchema['~standard'].validate(validatedOutput);
2188
- if (result.issues) {
2189
- throw new ValidationError({
2190
- issues: result.issues,
2191
- message: `Eval output validation failed`,
2192
- });
2193
- }
2194
- evalValidatedOutput = result.value;
2195
- }
2196
-
2197
- let handlerResult: EvalHandlerResult;
2198
- if (inputSchema && outputSchema) {
2199
- handlerResult = await (evalItem.handler as any)(
2200
- ctx,
2201
- evalValidatedInput,
2202
- evalValidatedOutput
2203
- );
2204
- } else if (inputSchema) {
2205
- handlerResult = await (evalItem.handler as any)(ctx, evalValidatedInput);
2206
- } else if (outputSchema) {
2207
- handlerResult = await (evalItem.handler as any)(ctx, evalValidatedOutput);
2208
- } else {
2209
- handlerResult = await (evalItem.handler as any)(ctx);
2210
- }
2211
-
2212
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2213
- try {
2214
- await evalRunEventProvider.complete({
2215
- id: evalRunId,
2216
- result: { success: true, ...handlerResult },
2217
- });
2218
- } catch (e) {
2219
- internal.debug('Failed to send eval run complete event', {
2220
- evalRunId,
2221
- error: e instanceof Error ? e.message : String(e),
2222
- });
2223
- }
2224
- }
2225
- } catch (error) {
2226
- const errorMessage = error instanceof Error ? error.message : String(error);
2227
- internal.error(`Error executing eval '${evalName}'`, { error });
2228
-
2229
- // Send error event to match traced branch behavior
2230
- if (shouldSendEvalRunEvents && evalRunEventProvider) {
2231
- try {
2232
- await evalRunEventProvider.complete({
2233
- id: evalRunId,
2234
- error: errorMessage,
2235
- result: {
2236
- success: false,
2237
- passed: false,
2238
- error: errorMessage,
2239
- metadata: {},
2240
- },
2241
- });
2242
- } catch (e) {
2243
- internal.debug('Failed to send eval run complete event', {
2244
- evalRunId,
2245
- errorMessage,
2246
- error: e instanceof Error ? e.message : String(e),
2247
- });
2248
- }
2249
- }
2250
- }
2251
- });
2252
- }
2253
- }
2254
- }
2255
- });
2256
-
2257
- agent.removeEventListener = (eventName: AgentEventName, callback: any): void => {
2258
- const agentForListeners = agent as any as Agent<any, any, any>;
2259
- const callbackForListeners = callback as any as AgentEventCallback<any>;
2260
- const listeners = agentEventListeners.get(agentForListeners);
2261
- if (!listeners) return;
2262
- const callbacks = listeners.get(eventName);
2263
- if (!callbacks) return;
2264
- callbacks.delete(callbackForListeners);
2265
- };
2266
-
2267
- if (inputSchema) {
2268
- agent.inputSchema = inputSchema;
2269
- }
2270
-
2271
- if (outputSchema) {
2272
- agent.outputSchema = outputSchema;
2273
- }
2274
-
2275
- if (config.schema?.stream) {
2276
- agent.stream = config.schema.stream;
2277
- }
2278
-
2279
- // Add validator method with overloads
2280
- agent.validator = ((override?: any) => {
2281
- const effectiveInputSchema = override?.input ?? inputSchema;
2282
- // Only use agent's output schema if no override was provided at all.
2283
- // If override is provided (even with just input), don't auto-apply agent's output schema
2284
- // unless the override explicitly includes output.
2285
- const effectiveOutputSchema = override ? override.output : outputSchema;
2286
-
2287
- // Helper to build the standard Hono input validator so types flow
2288
- const buildInputValidator = (schema?: StandardSchemaV1) =>
2289
- validator('json', async (value, c) => {
2290
- if (schema) {
2291
- const result = await validateSchema(schema, value);
2292
- if (!result.success) {
2293
- return c.json(
2294
- {
2295
- error: 'Validation failed',
2296
- message: formatValidationIssues(result.issues),
2297
- issues: result.issues,
2298
- },
2299
- 400
2300
- );
2301
- }
2302
- return result.data;
2303
- }
2304
- return value;
2305
- });
2306
-
2307
- // If no output schema, preserve existing behavior: pure input validation
2308
- if (!effectiveOutputSchema) {
2309
- return buildInputValidator(effectiveInputSchema);
2310
- }
2311
-
2312
- // Output validation middleware (runs after handler)
2313
- const outputValidator: MiddlewareHandler = async (c, next) => {
2314
- await next();
2315
-
2316
- const res = c.res;
2317
- if (!res) return;
2318
-
2319
- // Skip output validation for streaming agents
2320
- if (config.schema?.stream) {
2321
- return;
2322
- }
2323
-
2324
- // Only validate JSON responses
2325
- const contentType = res.headers.get('Content-Type') ?? '';
2326
- if (!contentType.toLowerCase().includes('application/json')) {
2327
- return;
2328
- }
2329
-
2330
- // Clone so we don't consume the body that will be sent
2331
- let responseBody: unknown;
2332
- try {
2333
- const cloned = res.clone();
2334
- responseBody = await cloned.json();
2335
- } catch {
2336
- const OutputValidationError = StructuredError('OutputValidationError')<{
2337
- issues: any[];
2338
- }>();
2339
- throw new OutputValidationError({
2340
- message: 'Output validation failed: response is not valid JSON',
2341
- issues: [],
2342
- });
2343
- }
2344
-
2345
- const result = await validateSchema(effectiveOutputSchema, responseBody);
2346
- if (!result.success) {
2347
- const OutputValidationError = StructuredError('OutputValidationError')<{
2348
- issues: any[];
2349
- }>();
2350
- throw new OutputValidationError({
2351
- message: `Output validation failed: ${formatValidationIssues(result.issues)}`,
2352
- issues: result.issues,
2353
- });
2354
- }
2355
-
2356
- // Replace response with validated/sanitized JSON
2357
- c.res = new Response(JSON.stringify(result.data), {
2358
- status: res.status,
2359
- headers: res.headers,
2360
- });
2361
- };
2362
-
2363
- // If we have no input schema, we only do output validation
2364
- if (!effectiveInputSchema) {
2365
- return outputValidator as unknown as Handler;
2366
- }
2367
-
2368
- // Compose: input validator → output validator
2369
- const inputMiddleware = buildInputValidator(effectiveInputSchema);
2370
-
2371
- const composed: MiddlewareHandler = async (c, next) => {
2372
- // Run the validator first; its next() runs the output validator,
2373
- // whose next() runs the actual handler(s)
2374
- const result = await inputMiddleware(c, async () => {
2375
- await outputValidator(c, next);
2376
- });
2377
- // If inputMiddleware returned early (validation failed), return that response
2378
- return result;
2379
- };
2380
-
2381
- return composed as unknown as Handler;
2382
- }) as AgentValidator<TInput, TOutput>;
2383
-
2384
- // Register the agent for runtime use
2385
- // @ts-expect-error - metadata might be incomplete until build plugin injects InternalAgentMetadata
2386
- agents.set(name, agent as Agent<TInput, TOutput, TStream, TConfig, TAppState>);
2387
-
2388
- // Create and return AgentRunner
2389
- const runner: any = {
2390
- metadata: metadata as AgentMetadata,
2391
- validator: agent.validator,
2392
- inputSchema: inputSchema as TInput,
2393
- outputSchema: outputSchema as TOutput,
2394
- stream: (config.schema?.stream as TStream) || (false as TStream),
2395
- evals: agent.evals,
2396
- createEval,
2397
- addEventListener: agent.addEventListener,
2398
- removeEventListener: agent.removeEventListener,
2399
- run: inputSchema
2400
- ? async (input: InferSchemaInput<Exclude<TInput, undefined>>) => {
2401
- // Wrap with span if in HTTP context with tracer
2402
- if (inHTTPContext()) {
2403
- const honoCtx = getHTTPContext();
2404
- const tracer = honoCtx.var.tracer;
2405
- if (tracer) {
2406
- return runWithSpan(
2407
- tracer,
2408
- agent as Agent<TInput, TOutput, TStream>,
2409
- honoCtx,
2410
- async () => await agent.handler(input)
2411
- );
2412
- }
2413
- }
2414
- return await agent.handler(input);
2415
- }
2416
- : async () => {
2417
- // Wrap with span if in HTTP context with tracer
2418
- if (inHTTPContext()) {
2419
- const honoCtx = getHTTPContext();
2420
- const tracer = honoCtx.var.tracer;
2421
- if (tracer) {
2422
- return runWithSpan(
2423
- tracer,
2424
- agent as Agent<TInput, TOutput, TStream>,
2425
- honoCtx,
2426
- async () => await agent.handler()
2427
- );
2428
- }
2429
- }
2430
- return await agent.handler();
2431
- },
2432
- [INTERNAL_AGENT]: agent, // Store reference to internal agent for testing
2433
- };
2434
-
2435
- return runner as AgentRunner<TInput, TOutput, TStream>;
2436
- }
2437
-
2438
- /**
2439
- * Run a handler with the agent identifier set in trace state.
2440
- * Used for non-HTTP contexts (standalone) where we still want to propagate
2441
- * the agent ID to downstream API calls.
2442
- */
2443
- const runWithAgentContext = async <T>(agentId: string, handler: () => Promise<T>): Promise<T> => {
2444
- const currentContext = context.active();
2445
- const activeSpan = trace.getSpan(currentContext);
2446
-
2447
- if (!activeSpan) {
2448
- // No active span, just run the handler
2449
- return handler();
2450
- }
2451
-
2452
- // Enrich the context's traceState with the agent ID so it propagates
2453
- // to downstream calls. Note: this does NOT affect the active recording
2454
- // span's exported traceState (that was set at span creation). This only
2455
- // affects propagation context for outbound requests.
2456
- const contextWithAgentId = enrichContextWithTraceState(currentContext, {
2457
- aid: agentId,
2458
- });
2459
-
2460
- return context.with(contextWithAgentId, handler);
2461
- };
2462
-
2463
- const runWithSpan = async <
2464
- T,
2465
- TInput extends StandardSchemaV1 | undefined = any,
2466
- TOutput extends StandardSchemaV1 | undefined = any,
2467
- TStream extends boolean = false,
2468
- >(
2469
- tracer: Tracer,
2470
- agent: Agent<TInput, TOutput, TStream>,
2471
- ctx: Context,
2472
- handler: () => Promise<T>
2473
- ): Promise<T> => {
2474
- const currentContext = context.active();
2475
-
2476
- // Build enriched traceState BEFORE span creation so the recording span
2477
- // inherits it and it gets exported to OTLP. This ensures the agent ID
2478
- // and other metadata appear in ClickHouse TraceState column.
2479
- const deploymentId = runtimeConfig.getDeploymentId();
2480
- const projectId = runtimeConfig.getProjectId();
2481
- const orgId = runtimeConfig.getOrganizationId();
2482
- const isDevMode = runtimeConfig.isDevMode();
2483
-
2484
- const enrichedContext = enrichContextWithTraceState(currentContext, {
2485
- aid: agent.metadata.id,
2486
- did: deploymentId,
2487
- pid: projectId,
2488
- oid: orgId,
2489
- d: isDevMode ? '1' : undefined,
2490
- });
2491
-
2492
- const span = tracer.startSpan('agent.run', {}, enrichedContext);
2493
-
2494
- // Set agent attributes on the span immediately after creation
2495
- span.setAttributes({
2496
- '@agentuity/agentId': agent.metadata.agentId, // stable ID (agent_*) - consistent across deployments
2497
- '@agentuity/agentInstanceId': agent.metadata.id, // deployment-specific ID (agentid_*) - changes per deployment
2498
- '@agentuity/agentDescription': agent.metadata.description,
2499
- '@agentuity/agentName': agent.metadata.name,
2500
- '@agentuity/threadId': ctx.var.thread.id,
2501
- });
2502
-
2503
- const spanId = span.spanContext().spanId;
2504
-
2505
- // Store span ID in PrivateVariables
2506
- const _ctx = privateContext(ctx);
2507
- _ctx.set('agentRunSpanId', spanId);
2508
-
2509
- try {
2510
- // Create a new context with the span active.
2511
- // The span already carries the enriched traceState (inherited from
2512
- // enrichedContext), so downstream API calls via propagation.inject()
2513
- // will see aid/pid/oid/did/d.
2514
- const spanContext = trace.setSpan(currentContext, span);
2515
- return await context.with(spanContext, handler);
2516
- } catch (error) {
2517
- span.recordException(error as Error);
2518
- span.setStatus({ code: SpanStatusCode.ERROR });
2519
- throw error;
2520
- } finally {
2521
- span.end();
2522
- }
2523
- };
2524
-
2525
- const createAgentRunner = <
2526
- TInput extends StandardSchemaV1 | undefined = any,
2527
- TOutput extends StandardSchemaV1 | undefined = any,
2528
- TStream extends boolean = false,
2529
- >(
2530
- agent: Agent<TInput, TOutput, TStream>,
2531
- ctx: Context
2532
- ): AgentRunner<TInput, TOutput, TStream> => {
2533
- const tracer = ctx.var.tracer;
2534
-
2535
- if (agent.inputSchema) {
2536
- return {
2537
- metadata: agent.metadata,
2538
- run: async (input: InferSchemaInput<Exclude<TInput, undefined>>) => {
2539
- return runWithSpan<any, TInput, TOutput, TStream>(
2540
- tracer,
2541
- agent,
2542
- ctx,
2543
- async () => await agent.handler(input)
2544
- );
2545
- },
2546
- } as AgentRunner<TInput, TOutput, TStream>;
2547
- } else {
2548
- return {
2549
- metadata: agent.metadata,
2550
- run: async () => {
2551
- return runWithSpan<any, TInput, TOutput, TStream>(
2552
- tracer,
2553
- agent,
2554
- ctx,
2555
- async () => await agent.handler()
2556
- );
2557
- },
2558
- } as AgentRunner<TInput, TOutput, TStream>;
2559
- }
2560
- };
2561
-
2562
- /**
2563
- * Populate the agents object with all registered agents
2564
- * Keys are converted to camelCase to match the generated TypeScript types
2565
- */
2566
- export const populateAgentsRegistry = (ctx: Context): any => {
2567
- const agentsObj: any = {};
2568
- // Track ownership of camelCase keys to detect collisions between different raw names
2569
- const ownershipMap = new Map<string, string>();
2570
-
2571
- // Build flat registry of agents
2572
- for (const [name, agentFn] of agents) {
2573
- const runner = createAgentRunner(agentFn, ctx);
2574
- const key = toCamelCase(name);
2575
-
2576
- // Validate key is non-empty
2577
- if (!key) {
2578
- internal.warn(`Agent name "${name}" converts to empty camelCase key. Skipping.`);
2579
- continue;
2580
- }
2581
-
2582
- // Detect collision on key - check ownership
2583
- const existingOwner = ownershipMap.get(key);
2584
- if (existingOwner && existingOwner !== name) {
2585
- internal.error(
2586
- `Agent registry collision: "${name}" conflicts with "${existingOwner}" (both map to camelCase key "${key}")`
2587
- );
2588
- throw new Error(`Agent registry collision detected for key "${key}"`);
2589
- }
2590
-
2591
- agentsObj[key] = runner;
2592
- // Record ownership
2593
- ownershipMap.set(key, name);
2594
- }
2595
-
2596
- return agentsObj;
2597
- };
2598
-
2599
- export const createAgentMiddleware = (agentName: AgentName | ''): MiddlewareHandler => {
2600
- return async (ctx, next) => {
2601
- // Populate agents object with strongly-typed keys
2602
- const agentsObj = populateAgentsRegistry(ctx);
2603
-
2604
- // Track agent ID for session telemetry
2605
- if (agentName) {
2606
- const agentKey = toCamelCase(agentName);
2607
- const agent = agentsObj[agentKey];
2608
- const _ctx = privateContext(ctx);
2609
- // we add both so that you can query by either
2610
- if (agent?.metadata?.id) {
2611
- _ctx.var.agentIds.add(agent.metadata.id);
2612
- }
2613
- if (agent?.metadata?.agentId) {
2614
- _ctx.var.agentIds.add(agent.metadata.agentId);
2615
- }
2616
- }
2617
-
2618
- const sessionId = ctx.var.sessionId;
2619
- const thread = ctx.var.thread;
2620
- const session = ctx.var.session;
2621
- const config = agentName ? getAgentConfig(agentName as AgentName) : undefined;
2622
- const app = ctx.var.app;
2623
-
2624
- const args: RequestAgentContextArgs<AgentRegistry, unknown, unknown> = {
2625
- agent: agentsObj,
2626
- logger: ctx.var.logger,
2627
- tracer: ctx.var.tracer,
2628
- sessionId,
2629
- session,
2630
- thread,
2631
- handler: ctx.var.waitUntilHandler,
2632
- config: config || {},
2633
- app: app || {},
2634
- runtime: getGlobalRuntimeState(),
2635
- auth: ctx.var.auth ?? null,
2636
- };
2637
-
2638
- return setupRequestAgentContext(ctx as unknown as Record<string, unknown>, args, next);
2639
- };
2640
- };
2641
-
2642
- export const getAgents = () => agents;
2643
-
2644
- export const runAgentSetups = async (appState: AppState): Promise<void> => {
2645
- for (const [name, agent] of agents.entries()) {
2646
- if (agent.setup) {
2647
- const config = await agent.setup(appState);
2648
- setAgentConfig(name as AgentName, config);
2649
- }
2650
- }
2651
- // Note: Server readiness is managed by Vite (dev) or Bun.serve (prod)
2652
- };
2653
-
2654
- export const runAgentShutdowns = async (appState: AppState): Promise<void> => {
2655
- const runtime = getGlobalRuntimeState();
2656
- for (const [name, agent] of runtime.agents.entries()) {
2657
- if (agent.shutdown) {
2658
- const config = runtime.agentConfigs.get(name) as any;
2659
- await agent.shutdown(appState, config);
2660
- }
2661
- }
2662
- };
2663
-
2664
- /**
2665
- * Run an agent within a specific AgentContext.
2666
- * Sets up AsyncLocalStorage with the provided context and executes the agent.
2667
- *
2668
- * This is the recommended way to test agents in unit tests. It automatically:
2669
- * - Registers the agent in the runtime state so event listeners fire
2670
- * - Sets up AsyncLocalStorage so getAgentContext() works inside the agent
2671
- * - Handles both agents with input and agents without input
2672
- *
2673
- * **Use cases:**
2674
- * - Unit testing agents with TestAgentContext
2675
- * - Running agents outside HTTP request flow
2676
- * - Custom agent execution environments
2677
- * - Testing event listeners and evaluations
2678
- *
2679
- * @template TInput - Type of the input parameter
2680
- * @template TOutput - Type of the return value
2681
- *
2682
- * @param ctx - The AgentContext to use (typically TestAgentContext in tests)
2683
- * @param agent - The AgentRunner to execute (returned from createAgent)
2684
- * @param input - Input data (required if agent has input schema, omit otherwise)
2685
- *
2686
- * @returns Promise resolving to the agent's output
2687
- *
2688
- * @example
2689
- * ```typescript
2690
- * import { runInAgentContext, TestAgentContext } from '@agentuity/runtime/test';
2691
- *
2692
- * test('greeting agent', async () => {
2693
- * const ctx = new TestAgentContext();
2694
- * const result = await runInAgentContext(ctx, greetingAgent, {
2695
- * name: 'Alice',
2696
- * age: 30
2697
- * });
2698
- * expect(result).toBe('Hello, Alice! You are 30 years old.');
2699
- * });
2700
- *
2701
- * test('no-input agent', async () => {
2702
- * const ctx = new TestAgentContext();
2703
- * const result = await runInAgentContext(ctx, statusAgent);
2704
- * expect(result).toEqual({ status: 'ok' });
2705
- * });
2706
- * ```
2707
- */
2708
- export async function runInAgentContext<TInput, TOutput>(
2709
- ctx: AgentContext<any, any, any>,
2710
- agent: AgentRunner<any, any, any>,
2711
- input?: TInput
2712
- ): Promise<TOutput> {
2713
- const storage = getAgentAsyncLocalStorage();
2714
-
2715
- // Register agent in runtime state so events fire (lookup by metadata.name)
2716
- const agentName = agent.metadata.name;
2717
- const runtime = getAgentRuntime(ctx);
2718
-
2719
- // Get internal agent from runner (stored via symbol) or global registry
2720
- const internalAgent = (agent as any)[INTERNAL_AGENT] || agents.get(agentName);
2721
-
2722
- if (internalAgent && agentName) {
2723
- runtime.agents.set(agentName, internalAgent);
2724
-
2725
- // Copy event listeners from global to context runtime
2726
- const globalListeners = agentEventListeners.get(internalAgent);
2727
- if (globalListeners) {
2728
- runtime.agentEventListeners.set(internalAgent, globalListeners);
2729
- }
2730
- }
2731
-
2732
- return storage.run(ctx, async () => {
2733
- if (input !== undefined) {
2734
- return await (agent.run as any)(input);
2735
- } else {
2736
- return await (agent.run as any)();
2737
- }
2738
- });
2739
- }