@browserbasehq/orca 3.2.0-preview.3 → 3.2.0-preview.5

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 (313) hide show
  1. package/dist/cjs/lib/inference.d.ts +3 -1
  2. package/dist/cjs/lib/inference.js +2 -2
  3. package/dist/cjs/lib/inference.js.map +1 -1
  4. package/dist/cjs/lib/prompt.d.ts +1 -1
  5. package/dist/cjs/lib/prompt.js +11 -2
  6. package/dist/cjs/lib/prompt.js.map +1 -1
  7. package/dist/cjs/lib/utils.d.ts +1 -0
  8. package/dist/cjs/lib/utils.js +4 -0
  9. package/dist/cjs/lib/utils.js.map +1 -1
  10. package/dist/cjs/lib/v3/agent/AgentClient.d.ts +8 -0
  11. package/dist/cjs/lib/v3/agent/AgentClient.js +13 -0
  12. package/dist/cjs/lib/v3/agent/AgentClient.js.map +1 -1
  13. package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +6 -7
  14. package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
  15. package/dist/cjs/lib/v3/agent/GoogleCUAClient.js +6 -7
  16. package/dist/cjs/lib/v3/agent/GoogleCUAClient.js.map +1 -1
  17. package/dist/cjs/lib/v3/agent/MicrosoftCUAClient.js +1 -0
  18. package/dist/cjs/lib/v3/agent/MicrosoftCUAClient.js.map +1 -1
  19. package/dist/cjs/lib/v3/agent/OpenAICUAClient.d.ts +4 -4
  20. package/dist/cjs/lib/v3/agent/OpenAICUAClient.js +67 -8
  21. package/dist/cjs/lib/v3/agent/OpenAICUAClient.js.map +1 -1
  22. package/dist/cjs/lib/v3/agent/prompts/agentSystemPrompt.d.ts +2 -2
  23. package/dist/cjs/lib/v3/agent/prompts/agentSystemPrompt.js +10 -11
  24. package/dist/cjs/lib/v3/agent/prompts/agentSystemPrompt.js.map +1 -1
  25. package/dist/cjs/lib/v3/agent/tools/act.js +1 -10
  26. package/dist/cjs/lib/v3/agent/tools/act.js.map +1 -1
  27. package/dist/cjs/lib/v3/agent/tools/ariaTree.js +1 -12
  28. package/dist/cjs/lib/v3/agent/tools/ariaTree.js.map +1 -1
  29. package/dist/cjs/lib/v3/agent/tools/braveSearch.js.map +1 -1
  30. package/dist/cjs/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
  31. package/dist/cjs/lib/v3/agent/tools/click.js.map +1 -1
  32. package/dist/cjs/lib/v3/agent/tools/clickAndHold.js.map +1 -1
  33. package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  34. package/dist/cjs/lib/v3/agent/tools/extract.js +1 -10
  35. package/dist/cjs/lib/v3/agent/tools/extract.js.map +1 -1
  36. package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  37. package/dist/cjs/lib/v3/agent/tools/fillform.d.ts +0 -1
  38. package/dist/cjs/lib/v3/agent/tools/fillform.js +8 -20
  39. package/dist/cjs/lib/v3/agent/tools/fillform.js.map +1 -1
  40. package/dist/cjs/lib/v3/agent/tools/index.d.ts +2 -2
  41. package/dist/cjs/lib/v3/agent/tools/index.js +53 -5
  42. package/dist/cjs/lib/v3/agent/tools/index.js.map +1 -1
  43. package/dist/cjs/lib/v3/agent/tools/keys.d.ts +1 -1
  44. package/dist/cjs/lib/v3/agent/tools/keys.js.map +1 -1
  45. package/dist/cjs/lib/v3/agent/tools/type.js.map +1 -1
  46. package/dist/cjs/lib/v3/agent/utils/captchaSolver.d.ts +76 -0
  47. package/dist/cjs/lib/v3/agent/utils/captchaSolver.js +175 -0
  48. package/dist/cjs/lib/v3/agent/utils/captchaSolver.js.map +1 -0
  49. package/dist/cjs/lib/v3/agent/utils/variables.d.ts +5 -0
  50. package/dist/cjs/lib/v3/agent/utils/variables.js +9 -0
  51. package/dist/cjs/lib/v3/agent/utils/variables.js.map +1 -1
  52. package/dist/cjs/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
  53. package/dist/cjs/lib/v3/flowlogger/EventEmitter.js +30 -0
  54. package/dist/cjs/lib/v3/flowlogger/EventEmitter.js.map +1 -0
  55. package/dist/cjs/lib/v3/flowlogger/EventSink.d.ts +44 -0
  56. package/dist/cjs/lib/v3/flowlogger/EventSink.js +217 -0
  57. package/dist/cjs/lib/v3/flowlogger/EventSink.js.map +1 -0
  58. package/dist/cjs/lib/v3/flowlogger/EventStore.d.ts +26 -0
  59. package/dist/cjs/lib/v3/flowlogger/EventStore.js +135 -0
  60. package/dist/cjs/lib/v3/flowlogger/EventStore.js.map +1 -0
  61. package/dist/cjs/lib/v3/flowlogger/FlowLogger.d.ts +99 -0
  62. package/dist/cjs/lib/v3/flowlogger/FlowLogger.js +591 -0
  63. package/dist/cjs/lib/v3/flowlogger/FlowLogger.js.map +1 -0
  64. package/dist/cjs/lib/v3/flowlogger/prettify.d.ts +6 -0
  65. package/dist/cjs/lib/v3/flowlogger/prettify.js +395 -0
  66. package/dist/cjs/lib/v3/flowlogger/prettify.js.map +1 -0
  67. package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js +43 -57
  68. package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
  69. package/dist/cjs/lib/v3/handlers/observeHandler.js +2 -1
  70. package/dist/cjs/lib/v3/handlers/observeHandler.js.map +1 -1
  71. package/dist/cjs/lib/v3/handlers/v3AgentHandler.d.ts +2 -5
  72. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +112 -78
  73. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  74. package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.d.ts +5 -0
  75. package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js +134 -14
  76. package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
  77. package/dist/cjs/lib/v3/llm/aisdk.js +11 -17
  78. package/dist/cjs/lib/v3/llm/aisdk.js.map +1 -1
  79. package/dist/cjs/lib/v3/types/private/cache.d.ts +0 -1
  80. package/dist/cjs/lib/v3/types/private/cache.js.map +1 -1
  81. package/dist/cjs/lib/v3/types/private/handlers.d.ts +1 -0
  82. package/dist/cjs/lib/v3/types/private/handlers.js.map +1 -1
  83. package/dist/cjs/lib/v3/types/public/api.d.ts +24 -7
  84. package/dist/cjs/lib/v3/types/public/api.js +41 -14
  85. package/dist/cjs/lib/v3/types/public/api.js.map +1 -1
  86. package/dist/cjs/lib/v3/types/public/methods.d.ts +1 -0
  87. package/dist/cjs/lib/v3/types/public/methods.js.map +1 -1
  88. package/dist/cjs/lib/v3/types/public/options.d.ts +7 -0
  89. package/dist/cjs/lib/v3/types/public/options.js.map +1 -1
  90. package/dist/cjs/lib/v3/types/public/variables.d.ts +7 -0
  91. package/dist/cjs/lib/v3/types/public/variables.js +22 -0
  92. package/dist/cjs/lib/v3/types/public/variables.js.map +1 -0
  93. package/dist/cjs/lib/v3/understudy/cdp.d.ts +3 -12
  94. package/dist/cjs/lib/v3/understudy/cdp.js +134 -21
  95. package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
  96. package/dist/cjs/lib/v3/understudy/page.js +28 -18
  97. package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
  98. package/dist/cjs/lib/v3/v3.d.ts +12 -2
  99. package/dist/cjs/lib/v3/v3.js +194 -160
  100. package/dist/cjs/lib/v3/v3.js.map +1 -1
  101. package/dist/cjs/lib/version.d.ts +1 -1
  102. package/dist/cjs/lib/version.js +1 -1
  103. package/dist/cjs/lib/version.js.map +1 -1
  104. package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.d.ts +1 -0
  105. package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js +56 -0
  106. package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js.map +1 -0
  107. package/dist/cjs/tests/integration/agent-hybrid-mode.spec.js +6 -6
  108. package/dist/cjs/tests/integration/agent-hybrid-mode.spec.js.map +1 -1
  109. package/dist/cjs/tests/integration/flowLogger.spec.d.ts +1 -0
  110. package/dist/cjs/tests/integration/flowLogger.spec.js +714 -0
  111. package/dist/cjs/tests/integration/flowLogger.spec.js.map +1 -0
  112. package/dist/cjs/tests/integration/testUtils.d.ts +33 -0
  113. package/dist/cjs/tests/integration/testUtils.js +144 -0
  114. package/dist/cjs/tests/integration/testUtils.js.map +1 -1
  115. package/dist/cjs/tests/integration/timeouts.spec.js +113 -3
  116. package/dist/cjs/tests/integration/timeouts.spec.js.map +1 -1
  117. package/dist/cjs/tests/unit/agent-captcha-hooks.test.d.ts +1 -0
  118. package/dist/cjs/tests/unit/agent-captcha-hooks.test.js +285 -0
  119. package/dist/cjs/tests/unit/agent-captcha-hooks.test.js.map +1 -0
  120. package/dist/cjs/tests/unit/agent-execution-model.test.js +25 -3
  121. package/dist/cjs/tests/unit/agent-execution-model.test.js.map +1 -1
  122. package/dist/cjs/tests/unit/agent-system-prompt-variables.test.d.ts +1 -0
  123. package/dist/cjs/tests/unit/agent-system-prompt-variables.test.js +23 -0
  124. package/dist/cjs/tests/unit/agent-system-prompt-variables.test.js.map +1 -0
  125. package/dist/cjs/tests/unit/api-client-observe-variables.test.d.ts +1 -0
  126. package/dist/cjs/tests/unit/api-client-observe-variables.test.js +86 -0
  127. package/dist/cjs/tests/unit/api-client-observe-variables.test.js.map +1 -0
  128. package/dist/cjs/tests/unit/api-variables-schema.test.d.ts +1 -0
  129. package/dist/cjs/tests/unit/api-variables-schema.test.js +37 -0
  130. package/dist/cjs/tests/unit/api-variables-schema.test.js.map +1 -0
  131. package/dist/cjs/tests/unit/browserbase-session-accessors.test.js +20 -0
  132. package/dist/cjs/tests/unit/browserbase-session-accessors.test.js.map +1 -1
  133. package/dist/cjs/tests/unit/captcha-solver.test.d.ts +1 -0
  134. package/dist/cjs/tests/unit/captcha-solver.test.js +154 -0
  135. package/dist/cjs/tests/unit/captcha-solver.test.js.map +1 -0
  136. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
  137. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js +95 -0
  138. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
  139. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
  140. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js +43 -0
  141. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
  142. package/dist/cjs/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
  143. package/dist/cjs/tests/unit/flowlogger-eventstore.test.js +250 -0
  144. package/dist/cjs/tests/unit/flowlogger-eventstore.test.js.map +1 -0
  145. package/dist/cjs/tests/unit/openai-cua-client.test.d.ts +1 -0
  146. package/dist/cjs/tests/unit/openai-cua-client.test.js +71 -0
  147. package/dist/cjs/tests/unit/openai-cua-client.test.js.map +1 -0
  148. package/dist/cjs/tests/unit/prompt-observe-variables.test.d.ts +1 -0
  149. package/dist/cjs/tests/unit/prompt-observe-variables.test.js +19 -0
  150. package/dist/cjs/tests/unit/prompt-observe-variables.test.js.map +1 -0
  151. package/dist/cjs/tests/unit/public-api/public-types.test.js.map +1 -1
  152. package/dist/cjs/tests/unit/timeout-handlers.test.js +50 -0
  153. package/dist/cjs/tests/unit/timeout-handlers.test.js.map +1 -1
  154. package/dist/esm/lib/inference.d.ts +3 -1
  155. package/dist/esm/lib/inference.js +2 -2
  156. package/dist/esm/lib/inference.js.map +1 -1
  157. package/dist/esm/lib/prompt.d.ts +1 -1
  158. package/dist/esm/lib/prompt.js +11 -2
  159. package/dist/esm/lib/prompt.js.map +1 -1
  160. package/dist/esm/lib/utils.d.ts +1 -0
  161. package/dist/esm/lib/utils.js +3 -0
  162. package/dist/esm/lib/utils.js.map +1 -1
  163. package/dist/esm/lib/v3/agent/AgentClient.d.ts +8 -0
  164. package/dist/esm/lib/v3/agent/AgentClient.js +13 -0
  165. package/dist/esm/lib/v3/agent/AgentClient.js.map +1 -1
  166. package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +6 -7
  167. package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
  168. package/dist/esm/lib/v3/agent/GoogleCUAClient.js +6 -7
  169. package/dist/esm/lib/v3/agent/GoogleCUAClient.js.map +1 -1
  170. package/dist/esm/lib/v3/agent/MicrosoftCUAClient.js +1 -0
  171. package/dist/esm/lib/v3/agent/MicrosoftCUAClient.js.map +1 -1
  172. package/dist/esm/lib/v3/agent/OpenAICUAClient.d.ts +4 -4
  173. package/dist/esm/lib/v3/agent/OpenAICUAClient.js +67 -8
  174. package/dist/esm/lib/v3/agent/OpenAICUAClient.js.map +1 -1
  175. package/dist/esm/lib/v3/agent/prompts/agentSystemPrompt.d.ts +2 -2
  176. package/dist/esm/lib/v3/agent/prompts/agentSystemPrompt.js +10 -11
  177. package/dist/esm/lib/v3/agent/prompts/agentSystemPrompt.js.map +1 -1
  178. package/dist/esm/lib/v3/agent/tools/act.js +1 -10
  179. package/dist/esm/lib/v3/agent/tools/act.js.map +1 -1
  180. package/dist/esm/lib/v3/agent/tools/ariaTree.js +1 -12
  181. package/dist/esm/lib/v3/agent/tools/ariaTree.js.map +1 -1
  182. package/dist/esm/lib/v3/agent/tools/braveSearch.js.map +1 -1
  183. package/dist/esm/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
  184. package/dist/esm/lib/v3/agent/tools/click.js.map +1 -1
  185. package/dist/esm/lib/v3/agent/tools/clickAndHold.js.map +1 -1
  186. package/dist/esm/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  187. package/dist/esm/lib/v3/agent/tools/extract.js +1 -10
  188. package/dist/esm/lib/v3/agent/tools/extract.js.map +1 -1
  189. package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  190. package/dist/esm/lib/v3/agent/tools/fillform.d.ts +0 -1
  191. package/dist/esm/lib/v3/agent/tools/fillform.js +8 -20
  192. package/dist/esm/lib/v3/agent/tools/fillform.js.map +1 -1
  193. package/dist/esm/lib/v3/agent/tools/index.d.ts +2 -2
  194. package/dist/esm/lib/v3/agent/tools/index.js +53 -5
  195. package/dist/esm/lib/v3/agent/tools/index.js.map +1 -1
  196. package/dist/esm/lib/v3/agent/tools/keys.d.ts +1 -1
  197. package/dist/esm/lib/v3/agent/tools/keys.js.map +1 -1
  198. package/dist/esm/lib/v3/agent/tools/type.js.map +1 -1
  199. package/dist/esm/lib/v3/agent/utils/captchaSolver.d.ts +76 -0
  200. package/dist/esm/lib/v3/agent/utils/captchaSolver.js +171 -0
  201. package/dist/esm/lib/v3/agent/utils/captchaSolver.js.map +1 -0
  202. package/dist/esm/lib/v3/agent/utils/variables.d.ts +5 -0
  203. package/dist/esm/lib/v3/agent/utils/variables.js +8 -0
  204. package/dist/esm/lib/v3/agent/utils/variables.js.map +1 -1
  205. package/dist/esm/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
  206. package/dist/esm/lib/v3/flowlogger/EventEmitter.js +26 -0
  207. package/dist/esm/lib/v3/flowlogger/EventEmitter.js.map +1 -0
  208. package/dist/esm/lib/v3/flowlogger/EventSink.d.ts +44 -0
  209. package/dist/esm/lib/v3/flowlogger/EventSink.js +206 -0
  210. package/dist/esm/lib/v3/flowlogger/EventSink.js.map +1 -0
  211. package/dist/esm/lib/v3/flowlogger/EventStore.d.ts +26 -0
  212. package/dist/esm/lib/v3/flowlogger/EventStore.js +127 -0
  213. package/dist/esm/lib/v3/flowlogger/EventStore.js.map +1 -0
  214. package/dist/esm/lib/v3/flowlogger/FlowLogger.d.ts +99 -0
  215. package/dist/esm/lib/v3/flowlogger/FlowLogger.js +583 -0
  216. package/dist/esm/lib/v3/flowlogger/FlowLogger.js.map +1 -0
  217. package/dist/esm/lib/v3/flowlogger/prettify.d.ts +6 -0
  218. package/dist/esm/lib/v3/flowlogger/prettify.js +389 -0
  219. package/dist/esm/lib/v3/flowlogger/prettify.js.map +1 -0
  220. package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js +43 -57
  221. package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
  222. package/dist/esm/lib/v3/handlers/observeHandler.js +2 -1
  223. package/dist/esm/lib/v3/handlers/observeHandler.js.map +1 -1
  224. package/dist/esm/lib/v3/handlers/v3AgentHandler.d.ts +2 -5
  225. package/dist/esm/lib/v3/handlers/v3AgentHandler.js +112 -78
  226. package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  227. package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.d.ts +5 -0
  228. package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js +134 -14
  229. package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
  230. package/dist/esm/lib/v3/llm/aisdk.js +11 -17
  231. package/dist/esm/lib/v3/llm/aisdk.js.map +1 -1
  232. package/dist/esm/lib/v3/types/private/cache.d.ts +0 -1
  233. package/dist/esm/lib/v3/types/private/cache.js.map +1 -1
  234. package/dist/esm/lib/v3/types/private/handlers.d.ts +1 -0
  235. package/dist/esm/lib/v3/types/private/handlers.js.map +1 -1
  236. package/dist/esm/lib/v3/types/public/api.d.ts +24 -7
  237. package/dist/esm/lib/v3/types/public/api.js +36 -12
  238. package/dist/esm/lib/v3/types/public/api.js.map +1 -1
  239. package/dist/esm/lib/v3/types/public/methods.d.ts +1 -0
  240. package/dist/esm/lib/v3/types/public/methods.js.map +1 -1
  241. package/dist/esm/lib/v3/types/public/options.d.ts +7 -0
  242. package/dist/esm/lib/v3/types/public/options.js.map +1 -1
  243. package/dist/esm/lib/v3/types/public/variables.d.ts +7 -0
  244. package/dist/esm/lib/v3/types/public/variables.js +19 -0
  245. package/dist/esm/lib/v3/types/public/variables.js.map +1 -0
  246. package/dist/esm/lib/v3/understudy/cdp.d.ts +3 -12
  247. package/dist/esm/lib/v3/understudy/cdp.js +134 -21
  248. package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
  249. package/dist/esm/lib/v3/understudy/page.js +28 -18
  250. package/dist/esm/lib/v3/understudy/page.js.map +1 -1
  251. package/dist/esm/lib/v3/v3.d.ts +12 -2
  252. package/dist/esm/lib/v3/v3.js +194 -160
  253. package/dist/esm/lib/v3/v3.js.map +1 -1
  254. package/dist/esm/lib/version.d.ts +1 -1
  255. package/dist/esm/lib/version.js +1 -1
  256. package/dist/esm/lib/version.js.map +1 -1
  257. package/dist/esm/tests/integration/agent-captcha-autosolve.spec.d.ts +1 -0
  258. package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js +54 -0
  259. package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js.map +1 -0
  260. package/dist/esm/tests/integration/agent-hybrid-mode.spec.js +6 -6
  261. package/dist/esm/tests/integration/agent-hybrid-mode.spec.js.map +1 -1
  262. package/dist/esm/tests/integration/flowLogger.spec.d.ts +1 -0
  263. package/dist/esm/tests/integration/flowLogger.spec.js +712 -0
  264. package/dist/esm/tests/integration/flowLogger.spec.js.map +1 -0
  265. package/dist/esm/tests/integration/testUtils.d.ts +33 -0
  266. package/dist/esm/tests/integration/testUtils.js +138 -0
  267. package/dist/esm/tests/integration/testUtils.js.map +1 -1
  268. package/dist/esm/tests/integration/timeouts.spec.js +113 -3
  269. package/dist/esm/tests/integration/timeouts.spec.js.map +1 -1
  270. package/dist/esm/tests/unit/agent-captcha-hooks.test.d.ts +1 -0
  271. package/dist/esm/tests/unit/agent-captcha-hooks.test.js +283 -0
  272. package/dist/esm/tests/unit/agent-captcha-hooks.test.js.map +1 -0
  273. package/dist/esm/tests/unit/agent-execution-model.test.js +25 -3
  274. package/dist/esm/tests/unit/agent-execution-model.test.js.map +1 -1
  275. package/dist/esm/tests/unit/agent-system-prompt-variables.test.d.ts +1 -0
  276. package/dist/esm/tests/unit/agent-system-prompt-variables.test.js +21 -0
  277. package/dist/esm/tests/unit/agent-system-prompt-variables.test.js.map +1 -0
  278. package/dist/esm/tests/unit/api-client-observe-variables.test.d.ts +1 -0
  279. package/dist/esm/tests/unit/api-client-observe-variables.test.js +84 -0
  280. package/dist/esm/tests/unit/api-client-observe-variables.test.js.map +1 -0
  281. package/dist/esm/tests/unit/api-variables-schema.test.d.ts +1 -0
  282. package/dist/esm/tests/unit/api-variables-schema.test.js +35 -0
  283. package/dist/esm/tests/unit/api-variables-schema.test.js.map +1 -0
  284. package/dist/esm/tests/unit/browserbase-session-accessors.test.js +20 -0
  285. package/dist/esm/tests/unit/browserbase-session-accessors.test.js.map +1 -1
  286. package/dist/esm/tests/unit/captcha-solver.test.d.ts +1 -0
  287. package/dist/esm/tests/unit/captcha-solver.test.js +152 -0
  288. package/dist/esm/tests/unit/captcha-solver.test.js.map +1 -0
  289. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
  290. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js +93 -0
  291. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
  292. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
  293. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js +41 -0
  294. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
  295. package/dist/esm/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
  296. package/dist/esm/tests/unit/flowlogger-eventstore.test.js +248 -0
  297. package/dist/esm/tests/unit/flowlogger-eventstore.test.js.map +1 -0
  298. package/dist/esm/tests/unit/openai-cua-client.test.d.ts +1 -0
  299. package/dist/esm/tests/unit/openai-cua-client.test.js +69 -0
  300. package/dist/esm/tests/unit/openai-cua-client.test.js.map +1 -0
  301. package/dist/esm/tests/unit/prompt-observe-variables.test.d.ts +1 -0
  302. package/dist/esm/tests/unit/prompt-observe-variables.test.js +17 -0
  303. package/dist/esm/tests/unit/prompt-observe-variables.test.js.map +1 -0
  304. package/dist/esm/tests/unit/public-api/public-types.test.js.map +1 -1
  305. package/dist/esm/tests/unit/timeout-handlers.test.js +50 -0
  306. package/dist/esm/tests/unit/timeout-handlers.test.js.map +1 -1
  307. package/package.json +4 -2
  308. package/dist/cjs/lib/v3/flowLogger.d.ts +0 -139
  309. package/dist/cjs/lib/v3/flowLogger.js +0 -881
  310. package/dist/cjs/lib/v3/flowLogger.js.map +0 -1
  311. package/dist/esm/lib/v3/flowLogger.d.ts +0 -139
  312. package/dist/esm/lib/v3/flowLogger.js +0 -868
  313. package/dist/esm/lib/v3/flowLogger.js.map +0 -1
@@ -0,0 +1,171 @@
1
+ const SOLVING_STARTED = "browserbase-solving-started";
2
+ const SOLVING_FINISHED = "browserbase-solving-finished";
3
+ const SOLVING_ERRORED = "browserbase-solving-errored";
4
+ /** Maximum time (ms) to wait for the captcha solver before giving up. */
5
+ const SOLVE_TIMEOUT_MS = 90_000;
6
+ // ---------------------------------------------------------------------------
7
+ // Shared captcha notification strings
8
+ // ---------------------------------------------------------------------------
9
+ /** Injected into the agent message stream after a successful captcha solve. */
10
+ export const CAPTCHA_SOLVED_MSG = "A captcha was automatically detected and solved — no further interaction with the captcha is needed, even if it does not visually appear solved. Do not click the captcha checkbox, widget, or challenge again. Continue with your task.";
11
+ /** Injected into the agent message stream when the captcha solver fails. */
12
+ export const CAPTCHA_ERRORED_MSG = "A captcha was detected but the automatic captcha solver failed to solve it. You may need to try a different approach or navigate around the captcha.";
13
+ /** Appended to the system prompt (DOM/hybrid agents) when captchas auto-solve. */
14
+ export const CAPTCHA_SYSTEM_PROMPT_NOTE = "Captchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself — they will be handled for you. Do not click the captcha checkbox, widget, or challenge again after it has been solved, even if it still looks unresolved. Continue with your task as if the captcha does not exist.";
15
+ /** Appended to the CUA system prompt when captchas auto-solve. */
16
+ export const CAPTCHA_CUA_SYSTEM_PROMPT_NOTE = "\n\nCaptchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself — they will be handled for you. Continue with your task as if the captcha does not exist.";
17
+ /**
18
+ * Tracks Browserbase captcha solver state via console messages and provides
19
+ * a blocking `waitIfSolving()` that agents call before each step/action.
20
+ *
21
+ * Accepts a page-provider callback so the listener is automatically
22
+ * re-attached when the active page changes (e.g. popup / new tab).
23
+ *
24
+ * All concurrent callers of `waitIfSolving()` share the same underlying
25
+ * promise, so multiple waiters are safely resolved together.
26
+ */
27
+ export class CaptchaSolver {
28
+ solving = false;
29
+ _solvedSinceLastConsume = false;
30
+ _erroredSinceLastConsume = false;
31
+ listener = null;
32
+ attachedPage = null;
33
+ pageProvider = null;
34
+ /** Shared promise that all concurrent waitIfSolving() callers await. */
35
+ waitPromise = null;
36
+ /** Resolves the shared waitPromise. */
37
+ resolveWait = null;
38
+ /** Timeout handle for the 90s deadline. */
39
+ waitTimer = null;
40
+ /**
41
+ * Initialise with a callback that returns the current active page.
42
+ * The listener is lazily (re-)attached whenever the active page changes.
43
+ */
44
+ init(pageProvider) {
45
+ this.pageProvider = pageProvider;
46
+ }
47
+ /** Whether a captcha solve is currently in progress. */
48
+ isSolving() {
49
+ return this.solving;
50
+ }
51
+ /**
52
+ * Ensure the console listener is attached to the current active page.
53
+ * If the active page has changed since the last call, the old listener
54
+ * is removed and a new one is installed.
55
+ */
56
+ async ensureAttached() {
57
+ if (!this.pageProvider)
58
+ return;
59
+ const page = await this.pageProvider();
60
+ if (page === this.attachedPage)
61
+ return;
62
+ // Detach from the old page
63
+ this.detachListener();
64
+ this.attachedPage = page;
65
+ this.listener = (msg) => {
66
+ const text = msg.text();
67
+ if (text === SOLVING_STARTED) {
68
+ this.solving = true;
69
+ }
70
+ else if (text === SOLVING_FINISHED) {
71
+ this.solving = false;
72
+ this._solvedSinceLastConsume = true;
73
+ this.settle();
74
+ }
75
+ else if (text === SOLVING_ERRORED) {
76
+ this.solving = false;
77
+ this._erroredSinceLastConsume = true;
78
+ this.settle();
79
+ }
80
+ };
81
+ page.on("console", this.listener);
82
+ }
83
+ /**
84
+ * Returns a promise that resolves immediately if no captcha is being
85
+ * solved, or blocks until the solver finishes, errors, or the 90s
86
+ * timeout is reached.
87
+ *
88
+ * Also re-attaches the listener to the current active page if it has
89
+ * changed since the last call.
90
+ *
91
+ * All concurrent callers share the same promise, so no waiter is
92
+ * orphaned.
93
+ */
94
+ async waitIfSolving() {
95
+ await this.ensureAttached();
96
+ if (!this.solving)
97
+ return;
98
+ // Return the existing shared promise if one is already pending
99
+ if (this.waitPromise)
100
+ return this.waitPromise;
101
+ this.waitPromise = new Promise((resolve) => {
102
+ this.resolveWait = resolve;
103
+ this.waitTimer = setTimeout(() => {
104
+ this.solving = false;
105
+ this._erroredSinceLastConsume = true;
106
+ this.settle();
107
+ }, SOLVE_TIMEOUT_MS);
108
+ });
109
+ return this.waitPromise;
110
+ }
111
+ /**
112
+ * Returns and resets the solve event flags.
113
+ * Call after `waitIfSolving()` to check whether a captcha was solved
114
+ * (or errored) since the last consume. This captures events even if
115
+ * the solve completed between two `waitIfSolving()` calls.
116
+ */
117
+ consumeSolveResult() {
118
+ const result = {
119
+ solved: this._solvedSinceLastConsume,
120
+ errored: this._erroredSinceLastConsume,
121
+ };
122
+ this._solvedSinceLastConsume = false;
123
+ this._erroredSinceLastConsume = false;
124
+ return result;
125
+ }
126
+ /**
127
+ * Remove the console listener and reset all state.
128
+ */
129
+ dispose() {
130
+ this.detachListener();
131
+ this.attachedPage = null;
132
+ this.pageProvider = null;
133
+ this.solving = false;
134
+ this._solvedSinceLastConsume = false;
135
+ this._erroredSinceLastConsume = false;
136
+ this.settle();
137
+ }
138
+ // ------------------------------------------------------------------
139
+ // Internal helpers
140
+ // ------------------------------------------------------------------
141
+ /** Remove the console listener from the currently attached page. */
142
+ detachListener() {
143
+ if (this.attachedPage && this.listener) {
144
+ this.attachedPage.off("console", this.listener);
145
+ }
146
+ this.listener = null;
147
+ // If a solve was in progress, mark it as errored so consumers
148
+ // know it was interrupted (consistent with the timeout path).
149
+ if (this.solving) {
150
+ this._erroredSinceLastConsume = true;
151
+ }
152
+ // Reset solving state so waiters aren't stuck waiting for events
153
+ // that can never arrive from the detached page.
154
+ this.solving = false;
155
+ this.settle();
156
+ }
157
+ /** Resolve the shared wait promise and clear the timeout. */
158
+ settle() {
159
+ if (this.waitTimer) {
160
+ clearTimeout(this.waitTimer);
161
+ this.waitTimer = null;
162
+ }
163
+ if (this.resolveWait) {
164
+ const resolve = this.resolveWait;
165
+ this.resolveWait = null;
166
+ this.waitPromise = null;
167
+ resolve();
168
+ }
169
+ }
170
+ }
171
+ //# sourceMappingURL=captchaSolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captchaSolver.js","sourceRoot":"","sources":["../../../../../../lib/v3/agent/utils/captchaSolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Page } from \"../../understudy/page.js\";\nimport type { ConsoleMessage } from \"../../understudy/consoleMessage.js\";\n\nconst SOLVING_STARTED = \"browserbase-solving-started\";\nconst SOLVING_FINISHED = \"browserbase-solving-finished\";\nconst SOLVING_ERRORED = \"browserbase-solving-errored\";\n\n/** Maximum time (ms) to wait for the captcha solver before giving up. */\nconst SOLVE_TIMEOUT_MS = 90_000;\n\n// ---------------------------------------------------------------------------\n// Shared captcha notification strings\n// ---------------------------------------------------------------------------\n\n/** Injected into the agent message stream after a successful captcha solve. */\nexport const CAPTCHA_SOLVED_MSG =\n \"A captcha was automatically detected and solved — no further interaction with the captcha is needed, even if it does not visually appear solved. Do not click the captcha checkbox, widget, or challenge again. Continue with your task.\";\n\n/** Injected into the agent message stream when the captcha solver fails. */\nexport const CAPTCHA_ERRORED_MSG =\n \"A captcha was detected but the automatic captcha solver failed to solve it. You may need to try a different approach or navigate around the captcha.\";\n\n/** Appended to the system prompt (DOM/hybrid agents) when captchas auto-solve. */\nexport const CAPTCHA_SYSTEM_PROMPT_NOTE =\n \"Captchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself — they will be handled for you. Do not click the captcha checkbox, widget, or challenge again after it has been solved, even if it still looks unresolved. Continue with your task as if the captcha does not exist.\";\n\n/** Appended to the CUA system prompt when captchas auto-solve. */\nexport const CAPTCHA_CUA_SYSTEM_PROMPT_NOTE =\n \"\\n\\nCaptchas on this page are automatically detected and solved by the browser environment. Do not interact with or attempt to solve any captchas yourself — they will be handled for you. Continue with your task as if the captcha does not exist.\";\n\n/**\n * Tracks Browserbase captcha solver state via console messages and provides\n * a blocking `waitIfSolving()` that agents call before each step/action.\n *\n * Accepts a page-provider callback so the listener is automatically\n * re-attached when the active page changes (e.g. popup / new tab).\n *\n * All concurrent callers of `waitIfSolving()` share the same underlying\n * promise, so multiple waiters are safely resolved together.\n */\nexport class CaptchaSolver {\n private solving = false;\n private _solvedSinceLastConsume = false;\n private _erroredSinceLastConsume = false;\n private listener: ((msg: ConsoleMessage) => void) | null = null;\n private attachedPage: Page | null = null;\n private pageProvider: (() => Promise<Page>) | null = null;\n\n /** Shared promise that all concurrent waitIfSolving() callers await. */\n private waitPromise: Promise<void> | null = null;\n /** Resolves the shared waitPromise. */\n private resolveWait: (() => void) | null = null;\n /** Timeout handle for the 90s deadline. */\n private waitTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Initialise with a callback that returns the current active page.\n * The listener is lazily (re-)attached whenever the active page changes.\n */\n init(pageProvider: () => Promise<Page>): void {\n this.pageProvider = pageProvider;\n }\n\n /** Whether a captcha solve is currently in progress. */\n isSolving(): boolean {\n return this.solving;\n }\n\n /**\n * Ensure the console listener is attached to the current active page.\n * If the active page has changed since the last call, the old listener\n * is removed and a new one is installed.\n */\n async ensureAttached(): Promise<void> {\n if (!this.pageProvider) return;\n const page = await this.pageProvider();\n if (page === this.attachedPage) return;\n\n // Detach from the old page\n this.detachListener();\n\n this.attachedPage = page;\n this.listener = (msg: ConsoleMessage) => {\n const text = msg.text();\n if (text === SOLVING_STARTED) {\n this.solving = true;\n } else if (text === SOLVING_FINISHED) {\n this.solving = false;\n this._solvedSinceLastConsume = true;\n this.settle();\n } else if (text === SOLVING_ERRORED) {\n this.solving = false;\n this._erroredSinceLastConsume = true;\n this.settle();\n }\n };\n page.on(\"console\", this.listener);\n }\n\n /**\n * Returns a promise that resolves immediately if no captcha is being\n * solved, or blocks until the solver finishes, errors, or the 90s\n * timeout is reached.\n *\n * Also re-attaches the listener to the current active page if it has\n * changed since the last call.\n *\n * All concurrent callers share the same promise, so no waiter is\n * orphaned.\n */\n async waitIfSolving(): Promise<void> {\n await this.ensureAttached();\n\n if (!this.solving) return;\n\n // Return the existing shared promise if one is already pending\n if (this.waitPromise) return this.waitPromise;\n\n this.waitPromise = new Promise<void>((resolve) => {\n this.resolveWait = resolve;\n this.waitTimer = setTimeout(() => {\n this.solving = false;\n this._erroredSinceLastConsume = true;\n this.settle();\n }, SOLVE_TIMEOUT_MS);\n });\n\n return this.waitPromise;\n }\n\n /**\n * Returns and resets the solve event flags.\n * Call after `waitIfSolving()` to check whether a captcha was solved\n * (or errored) since the last consume. This captures events even if\n * the solve completed between two `waitIfSolving()` calls.\n */\n consumeSolveResult(): { solved: boolean; errored: boolean } {\n const result = {\n solved: this._solvedSinceLastConsume,\n errored: this._erroredSinceLastConsume,\n };\n this._solvedSinceLastConsume = false;\n this._erroredSinceLastConsume = false;\n return result;\n }\n\n /**\n * Remove the console listener and reset all state.\n */\n dispose(): void {\n this.detachListener();\n this.attachedPage = null;\n this.pageProvider = null;\n this.solving = false;\n this._solvedSinceLastConsume = false;\n this._erroredSinceLastConsume = false;\n this.settle();\n }\n\n // ------------------------------------------------------------------\n // Internal helpers\n // ------------------------------------------------------------------\n\n /** Remove the console listener from the currently attached page. */\n private detachListener(): void {\n if (this.attachedPage && this.listener) {\n this.attachedPage.off(\"console\", this.listener);\n }\n this.listener = null;\n // If a solve was in progress, mark it as errored so consumers\n // know it was interrupted (consistent with the timeout path).\n if (this.solving) {\n this._erroredSinceLastConsume = true;\n }\n // Reset solving state so waiters aren't stuck waiting for events\n // that can never arrive from the detached page.\n this.solving = false;\n this.settle();\n }\n\n /** Resolve the shared wait promise and clear the timeout. */\n private settle(): void {\n if (this.waitTimer) {\n clearTimeout(this.waitTimer);\n this.waitTimer = null;\n }\n if (this.resolveWait) {\n const resolve = this.resolveWait;\n this.resolveWait = null;\n this.waitPromise = null;\n resolve();\n }\n }\n}\n"]}
@@ -9,6 +9,11 @@ export declare function resolveVariableValue(v: VariableValue): string;
9
9
  * Returns undefined for simple primitive values.
10
10
  */
11
11
  export declare function getVariableDescription(v: VariableValue): string | undefined;
12
+ export interface VariablePromptEntry {
13
+ name: string;
14
+ description?: string;
15
+ }
16
+ export declare function getVariablePromptEntries(variables?: Variables): VariablePromptEntry[];
12
17
  /**
13
18
  * Substitutes %variableName% tokens in text with resolved variable values.
14
19
  * Works with both simple and rich variable formats.
@@ -18,6 +18,14 @@ export function getVariableDescription(v) {
18
18
  }
19
19
  return undefined;
20
20
  }
21
+ export function getVariablePromptEntries(variables) {
22
+ if (!variables)
23
+ return [];
24
+ return Object.entries(variables).map(([name, value]) => ({
25
+ name,
26
+ description: getVariableDescription(value),
27
+ }));
28
+ }
21
29
  /**
22
30
  * Substitutes %variableName% tokens in text with resolved variable values.
23
31
  * Works with both simple and rich variable formats.
@@ -1 +1 @@
1
- {"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../../../../lib/v3/agent/utils/variables.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,CAAgB;IACnD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,CAAgB;IACrD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,CAAC,WAAW,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,SAAqB;IAErB,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAqB;IAErB,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { Variables, VariableValue } from \"../../types/public/agent.js\";\n\n/**\n * Resolves a VariableValue to its primitive string value.\n * Handles both simple primitives (\"secret\") and rich objects ({ value: \"secret\", description: \"...\" }).\n */\nexport function resolveVariableValue(v: VariableValue): string {\n if (typeof v === \"object\" && v !== null && \"value\" in v) {\n return String(v.value);\n }\n return String(v);\n}\n\n/**\n * Extracts the optional description from a VariableValue.\n * Returns undefined for simple primitive values.\n */\nexport function getVariableDescription(v: VariableValue): string | undefined {\n if (typeof v === \"object\" && v !== null && \"value\" in v) {\n return v.description;\n }\n return undefined;\n}\n\n/**\n * Substitutes %variableName% tokens in text with resolved variable values.\n * Works with both simple and rich variable formats.\n */\nexport function substituteVariables(\n text: string,\n variables?: Variables,\n): string {\n if (!variables) return text;\n let result = text;\n for (const [key, v] of Object.entries(variables)) {\n const token = `%${key}%`;\n result = result.split(token).join(resolveVariableValue(v));\n }\n return result;\n}\n\n/**\n * Flattens Variables to Record<string, string> for internal consumers\n * that only need key→value mappings (e.g., actHandler, cache replay).\n */\nexport function flattenVariables(\n variables?: Variables,\n): Record<string, string> | undefined {\n if (!variables || Object.keys(variables).length === 0) return undefined;\n const result: Record<string, string> = {};\n for (const [key, v] of Object.entries(variables)) {\n result[key] = resolveVariableValue(v);\n }\n return result;\n}\n"]}
1
+ {"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../../../../lib/v3/agent/utils/variables.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,CAAgB;IACnD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACxD,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,CAAgB;IACrD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,CAAC,WAAW,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAOD,MAAM,UAAU,wBAAwB,CACtC,SAAqB;IAErB,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvD,IAAI;QACJ,WAAW,EAAE,sBAAsB,CAAC,KAAK,CAAC;KAC3C,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,SAAqB;IAErB,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAqB;IAErB,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { Variables, VariableValue } from \"../../types/public/agent.js\";\n\n/**\n * Resolves a VariableValue to its primitive string value.\n * Handles both simple primitives (\"secret\") and rich objects ({ value: \"secret\", description: \"...\" }).\n */\nexport function resolveVariableValue(v: VariableValue): string {\n if (typeof v === \"object\" && v !== null && \"value\" in v) {\n return String(v.value);\n }\n return String(v);\n}\n\n/**\n * Extracts the optional description from a VariableValue.\n * Returns undefined for simple primitive values.\n */\nexport function getVariableDescription(v: VariableValue): string | undefined {\n if (typeof v === \"object\" && v !== null && \"value\" in v) {\n return v.description;\n }\n return undefined;\n}\n\nexport interface VariablePromptEntry {\n name: string;\n description?: string;\n}\n\nexport function getVariablePromptEntries(\n variables?: Variables,\n): VariablePromptEntry[] {\n if (!variables) return [];\n return Object.entries(variables).map(([name, value]) => ({\n name,\n description: getVariableDescription(value),\n }));\n}\n\n/**\n * Substitutes %variableName% tokens in text with resolved variable values.\n * Works with both simple and rich variable formats.\n */\nexport function substituteVariables(\n text: string,\n variables?: Variables,\n): string {\n if (!variables) return text;\n let result = text;\n for (const [key, v] of Object.entries(variables)) {\n const token = `%${key}%`;\n result = result.split(token).join(resolveVariableValue(v));\n }\n return result;\n}\n\n/**\n * Flattens Variables to Record<string, string> for internal consumers\n * that only need key→value mappings (e.g., actHandler, cache replay).\n */\nexport function flattenVariables(\n variables?: Variables,\n): Record<string, string> | undefined {\n if (!variables || Object.keys(variables).length === 0) return undefined;\n const result: Record<string, string> = {};\n for (const [key, v] of Object.entries(variables)) {\n result[key] = resolveVariableValue(v);\n }\n return result;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import { EventEmitter } from "node:events";
2
+ export declare class EventEmitterWithWildcardSupport extends EventEmitter {
3
+ private readonly wildcardListeners;
4
+ on(eventName: string | symbol, listener: (...args: unknown[]) => void): this;
5
+ off(eventName: string | symbol, listener: (...args: unknown[]) => void): this;
6
+ emit(eventName: string | symbol, ...args: unknown[]): boolean;
7
+ }
@@ -0,0 +1,26 @@
1
+ import { EventEmitter } from "node:events";
2
+ export class EventEmitterWithWildcardSupport extends EventEmitter {
3
+ wildcardListeners = new Set();
4
+ on(eventName, listener) {
5
+ if (eventName === "*") {
6
+ this.wildcardListeners.add(listener);
7
+ return this;
8
+ }
9
+ return super.on(eventName, listener);
10
+ }
11
+ off(eventName, listener) {
12
+ if (eventName === "*") {
13
+ this.wildcardListeners.delete(listener);
14
+ return this;
15
+ }
16
+ return super.off(eventName, listener);
17
+ }
18
+ emit(eventName, ...args) {
19
+ const handled = super.emit(eventName, ...args);
20
+ for (const listener of this.wildcardListeners) {
21
+ listener(...args);
22
+ }
23
+ return handled || this.wildcardListeners.size > 0;
24
+ }
25
+ }
26
+ //# sourceMappingURL=EventEmitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventEmitter.js","sourceRoot":"","sources":["../../../../../lib/v3/flowlogger/EventEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,OAAO,+BAAgC,SAAQ,YAAY;IAC9C,iBAAiB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE7D,EAAE,CACT,SAA0B,EAC1B,QAAsC;QAEtC,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAEQ,GAAG,CACV,SAA0B,EAC1B,QAAsC;QAEtC,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACxC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAEQ,IAAI,CAAC,SAA0B,EAAE,GAAG,IAAe;QAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAAC;QAE/C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC9C,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,OAAO,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,CAAC;IACpD,CAAC;CACF","sourcesContent":["import { EventEmitter } from \"node:events\";\n\ntype WildcardEventListener = (...args: unknown[]) => void;\n\nexport class EventEmitterWithWildcardSupport extends EventEmitter {\n private readonly wildcardListeners = new Set<WildcardEventListener>();\n\n override on(\n eventName: string | symbol,\n listener: (...args: unknown[]) => void,\n ): this {\n if (eventName === \"*\") {\n this.wildcardListeners.add(listener);\n return this;\n }\n\n return super.on(eventName, listener);\n }\n\n override off(\n eventName: string | symbol,\n listener: (...args: unknown[]) => void,\n ): this {\n if (eventName === \"*\") {\n this.wildcardListeners.delete(listener);\n return this;\n }\n\n return super.off(eventName, listener);\n }\n\n override emit(eventName: string | symbol, ...args: unknown[]): boolean {\n const handled = super.emit(eventName, ...args);\n\n for (const listener of this.wildcardListeners) {\n listener(...args);\n }\n\n return handled || this.wildcardListeners.size > 0;\n }\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { FlowEvent } from "./FlowLogger.js";
2
+ import type { EventStoreApi, EventStoreQuery } from "./EventStore.js";
3
+ export interface EventSink {
4
+ emit(event: FlowEvent): Promise<void>;
5
+ query(query: EventStoreQuery): Promise<FlowEvent[]>;
6
+ destroy(): Promise<void>;
7
+ }
8
+ declare abstract class FileEventSink implements EventSink {
9
+ private readonly streamPromise;
10
+ constructor(sessionDirPromise: Promise<string | null>, fileName: string);
11
+ protected abstract serialize(event: FlowEvent): Promise<string | null>;
12
+ emit(event: FlowEvent): Promise<void>;
13
+ query(): Promise<FlowEvent[]>;
14
+ destroy(): Promise<void>;
15
+ }
16
+ export declare class JsonlFileEventSink extends FileEventSink {
17
+ constructor(sessionDirPromise: Promise<string | null>);
18
+ protected serialize(event: FlowEvent): Promise<string>;
19
+ }
20
+ export declare class PrettyLogFileEventSink extends FileEventSink {
21
+ private readonly store;
22
+ constructor(sessionDirPromise: Promise<string | null>, store: Pick<EventStoreApi, "query">);
23
+ protected serialize(event: FlowEvent): Promise<string | null>;
24
+ }
25
+ export declare class PrettyStderrEventSink implements EventSink {
26
+ private readonly store;
27
+ constructor(store: Pick<EventStoreApi, "query">);
28
+ emit(event: FlowEvent): Promise<void>;
29
+ query(): Promise<FlowEvent[]>;
30
+ destroy(): Promise<void>;
31
+ }
32
+ export declare class InMemoryEventSink implements EventSink {
33
+ protected readonly limit: number;
34
+ constructor(limit?: number);
35
+ protected readonly events: FlowEvent[];
36
+ protected storeEvent(event: FlowEvent): FlowEvent;
37
+ emit(event: FlowEvent): Promise<void>;
38
+ query(query: EventStoreQuery): Promise<FlowEvent[]>;
39
+ destroy(): Promise<void>;
40
+ }
41
+ export declare class ShallowInMemoryEventSink extends InMemoryEventSink {
42
+ protected storeEvent(event: FlowEvent): FlowEvent;
43
+ }
44
+ export {};
@@ -0,0 +1,206 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { FlowEvent } from "./FlowLogger.js";
4
+ import { prettifyColorStderrLine, prettifyEvent, prettifyIsCdpEvent, prettifySanitizeEvent, } from "./prettify.js";
5
+ // Checks whether an event matches a query used by queryable sinks. `eventId` matches both the event itself and descendants of that event.
6
+ function matchesEventStoreQuery(event, query) {
7
+ if (query.sessionId && event.sessionId !== query.sessionId)
8
+ return false;
9
+ if (query.eventId) {
10
+ const matchesEvent = event.eventId === query.eventId ||
11
+ event.eventParentIds.includes(query.eventId);
12
+ if (!matchesEvent) {
13
+ return false;
14
+ }
15
+ }
16
+ if (query.eventType) {
17
+ const pattern = new RegExp(`^${query.eventType
18
+ .replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
19
+ .replace(/\\\*/g, ".*")}$`);
20
+ if (!pattern.test(event.eventType)) {
21
+ return false;
22
+ }
23
+ }
24
+ return true;
25
+ }
26
+ // =============================================================================
27
+ // File Sink Helpers
28
+ // =============================================================================
29
+ // Returns true when a file sink's stream is still open and writable.
30
+ function isWritable(stream) {
31
+ return !!(stream && !stream.destroyed && stream.writable);
32
+ }
33
+ // Writes a serialized event to a file sink and converts callback-style stream completion into a promise.
34
+ function writeToStream(stream, value) {
35
+ return new Promise((resolve, reject) => {
36
+ try {
37
+ stream.write(value, (error) => {
38
+ if (error) {
39
+ reject(error);
40
+ return;
41
+ }
42
+ resolve();
43
+ });
44
+ }
45
+ catch (error) {
46
+ reject(error);
47
+ }
48
+ });
49
+ }
50
+ // =============================================================================
51
+ // Event Sink Implementations
52
+ // =============================================================================
53
+ class FileEventSink {
54
+ streamPromise; // Lazily opens the one file stream owned by this sink when the session directory resolves.
55
+ // Creates a best-effort file sink bound to a single session directory.
56
+ constructor(sessionDirPromise, fileName) {
57
+ this.streamPromise = sessionDirPromise.then((sessionDir) => sessionDir
58
+ ? fs.createWriteStream(path.join(sessionDir, fileName), { flags: "a" })
59
+ : null);
60
+ }
61
+ // Serializes and appends a single event. File sinks are intentionally best-effort and never allowed to affect library execution flow.
62
+ async emit(event) {
63
+ try {
64
+ const stream = await this.streamPromise;
65
+ if (!isWritable(stream)) {
66
+ return;
67
+ }
68
+ const serialized = await this.serialize(event);
69
+ if (!serialized) {
70
+ return;
71
+ }
72
+ await writeToStream(stream, serialized);
73
+ }
74
+ catch {
75
+ // best effort only
76
+ }
77
+ }
78
+ // File sinks are write-only and do not support query reads.
79
+ async query() {
80
+ return [];
81
+ }
82
+ // Closes the underlying file stream when the owning store shuts down.
83
+ async destroy() {
84
+ const stream = await this.streamPromise.catch(() => null);
85
+ if (!isWritable(stream)) {
86
+ return;
87
+ }
88
+ await new Promise((resolve) => {
89
+ stream.end(resolve);
90
+ });
91
+ }
92
+ }
93
+ export class JsonlFileEventSink extends FileEventSink {
94
+ // Writes full verbatim events to `session_events.jsonl`.
95
+ constructor(sessionDirPromise) {
96
+ super(sessionDirPromise, "session_events.jsonl");
97
+ }
98
+ // Serializes the full event for lossless machine-readable storage.
99
+ async serialize(event) {
100
+ return `${JSON.stringify(event)}\n`;
101
+ }
102
+ }
103
+ export class PrettyLogFileEventSink extends FileEventSink {
104
+ store;
105
+ // Writes human-readable pretty lines to `session_events.log`.
106
+ constructor(sessionDirPromise, store) {
107
+ super(sessionDirPromise, "session_events.log");
108
+ this.store = store;
109
+ }
110
+ // Pretty-prints the event using recent in-memory ancestry.
111
+ async serialize(event) {
112
+ const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));
113
+ return line ? `${line}\n` : null;
114
+ }
115
+ }
116
+ export class PrettyStderrEventSink {
117
+ store;
118
+ // Writes pretty lines to stderr for verbose local debugging. CDP events are intentionally omitted here to keep stderr high-signal.
119
+ constructor(store) {
120
+ this.store = store;
121
+ } // Queried during prettification so stderr lines can include recent ancestry tags.
122
+ // Best-effort stderr writer used only for interactive debugging output.
123
+ async emit(event) {
124
+ try {
125
+ if (prettifyIsCdpEvent(event)) {
126
+ return;
127
+ }
128
+ const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));
129
+ if (!line) {
130
+ return;
131
+ }
132
+ await new Promise((resolve, reject) => {
133
+ try {
134
+ process.stderr.write(`${prettifyColorStderrLine(line)}\n`, (error) => {
135
+ if (error) {
136
+ reject(error);
137
+ return;
138
+ }
139
+ resolve();
140
+ });
141
+ }
142
+ catch (error) {
143
+ reject(error);
144
+ }
145
+ });
146
+ }
147
+ catch {
148
+ // best effort only
149
+ }
150
+ }
151
+ // Stderr sink is write-only and does not support query reads.
152
+ async query() {
153
+ return [];
154
+ }
155
+ // No teardown is required for stderr.
156
+ async destroy() { }
157
+ }
158
+ export class InMemoryEventSink {
159
+ limit;
160
+ // Retains recent events for query lookups. Tests usually attach this sink explicitly when they need full historical payloads.
161
+ constructor(limit = Infinity) {
162
+ this.limit = limit;
163
+ }
164
+ events = []; // Retained history; `emit()` appends to it and trims old entries when `limit` is exceeded.
165
+ // Gives subclasses a hook to transform events before they are retained.
166
+ storeEvent(event) {
167
+ return event;
168
+ }
169
+ // Stores a new event and trims the oldest retained entries once the sink exceeds its configured limit.
170
+ async emit(event) {
171
+ this.events.push(this.storeEvent(event));
172
+ if (this.events.length > this.limit) {
173
+ this.events.splice(0, this.events.length - this.limit);
174
+ }
175
+ }
176
+ // Returns retained events that match the query, ordered by creation time.
177
+ async query(query) {
178
+ const filtered = this.events.filter((event) => matchesEventStoreQuery(event, query));
179
+ filtered.sort((left, right) => {
180
+ const createdAtOrder = left.eventCreatedAt.localeCompare(right.eventCreatedAt);
181
+ if (createdAtOrder !== 0) {
182
+ return createdAtOrder;
183
+ }
184
+ return left.eventId.localeCompare(right.eventId);
185
+ });
186
+ return query.limit ? filtered.slice(-query.limit) : filtered;
187
+ }
188
+ // Clears retained history when the owning store shuts down.
189
+ async destroy() {
190
+ this.events.length = 0;
191
+ }
192
+ }
193
+ export class ShallowInMemoryEventSink extends InMemoryEventSink {
194
+ // Retains only ancestry metadata for the default query sink so verbose or long-running sessions do not hold onto large payloads such as screenshots.
195
+ storeEvent(event) {
196
+ return new FlowEvent({
197
+ eventType: event.eventType,
198
+ eventId: event.eventId,
199
+ eventCreatedAt: event.eventCreatedAt,
200
+ sessionId: event.sessionId,
201
+ eventParentIds: [...event.eventParentIds],
202
+ data: {},
203
+ });
204
+ }
205
+ }
206
+ //# sourceMappingURL=EventSink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventSink.js","sourceRoot":"","sources":["../../../../../lib/v3/flowlogger/EventSink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAYvB,0IAA0I;AAC1I,SAAS,sBAAsB,CAC7B,KAAgB,EAChB,KAAsB;IAEtB,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAEzE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,YAAY,GAChB,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;YAC/B,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,IAAI,MAAM,CACxB,IAAI,KAAK,CAAC,SAAS;aAChB,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC;aACtC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAC7B,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF,qEAAqE;AACrE,SAAS,UAAU,CAAC,MAA6B;IAC/C,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC5D,CAAC;AAED,yGAAyG;AACzG,SAAS,aAAa,CAAC,MAAsB,EAAE,KAAa;IAC1D,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAoB,EAAE,EAAE;gBAC3C,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,KAAK,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAEhF,MAAe,aAAa;IACT,aAAa,CAAiC,CAAC,2FAA2F;IAE3J,uEAAuE;IACvE,YAAY,iBAAyC,EAAE,QAAgB;QACrE,IAAI,CAAC,aAAa,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CACzD,UAAU;YACR,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YACvE,CAAC,CAAC,IAAI,CACT,CAAC;IACJ,CAAC;IAID,sIAAsI;IACtI,KAAK,CAAC,IAAI,CAAC,KAAgB;QACzB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC;YACxC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxB,OAAO;YACT,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC/C,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,MAAM,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB;QACrB,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,KAAK;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,OAAO;QACX,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAS,EAAE,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IACnD,yDAAyD;IACzD,YAAY,iBAAyC;QACnD,KAAK,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAC;IACnD,CAAC;IAED,mEAAmE;IACzD,KAAK,CAAC,SAAS,CAAC,KAAgB;QACxC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,aAAa;IAIpC;IAHnB,8DAA8D;IAC9D,YACE,iBAAyC,EACxB,KAAmC;QAEpD,KAAK,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;QAF9B,UAAK,GAAL,KAAK,CAA8B;IAGtD,CAAC;IAED,2DAA2D;IACjD,KAAK,CAAC,SAAS,CAAC,KAAgB;QACxC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,CAAC;CACF;AAED,MAAM,OAAO,qBAAqB;IAEH;IAD7B,mIAAmI;IACnI,YAA6B,KAAmC;QAAnC,UAAK,GAAL,KAAK,CAA8B;IAAG,CAAC,CAAC,kFAAkF;IAEvJ,wEAAwE;IACxE,KAAK,CAAC,IAAI,CAAC,KAAgB;QACzB,IAAI,CAAC;YACH,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,aAAa,CAC9B,IAAI,CAAC,KAAK,EACV,qBAAqB,CAAC,KAAK,CAAC,CAC7B,CAAC;YACF,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,IAAI,CAAC;oBACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,EACpC,CAAC,KAAoB,EAAE,EAAE;wBACvB,IAAI,KAAK,EAAE,CAAC;4BACV,MAAM,CAAC,KAAK,CAAC,CAAC;4BACd,OAAO;wBACT,CAAC;wBACD,OAAO,EAAE,CAAC;oBACZ,CAAC,CACF,CAAC;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB;QACrB,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,KAAK;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,OAAO,KAAmB,CAAC;CAClC;AAED,MAAM,OAAO,iBAAiB;IAEG;IAD/B,8HAA8H;IAC9H,YAA+B,QAAQ,QAAQ;QAAhB,UAAK,GAAL,KAAK,CAAW;IAAG,CAAC;IAEhC,MAAM,GAAgB,EAAE,CAAC,CAAC,2FAA2F;IAExI,wEAAwE;IAC9D,UAAU,CAAC,KAAgB;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uGAAuG;IACvG,KAAK,CAAC,IAAI,CAAC,KAAgB;QACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,KAAK,CAAC,KAAsB;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC5C,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,CACrC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC5B,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,aAAa,CACtD,KAAK,CAAC,cAAc,CACrB,CAAC;YACF,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,cAAc,CAAC;YACxB,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC/D,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,iBAAiB;IAC7D,qJAAqJ;IAClI,UAAU,CAAC,KAAgB;QAC5C,OAAO,IAAI,SAAS,CAAC;YACnB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,cAAc,EAAE,CAAC,GAAG,KAAK,CAAC,cAAc,CAAC;YACzC,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;IACL,CAAC;CACF","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { FlowEvent } from \"./FlowLogger.js\";\nimport type { EventStoreApi, EventStoreQuery } from \"./EventStore.js\";\nimport {\n prettifyColorStderrLine,\n prettifyEvent,\n prettifyIsCdpEvent,\n prettifySanitizeEvent,\n} from \"./prettify.js\";\n\n// =============================================================================\n// Event Sink Contracts\n// =============================================================================\n\nexport interface EventSink {\n emit(event: FlowEvent): Promise<void>;\n query(query: EventStoreQuery): Promise<FlowEvent[]>;\n destroy(): Promise<void>;\n}\n\n// Checks whether an event matches a query used by queryable sinks. `eventId` matches both the event itself and descendants of that event.\nfunction matchesEventStoreQuery(\n event: FlowEvent,\n query: EventStoreQuery,\n): boolean {\n if (query.sessionId && event.sessionId !== query.sessionId) return false;\n\n if (query.eventId) {\n const matchesEvent =\n event.eventId === query.eventId ||\n event.eventParentIds.includes(query.eventId);\n if (!matchesEvent) {\n return false;\n }\n }\n\n if (query.eventType) {\n const pattern = new RegExp(\n `^${query.eventType\n .replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\\\\\*/g, \".*\")}$`,\n );\n if (!pattern.test(event.eventType)) {\n return false;\n }\n }\n\n return true;\n}\n\n// =============================================================================\n// File Sink Helpers\n// =============================================================================\n\n// Returns true when a file sink's stream is still open and writable.\nfunction isWritable(stream: fs.WriteStream | null): stream is fs.WriteStream {\n return !!(stream && !stream.destroyed && stream.writable);\n}\n\n// Writes a serialized event to a file sink and converts callback-style stream completion into a promise.\nfunction writeToStream(stream: fs.WriteStream, value: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n try {\n stream.write(value, (error?: Error | null) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n } catch (error) {\n reject(error);\n }\n });\n}\n\n// =============================================================================\n// Event Sink Implementations\n// =============================================================================\n\nabstract class FileEventSink implements EventSink {\n private readonly streamPromise: Promise<fs.WriteStream | null>; // Lazily opens the one file stream owned by this sink when the session directory resolves.\n\n // Creates a best-effort file sink bound to a single session directory.\n constructor(sessionDirPromise: Promise<string | null>, fileName: string) {\n this.streamPromise = sessionDirPromise.then((sessionDir) =>\n sessionDir\n ? fs.createWriteStream(path.join(sessionDir, fileName), { flags: \"a\" })\n : null,\n );\n }\n\n protected abstract serialize(event: FlowEvent): Promise<string | null>;\n\n // Serializes and appends a single event. File sinks are intentionally best-effort and never allowed to affect library execution flow.\n async emit(event: FlowEvent): Promise<void> {\n try {\n const stream = await this.streamPromise;\n if (!isWritable(stream)) {\n return;\n }\n\n const serialized = await this.serialize(event);\n if (!serialized) {\n return;\n }\n\n await writeToStream(stream, serialized);\n } catch {\n // best effort only\n }\n }\n\n // File sinks are write-only and do not support query reads.\n async query(): Promise<FlowEvent[]> {\n return [];\n }\n\n // Closes the underlying file stream when the owning store shuts down.\n async destroy(): Promise<void> {\n const stream = await this.streamPromise.catch((): null => null);\n if (!isWritable(stream)) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n stream.end(resolve);\n });\n }\n}\n\nexport class JsonlFileEventSink extends FileEventSink {\n // Writes full verbatim events to `session_events.jsonl`.\n constructor(sessionDirPromise: Promise<string | null>) {\n super(sessionDirPromise, \"session_events.jsonl\");\n }\n\n // Serializes the full event for lossless machine-readable storage.\n protected async serialize(event: FlowEvent): Promise<string> {\n return `${JSON.stringify(event)}\\n`;\n }\n}\n\nexport class PrettyLogFileEventSink extends FileEventSink {\n // Writes human-readable pretty lines to `session_events.log`.\n constructor(\n sessionDirPromise: Promise<string | null>,\n private readonly store: Pick<EventStoreApi, \"query\">, // Queried during prettification so each line can recover recent ancestry tags.\n ) {\n super(sessionDirPromise, \"session_events.log\");\n }\n\n // Pretty-prints the event using recent in-memory ancestry.\n protected async serialize(event: FlowEvent): Promise<string | null> {\n const line = await prettifyEvent(this.store, prettifySanitizeEvent(event));\n return line ? `${line}\\n` : null;\n }\n}\n\nexport class PrettyStderrEventSink implements EventSink {\n // Writes pretty lines to stderr for verbose local debugging. CDP events are intentionally omitted here to keep stderr high-signal.\n constructor(private readonly store: Pick<EventStoreApi, \"query\">) {} // Queried during prettification so stderr lines can include recent ancestry tags.\n\n // Best-effort stderr writer used only for interactive debugging output.\n async emit(event: FlowEvent): Promise<void> {\n try {\n if (prettifyIsCdpEvent(event)) {\n return;\n }\n\n const line = await prettifyEvent(\n this.store,\n prettifySanitizeEvent(event),\n );\n if (!line) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n try {\n process.stderr.write(\n `${prettifyColorStderrLine(line)}\\n`,\n (error?: Error | null) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n },\n );\n } catch (error) {\n reject(error);\n }\n });\n } catch {\n // best effort only\n }\n }\n\n // Stderr sink is write-only and does not support query reads.\n async query(): Promise<FlowEvent[]> {\n return [];\n }\n\n // No teardown is required for stderr.\n async destroy(): Promise<void> {}\n}\n\nexport class InMemoryEventSink implements EventSink {\n // Retains recent events for query lookups. Tests usually attach this sink explicitly when they need full historical payloads.\n constructor(protected readonly limit = Infinity) {}\n\n protected readonly events: FlowEvent[] = []; // Retained history; `emit()` appends to it and trims old entries when `limit` is exceeded.\n\n // Gives subclasses a hook to transform events before they are retained.\n protected storeEvent(event: FlowEvent): FlowEvent {\n return event;\n }\n\n // Stores a new event and trims the oldest retained entries once the sink exceeds its configured limit.\n async emit(event: FlowEvent): Promise<void> {\n this.events.push(this.storeEvent(event));\n if (this.events.length > this.limit) {\n this.events.splice(0, this.events.length - this.limit);\n }\n }\n\n // Returns retained events that match the query, ordered by creation time.\n async query(query: EventStoreQuery): Promise<FlowEvent[]> {\n const filtered = this.events.filter((event) =>\n matchesEventStoreQuery(event, query),\n );\n filtered.sort((left, right) => {\n const createdAtOrder = left.eventCreatedAt.localeCompare(\n right.eventCreatedAt,\n );\n if (createdAtOrder !== 0) {\n return createdAtOrder;\n }\n\n return left.eventId.localeCompare(right.eventId);\n });\n return query.limit ? filtered.slice(-query.limit) : filtered;\n }\n\n // Clears retained history when the owning store shuts down.\n async destroy(): Promise<void> {\n this.events.length = 0;\n }\n}\n\nexport class ShallowInMemoryEventSink extends InMemoryEventSink {\n // Retains only ancestry metadata for the default query sink so verbose or long-running sessions do not hold onto large payloads such as screenshots.\n protected override storeEvent(event: FlowEvent): FlowEvent {\n return new FlowEvent({\n eventType: event.eventType,\n eventId: event.eventId,\n eventCreatedAt: event.eventCreatedAt,\n sessionId: event.sessionId,\n eventParentIds: [...event.eventParentIds],\n data: {},\n });\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { V3Options } from "../types/public/index.js";
2
+ import { EventSink } from "./EventSink.js";
3
+ import { FlowEvent } from "./FlowLogger.js";
4
+ export interface EventStoreQuery {
5
+ sessionId?: string;
6
+ eventId?: string;
7
+ eventType?: string;
8
+ limit?: number;
9
+ }
10
+ export interface EventStoreApi {
11
+ readonly sessionId: string;
12
+ emit(event: FlowEvent): Promise<void>;
13
+ query(query: EventStoreQuery): Promise<FlowEvent[]>;
14
+ destroy(): Promise<void>;
15
+ }
16
+ export declare function getConfigDir(): string;
17
+ export declare class EventStore implements EventStoreApi {
18
+ readonly sessionId: string;
19
+ private readonly sinks;
20
+ private destroyed;
21
+ query: (query: EventStoreQuery) => Promise<FlowEvent[]>;
22
+ constructor(sessionId: string, options?: V3Options, querySink?: EventSink);
23
+ private registerSink;
24
+ emit: (event: FlowEvent) => Promise<void>;
25
+ destroy(): Promise<void>;
26
+ }