elasticdash-sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +775 -0
  3. package/dist/browser-ui.d.ts +43 -0
  4. package/dist/browser-ui.d.ts.map +1 -0
  5. package/dist/browser-ui.js +246 -0
  6. package/dist/browser-ui.js.map +1 -0
  7. package/dist/capture/event.d.ts +33 -0
  8. package/dist/capture/event.d.ts.map +1 -0
  9. package/dist/capture/event.js +2 -0
  10. package/dist/capture/event.js.map +1 -0
  11. package/dist/capture/index.d.ts +4 -0
  12. package/dist/capture/index.d.ts.map +1 -0
  13. package/dist/capture/index.js +4 -0
  14. package/dist/capture/index.js.map +1 -0
  15. package/dist/capture/recorder.d.ts +24 -0
  16. package/dist/capture/recorder.d.ts.map +1 -0
  17. package/dist/capture/recorder.js +46 -0
  18. package/dist/capture/recorder.js.map +1 -0
  19. package/dist/capture/replay.d.ts +20 -0
  20. package/dist/capture/replay.d.ts.map +1 -0
  21. package/dist/capture/replay.js +47 -0
  22. package/dist/capture/replay.js.map +1 -0
  23. package/dist/ci/api-client.d.ts +38 -0
  24. package/dist/ci/api-client.d.ts.map +1 -0
  25. package/dist/ci/api-client.js +96 -0
  26. package/dist/ci/api-client.js.map +1 -0
  27. package/dist/ci/benchmark.d.ts +33 -0
  28. package/dist/ci/benchmark.d.ts.map +1 -0
  29. package/dist/ci/benchmark.js +213 -0
  30. package/dist/ci/benchmark.js.map +1 -0
  31. package/dist/ci/ed-runner.d.ts +48 -0
  32. package/dist/ci/ed-runner.d.ts.map +1 -0
  33. package/dist/ci/ed-runner.js +260 -0
  34. package/dist/ci/ed-runner.js.map +1 -0
  35. package/dist/ci/executor.d.ts +13 -0
  36. package/dist/ci/executor.d.ts.map +1 -0
  37. package/dist/ci/executor.js +542 -0
  38. package/dist/ci/executor.js.map +1 -0
  39. package/dist/ci/git-info.d.ts +17 -0
  40. package/dist/ci/git-info.d.ts.map +1 -0
  41. package/dist/ci/git-info.js +102 -0
  42. package/dist/ci/git-info.js.map +1 -0
  43. package/dist/ci/index.d.ts +6 -0
  44. package/dist/ci/index.d.ts.map +1 -0
  45. package/dist/ci/index.js +4 -0
  46. package/dist/ci/index.js.map +1 -0
  47. package/dist/ci/measurement.d.ts +9 -0
  48. package/dist/ci/measurement.d.ts.map +1 -0
  49. package/dist/ci/measurement.js +15 -0
  50. package/dist/ci/measurement.js.map +1 -0
  51. package/dist/ci/replay.d.ts +31 -0
  52. package/dist/ci/replay.d.ts.map +1 -0
  53. package/dist/ci/replay.js +96 -0
  54. package/dist/ci/replay.js.map +1 -0
  55. package/dist/ci/reporters/default.d.ts +8 -0
  56. package/dist/ci/reporters/default.d.ts.map +1 -0
  57. package/dist/ci/reporters/default.js +46 -0
  58. package/dist/ci/reporters/default.js.map +1 -0
  59. package/dist/ci/reporters/index.d.ts +8 -0
  60. package/dist/ci/reporters/index.d.ts.map +1 -0
  61. package/dist/ci/reporters/index.js +14 -0
  62. package/dist/ci/reporters/index.js.map +1 -0
  63. package/dist/ci/reporters/json.d.ts +8 -0
  64. package/dist/ci/reporters/json.d.ts.map +1 -0
  65. package/dist/ci/reporters/json.js +14 -0
  66. package/dist/ci/reporters/json.js.map +1 -0
  67. package/dist/ci/reporters/junit.d.ts +8 -0
  68. package/dist/ci/reporters/junit.d.ts.map +1 -0
  69. package/dist/ci/reporters/junit.js +48 -0
  70. package/dist/ci/reporters/junit.js.map +1 -0
  71. package/dist/ci/runner.d.ts +3 -0
  72. package/dist/ci/runner.d.ts.map +1 -0
  73. package/dist/ci/runner.js +187 -0
  74. package/dist/ci/runner.js.map +1 -0
  75. package/dist/ci/test-discovery.d.ts +5 -0
  76. package/dist/ci/test-discovery.d.ts.map +1 -0
  77. package/dist/ci/test-discovery.js +11 -0
  78. package/dist/ci/test-discovery.js.map +1 -0
  79. package/dist/ci/test-loader.d.ts +19 -0
  80. package/dist/ci/test-loader.d.ts.map +1 -0
  81. package/dist/ci/test-loader.js +149 -0
  82. package/dist/ci/test-loader.js.map +1 -0
  83. package/dist/ci/test-registry.d.ts +42 -0
  84. package/dist/ci/test-registry.d.ts.map +1 -0
  85. package/dist/ci/test-registry.js +18 -0
  86. package/dist/ci/test-registry.js.map +1 -0
  87. package/dist/ci/trace-schema.d.ts +30 -0
  88. package/dist/ci/trace-schema.d.ts.map +1 -0
  89. package/dist/ci/trace-schema.js +66 -0
  90. package/dist/ci/trace-schema.js.map +1 -0
  91. package/dist/ci/trace-writer.d.ts +16 -0
  92. package/dist/ci/trace-writer.d.ts.map +1 -0
  93. package/dist/ci/trace-writer.js +108 -0
  94. package/dist/ci/trace-writer.js.map +1 -0
  95. package/dist/ci/types.d.ts +108 -0
  96. package/dist/ci/types.d.ts.map +1 -0
  97. package/dist/ci/types.js +3 -0
  98. package/dist/ci/types.js.map +1 -0
  99. package/dist/ci/upload-client.d.ts +74 -0
  100. package/dist/ci/upload-client.d.ts.map +1 -0
  101. package/dist/ci/upload-client.js +195 -0
  102. package/dist/ci/upload-client.js.map +1 -0
  103. package/dist/cli.d.ts +3 -0
  104. package/dist/cli.d.ts.map +1 -0
  105. package/dist/cli.js +716 -0
  106. package/dist/cli.js.map +1 -0
  107. package/dist/core/agent-state.d.ts +47 -0
  108. package/dist/core/agent-state.d.ts.map +1 -0
  109. package/dist/core/agent-state.js +137 -0
  110. package/dist/core/agent-state.js.map +1 -0
  111. package/dist/core/judge-utils.d.ts +22 -0
  112. package/dist/core/judge-utils.d.ts.map +1 -0
  113. package/dist/core/judge-utils.js +211 -0
  114. package/dist/core/judge-utils.js.map +1 -0
  115. package/dist/core/registry.d.ts +28 -0
  116. package/dist/core/registry.d.ts.map +1 -0
  117. package/dist/core/registry.js +52 -0
  118. package/dist/core/registry.js.map +1 -0
  119. package/dist/dashboard-server.d.ts +65 -0
  120. package/dist/dashboard-server.d.ts.map +1 -0
  121. package/dist/dashboard-server.js +3940 -0
  122. package/dist/dashboard-server.js.map +1 -0
  123. package/dist/execution/tool-runner.d.ts +26 -0
  124. package/dist/execution/tool-runner.d.ts.map +1 -0
  125. package/dist/execution/tool-runner.js +316 -0
  126. package/dist/execution/tool-runner.js.map +1 -0
  127. package/dist/html/dashboard.html +2218 -0
  128. package/dist/http.d.ts +14 -0
  129. package/dist/http.d.ts.map +1 -0
  130. package/dist/http.js +13 -0
  131. package/dist/http.js.map +1 -0
  132. package/dist/index.cjs +8102 -0
  133. package/dist/index.d.ts +61 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +67 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/interceptors/ai-interceptor.d.ts +26 -0
  138. package/dist/interceptors/ai-interceptor.d.ts.map +1 -0
  139. package/dist/interceptors/ai-interceptor.js +756 -0
  140. package/dist/interceptors/ai-interceptor.js.map +1 -0
  141. package/dist/interceptors/db-auto.d.ts +8 -0
  142. package/dist/interceptors/db-auto.d.ts.map +1 -0
  143. package/dist/interceptors/db-auto.js +217 -0
  144. package/dist/interceptors/db-auto.js.map +1 -0
  145. package/dist/interceptors/db.d.ts +23 -0
  146. package/dist/interceptors/db.d.ts.map +1 -0
  147. package/dist/interceptors/db.js +137 -0
  148. package/dist/interceptors/db.js.map +1 -0
  149. package/dist/interceptors/http.d.ts +28 -0
  150. package/dist/interceptors/http.d.ts.map +1 -0
  151. package/dist/interceptors/http.js +356 -0
  152. package/dist/interceptors/http.js.map +1 -0
  153. package/dist/interceptors/side-effects.d.ts +7 -0
  154. package/dist/interceptors/side-effects.d.ts.map +1 -0
  155. package/dist/interceptors/side-effects.js +72 -0
  156. package/dist/interceptors/side-effects.js.map +1 -0
  157. package/dist/interceptors/telemetry-push.d.ts +142 -0
  158. package/dist/interceptors/telemetry-push.d.ts.map +1 -0
  159. package/dist/interceptors/telemetry-push.js +463 -0
  160. package/dist/interceptors/telemetry-push.js.map +1 -0
  161. package/dist/interceptors/tool.d.ts +2 -0
  162. package/dist/interceptors/tool.d.ts.map +1 -0
  163. package/dist/interceptors/tool.js +274 -0
  164. package/dist/interceptors/tool.js.map +1 -0
  165. package/dist/interceptors/workflow-ai.d.ts +5 -0
  166. package/dist/interceptors/workflow-ai.d.ts.map +1 -0
  167. package/dist/interceptors/workflow-ai.js +382 -0
  168. package/dist/interceptors/workflow-ai.js.map +1 -0
  169. package/dist/internals/conditional-recorder.d.ts +21 -0
  170. package/dist/internals/conditional-recorder.d.ts.map +1 -0
  171. package/dist/internals/conditional-recorder.js +54 -0
  172. package/dist/internals/conditional-recorder.js.map +1 -0
  173. package/dist/internals/mock-resolver.d.ts +146 -0
  174. package/dist/internals/mock-resolver.d.ts.map +1 -0
  175. package/dist/internals/mock-resolver.js +427 -0
  176. package/dist/internals/mock-resolver.js.map +1 -0
  177. package/dist/matchers/index.d.ts +96 -0
  178. package/dist/matchers/index.d.ts.map +1 -0
  179. package/dist/matchers/index.js +668 -0
  180. package/dist/matchers/index.js.map +1 -0
  181. package/dist/observability.d.ts +82 -0
  182. package/dist/observability.d.ts.map +1 -0
  183. package/dist/observability.js +471 -0
  184. package/dist/observability.js.map +1 -0
  185. package/dist/portal-executor.d.ts +30 -0
  186. package/dist/portal-executor.d.ts.map +1 -0
  187. package/dist/portal-executor.js +324 -0
  188. package/dist/portal-executor.js.map +1 -0
  189. package/dist/portal-server.d.ts +3 -0
  190. package/dist/portal-server.d.ts.map +1 -0
  191. package/dist/portal-server.js +279 -0
  192. package/dist/portal-server.js.map +1 -0
  193. package/dist/proxy/llm-capture.d.ts +14 -0
  194. package/dist/proxy/llm-capture.d.ts.map +1 -0
  195. package/dist/proxy/llm-capture.js +264 -0
  196. package/dist/proxy/llm-capture.js.map +1 -0
  197. package/dist/reporter.d.ts +3 -0
  198. package/dist/reporter.d.ts.map +1 -0
  199. package/dist/reporter.js +72 -0
  200. package/dist/reporter.js.map +1 -0
  201. package/dist/runWorkflowSubprocess.d.ts +14 -0
  202. package/dist/runWorkflowSubprocess.d.ts.map +1 -0
  203. package/dist/runWorkflowSubprocess.js +66 -0
  204. package/dist/runWorkflowSubprocess.js.map +1 -0
  205. package/dist/runner.d.ts +16 -0
  206. package/dist/runner.d.ts.map +1 -0
  207. package/dist/runner.js +138 -0
  208. package/dist/runner.js.map +1 -0
  209. package/dist/socket-connector.d.ts +22 -0
  210. package/dist/socket-connector.d.ts.map +1 -0
  211. package/dist/socket-connector.js +104 -0
  212. package/dist/socket-connector.js.map +1 -0
  213. package/dist/telemetry-batcher.d.ts +56 -0
  214. package/dist/telemetry-batcher.d.ts.map +1 -0
  215. package/dist/telemetry-batcher.js +143 -0
  216. package/dist/telemetry-batcher.js.map +1 -0
  217. package/dist/test-setup.d.ts +12 -0
  218. package/dist/test-setup.d.ts.map +1 -0
  219. package/dist/test-setup.js +13 -0
  220. package/dist/test-setup.js.map +1 -0
  221. package/dist/tool-registry.d.ts +31 -0
  222. package/dist/tool-registry.d.ts.map +1 -0
  223. package/dist/tool-registry.js +73 -0
  224. package/dist/tool-registry.js.map +1 -0
  225. package/dist/tool-runner-worker.d.ts +2 -0
  226. package/dist/tool-runner-worker.d.ts.map +1 -0
  227. package/dist/tool-runner-worker.js +215 -0
  228. package/dist/tool-runner-worker.js.map +1 -0
  229. package/dist/trace-adapter/context.d.ts +72 -0
  230. package/dist/trace-adapter/context.d.ts.map +1 -0
  231. package/dist/trace-adapter/context.js +80 -0
  232. package/dist/trace-adapter/context.js.map +1 -0
  233. package/dist/tracing.d.ts +2 -0
  234. package/dist/tracing.d.ts.map +1 -0
  235. package/dist/tracing.js +59 -0
  236. package/dist/tracing.js.map +1 -0
  237. package/dist/trigger-executor.d.ts +12 -0
  238. package/dist/trigger-executor.d.ts.map +1 -0
  239. package/dist/trigger-executor.js +130 -0
  240. package/dist/trigger-executor.js.map +1 -0
  241. package/dist/types/portal.d.ts +76 -0
  242. package/dist/types/portal.d.ts.map +1 -0
  243. package/dist/types/portal.js +2 -0
  244. package/dist/types/portal.js.map +1 -0
  245. package/dist/utils/debug.d.ts +3 -0
  246. package/dist/utils/debug.d.ts.map +1 -0
  247. package/dist/utils/debug.js +8 -0
  248. package/dist/utils/debug.js.map +1 -0
  249. package/dist/utils/license-error.d.ts +23 -0
  250. package/dist/utils/license-error.d.ts.map +1 -0
  251. package/dist/utils/license-error.js +42 -0
  252. package/dist/utils/license-error.js.map +1 -0
  253. package/dist/utils/redact.d.ts +7 -0
  254. package/dist/utils/redact.d.ts.map +1 -0
  255. package/dist/utils/redact.js +26 -0
  256. package/dist/utils/redact.js.map +1 -0
  257. package/dist/workflow-runner-worker.d.ts +2 -0
  258. package/dist/workflow-runner-worker.d.ts.map +1 -0
  259. package/dist/workflow-runner-worker.js +329 -0
  260. package/dist/workflow-runner-worker.js.map +1 -0
  261. package/dist/workflow-runner.d.ts +14 -0
  262. package/dist/workflow-runner.d.ts.map +1 -0
  263. package/dist/workflow-runner.js +34 -0
  264. package/dist/workflow-runner.js.map +1 -0
  265. package/docs/agent-coding-instructions.md +138 -0
  266. package/docs/agent-integration-guide.md +564 -0
  267. package/docs/agents.md +140 -0
  268. package/docs/dashboard.md +394 -0
  269. package/docs/deno.md +69 -0
  270. package/docs/instrumentation.md +424 -0
  271. package/docs/langfuse-trace-structure.md +145 -0
  272. package/docs/matchers.md +173 -0
  273. package/docs/observability_contract.md +192 -0
  274. package/docs/observability_mode.md +195 -0
  275. package/docs/quickstart.md +621 -0
  276. package/docs/security-compliance.md +566 -0
  277. package/docs/test-writing-guidelines.md +444 -0
  278. package/docs/tools.md +165 -0
  279. package/docs/workflow-modes.md +253 -0
  280. package/package.json +76 -0
  281. package/src/browser-ui.ts +281 -0
  282. package/src/capture/event.ts +30 -0
  283. package/src/capture/index.ts +3 -0
  284. package/src/capture/recorder.ts +62 -0
  285. package/src/capture/replay.ts +55 -0
  286. package/src/ci/api-client.ts +136 -0
  287. package/src/ci/benchmark.ts +257 -0
  288. package/src/ci/ed-runner.ts +351 -0
  289. package/src/ci/executor.ts +671 -0
  290. package/src/ci/git-info.ts +127 -0
  291. package/src/ci/index.ts +5 -0
  292. package/src/ci/measurement.ts +25 -0
  293. package/src/ci/replay.ts +127 -0
  294. package/src/ci/reporters/default.ts +50 -0
  295. package/src/ci/reporters/index.ts +21 -0
  296. package/src/ci/reporters/json.ts +18 -0
  297. package/src/ci/reporters/junit.ts +61 -0
  298. package/src/ci/runner.ts +208 -0
  299. package/src/ci/test-discovery.ts +16 -0
  300. package/src/ci/test-loader.ts +187 -0
  301. package/src/ci/test-registry.ts +62 -0
  302. package/src/ci/trace-schema.ts +96 -0
  303. package/src/ci/trace-writer.ts +107 -0
  304. package/src/ci/types.ts +115 -0
  305. package/src/ci/upload-client.ts +300 -0
  306. package/src/cli.ts +811 -0
  307. package/src/core/agent-state.ts +162 -0
  308. package/src/core/judge-utils.ts +232 -0
  309. package/src/core/registry.ts +92 -0
  310. package/src/dashboard-server.ts +2047 -0
  311. package/src/execution/tool-runner.ts +352 -0
  312. package/src/html/dashboard.html +2218 -0
  313. package/src/http.ts +13 -0
  314. package/src/index.ts +138 -0
  315. package/src/interceptors/ai-interceptor.ts +798 -0
  316. package/src/interceptors/db-auto.ts +243 -0
  317. package/src/interceptors/db.ts +156 -0
  318. package/src/interceptors/http.ts +393 -0
  319. package/src/interceptors/side-effects.ts +83 -0
  320. package/src/interceptors/telemetry-push.ts +537 -0
  321. package/src/interceptors/tool.ts +287 -0
  322. package/src/interceptors/workflow-ai.ts +419 -0
  323. package/src/internals/conditional-recorder.ts +63 -0
  324. package/src/internals/mock-resolver.ts +492 -0
  325. package/src/matchers/index.ts +824 -0
  326. package/src/observability.ts +501 -0
  327. package/src/portal-executor.ts +355 -0
  328. package/src/portal-server.ts +304 -0
  329. package/src/proxy/llm-capture.ts +301 -0
  330. package/src/reporter.ts +81 -0
  331. package/src/runWorkflowSubprocess.ts +74 -0
  332. package/src/runner.ts +178 -0
  333. package/src/socket-connector.ts +117 -0
  334. package/src/telemetry-batcher.ts +191 -0
  335. package/src/test-setup.ts +16 -0
  336. package/src/tool-registry.ts +94 -0
  337. package/src/tool-runner-worker.ts +244 -0
  338. package/src/trace-adapter/context.ts +156 -0
  339. package/src/tracing.ts +62 -0
  340. package/src/trigger-executor.ts +171 -0
  341. package/src/types/agent.d.ts +63 -0
  342. package/src/types/expect.d.ts +81 -0
  343. package/src/types/modules.d.ts +2 -0
  344. package/src/types/portal.ts +69 -0
  345. package/src/utils/debug.ts +8 -0
  346. package/src/utils/license-error.ts +43 -0
  347. package/src/utils/redact.ts +25 -0
  348. package/src/workflow-runner-worker.ts +386 -0
  349. package/src/workflow-runner.ts +58 -0
@@ -0,0 +1,621 @@
1
+ # Quick Start Guide
2
+
3
+ Get ElasticDash running in 10 minutes and start debugging your AI workflows.
4
+
5
+ ## Implementation Checklist
6
+
7
+ Use this checklist to ensure your ElasticDash setup and workflow instrumentation are complete. Each item links to the relevant section below:
8
+
9
+ - [ ] [Install the SDK](#install-the-sdk)
10
+ - [ ] [Configure Environment Variables](#configure-environment-variables)
11
+ - [ ] [Create `ed_workflows.ts`](#create-ed_workflowsts)
12
+ - [ ] [Handle Streaming Workflows (if needed)](#streaming-workflows)
13
+ - [ ] [Create `ed_tools.ts`](#create-ed_toolsts)
14
+ - [ ] [Update Workflow Tool Calls](#update-workflow-tool-calls)
15
+ - [ ] [Add Dashboard Script to `package.json`](#add-dashboard-script-to-packagejson)
16
+ - [ ] [Check ESM/CJS Module System](#how-to-check-if-youre-using-esm-or-cjs)
17
+ - [ ] [Write and Run Example Test](#end-to-end-example)
18
+ - [ ] [Open the Dashboard](#open-the-dashboard)
19
+ - [ ] [(Optional) Get Trace Data from Langfuse](#get-trace-data-from-langfuse)
20
+ - [ ] [(Optional) Debug with the Dashboard](#debug-with-the-dashboard)
21
+ - [ ] [(Optional) Capture Streaming Flows](#capture-streaming-flows)
22
+
23
+ ---
24
+
25
+ ## Table of Contents
26
+
27
+ - [Quick Start Guide](#quick-start-guide)
28
+ - [Implementation Checklist](#implementation-checklist)
29
+ - [Table of Contents](#table-of-contents)
30
+ - [Section 1: Installation \& Configuration](#section-1-installation--configuration)
31
+ - [Install the SDK](#install-the-sdk)
32
+ - [Configure Environment Variables](#configure-environment-variables)
33
+ - [Create `ed_workflows.ts`](#create-ed_workflowsts)
34
+ - [Streaming Workflows](#streaming-workflows)
35
+ - [Create `ed_tools.ts`](#create-ed_toolsts)
36
+ - [Update Workflow Tool Calls](#update-workflow-tool-calls)
37
+ - [Add Dashboard Script to `package.json`](#add-dashboard-script-to-packagejson)
38
+ - [Section 2: Usage \& Example](#section-2-usage--example)
39
+ - [End-to-End Example](#end-to-end-example)
40
+ - [Open the Dashboard](#open-the-dashboard)
41
+ - [Get Trace Data from Langfuse](#get-trace-data-from-langfuse)
42
+ - [Debug with the Dashboard](#debug-with-the-dashboard)
43
+ - [Capture Streaming Flows](#capture-streaming-flows)
44
+ - [Next Steps](#next-steps)
45
+
46
+ ---
47
+
48
+ ## Section 1: Installation & Configuration
49
+
50
+ ### Install the SDK
51
+
52
+ ```bash
53
+ npm install elasticdash-sdk
54
+ ```
55
+
56
+ Add `.temp/` to `.gitignore` — ElasticDash writes runtime artifacts there:
57
+
58
+ ```gitignore
59
+ .temp/
60
+ ```
61
+
62
+ ### Configure Environment Variables
63
+
64
+ Set API keys for the LLM providers used in your workflows. Only set keys for providers you actually use.
65
+
66
+ ```bash
67
+ # OpenAI
68
+ export OPENAI_API_KEY="sk-..."
69
+
70
+ # Claude (Anthropic)
71
+ export ANTHROPIC_API_KEY="sk-ant-..."
72
+
73
+ # Gemini (Google)
74
+ export GEMINI_API_KEY="AIzaSy..."
75
+ # or use GOOGLE_API_KEY instead
76
+
77
+ # Grok (xAI)
78
+ export GROK_API_KEY="xai-..."
79
+ ```
80
+
81
+ Supported provider env var names are documented in `docs/matchers.md`.
82
+
83
+ ### Create `ed_workflows.ts`
84
+
85
+ Create `ed_workflows.ts` (or `ed_workflows.js`) in your project root. Export all workflow functions you want to debug:
86
+
87
+ ```ts
88
+ // ed_workflows.ts
89
+ export { checkoutFlow, refundFlow } from './src/workflows'
90
+ export { processOrderFlow } from './src/flows/orders'
91
+ ```
92
+
93
+ **Requirements:** Each exported workflow must be a directly callable async function with JSON-serializable input/output.
94
+
95
+ Valid examples:
96
+
97
+ ```ts
98
+ export async function checkoutFlow(input: { orderId: string }) {
99
+ return { ok: true, orderId: input.orderId }
100
+ }
101
+
102
+ export async function batchFlow(input: Array<{ id: string }>) {
103
+ return input.map((item) => ({ id: item.id, processed: true }))
104
+ }
105
+ ```
106
+
107
+ Not compatible (Next.js route handlers are not directly callable):
108
+
109
+ ```ts
110
+ // ❌ Route handler — wrap in a plain function instead
111
+ export async function POST(req: NextRequest): Promise<NextResponse> {
112
+ return NextResponse.json({ ok: true })
113
+ }
114
+ ```
115
+
116
+ If you use Next.js route handlers, export a separate plain function for workflow replay and call it from your route handler.
117
+
118
+ #### Streaming Workflows
119
+
120
+ If your workflow returns a streaming response (e.g., a Vercel AI SDK data-stream from a Next.js route), you need a wrapper that:
121
+
122
+ 1. Calls the route handler directly (no HTTP server required)
123
+ 2. Parses the stream into a structured result using `readVercelAIStream`
124
+ 3. Records the result with `recordToolCall`
125
+
126
+ Create a separate handler file (e.g., `app/api/chat-stream/chatStreamHandler.ts`) that is **only imported by `ed_workflows.ts`**, never by `route.ts` or any Next.js-bundled file:
127
+
128
+ ```ts
129
+ // app/api/chat-stream/chatStreamHandler.ts
130
+ import { NextRequest } from 'next/server'
131
+ import { readVercelAIStream, recordToolCall } from 'elasticdash-sdk'
132
+ import type { VercelAIStreamResult } from 'elasticdash-sdk'
133
+ import { POST } from './route'
134
+
135
+ export interface ChatStreamInput {
136
+ messages: Array<{ role: string; content: string }>
137
+ sessionId?: string
138
+ userToken?: string
139
+ }
140
+
141
+ export type ChatStreamResult = VercelAIStreamResult
142
+
143
+ export async function chatStreamHandler(args: ChatStreamInput): Promise<ChatStreamResult> {
144
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' }
145
+ if (args.userToken) headers['Authorization'] = `Bearer ${args.userToken}`
146
+
147
+ const req = new NextRequest('http://localhost/api/chat-stream', {
148
+ method: 'POST',
149
+ headers,
150
+ body: JSON.stringify({ messages: args.messages, ...(args.sessionId ? { sessionId: args.sessionId } : {}) }),
151
+ })
152
+
153
+ const response = await POST(req)
154
+
155
+ // If the response is not a data-stream, surface the error
156
+ if (response.headers.get('x-vercel-ai-data-stream') !== 'v1') {
157
+ let errorMessage = `HTTP ${response.status}`
158
+ try {
159
+ const json = await response.clone().json() as Record<string, unknown>
160
+ errorMessage = typeof json.error === 'string' ? json.error : JSON.stringify(json)
161
+ } catch {
162
+ errorMessage = await response.text().catch(() => errorMessage)
163
+ }
164
+ return { message: errorMessage, type: 'error', error: errorMessage }
165
+ }
166
+
167
+ const result = await readVercelAIStream(response)
168
+ recordToolCall('chatStream', args, result)
169
+ return result
170
+ }
171
+ ```
172
+
173
+ Then export it from `ed_workflows.ts`:
174
+
175
+ ```ts
176
+ // ed_workflows.ts
177
+ export { chatStreamHandler } from './app/api/chat-stream/chatStreamHandler'
178
+ ```
179
+
180
+ **Key points:**
181
+
182
+ - `readVercelAIStream` parses the Vercel AI SDK `text/plain` + `x-vercel-ai-data-stream: v1` wire protocol into a structured `VercelAIStreamResult`
183
+ - `recordToolCall` is called manually (via the `ed_tools` file) so that inner tool recordings from the pipeline are not suppressed
184
+ - The handler file imports `elasticdash-sdk` directly — keep it isolated from Next.js bundling by only importing it from `ed_workflows.ts`
185
+
186
+ ### Create `ed_tools.ts`
187
+
188
+ Create `ed_tools.ts` (or `ed_tools.js`) in your project root. Each tool wrapper needs two helpers and a standard pattern:
189
+
190
+ 1. **`resolveMock()`** — checks whether the dashboard has injected mock data for this tool call (supports `mock-all`, `mock-specific`, and `live` modes). Zero-cost no-op outside the worker subprocess.
191
+ 2. **`safeRecordToolCall()`** — records the tool call via `elasticdash-sdk`, but only when running inside the worker subprocess. The dynamic import is guarded so it never blocks your production service.
192
+
193
+ Paste these helpers at the top of `ed_tools.ts`, then wrap each tool using the pattern shown below:
194
+
195
+ ```ts
196
+ // ed_tools.ts
197
+ import { chargeCard as chargeCardImpl } from './src/services/payments'
198
+ import { getOrderDetails as getOrderDetailsImpl } from './src/services/orders'
199
+
200
+ // ---------------------------------------------------------------------------
201
+ // Mock resolution
202
+ // ---------------------------------------------------------------------------
203
+
204
+ /**
205
+ * Checks whether the current call to `toolName` should be short-circuited
206
+ * with mock data. Reads globals written by the elasticdash worker subprocess
207
+ * before the workflow starts.
208
+ *
209
+ * Zero-cost no-op outside the worker: returns { mocked: false } immediately
210
+ * when the globals are absent.
211
+ */
212
+ function resolveMock(toolName: string): { mocked: true; result: unknown } | { mocked: false } {
213
+ const g = globalThis as any
214
+ const mocks = g.__ELASTICDASH_TOOL_MOCKS__
215
+ if (!mocks) return { mocked: false }
216
+
217
+ const entry = mocks[toolName]
218
+ if (!entry || entry.mode === 'live') return { mocked: false }
219
+
220
+ if (!g.__ELASTICDASH_TOOL_CALL_COUNTERS__) g.__ELASTICDASH_TOOL_CALL_COUNTERS__ = {}
221
+ const counters = g.__ELASTICDASH_TOOL_CALL_COUNTERS__
222
+ counters[toolName] = (counters[toolName] ?? 0) + 1
223
+ const callNumber = counters[toolName]
224
+
225
+ if (entry.mode === 'mock-all') {
226
+ const data = entry.mockData ?? {}
227
+ const result = data[callNumber] !== undefined ? data[callNumber] : data[0]
228
+ return { mocked: true, result }
229
+ }
230
+
231
+ if (entry.mode === 'mock-specific') {
232
+ const indices = entry.callIndices ?? []
233
+ if (indices.includes(callNumber)) {
234
+ return { mocked: true, result: (entry.mockData ?? {})[callNumber] }
235
+ }
236
+ return { mocked: false }
237
+ }
238
+
239
+ return { mocked: false }
240
+ }
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // Recording
244
+ // ---------------------------------------------------------------------------
245
+
246
+ /**
247
+ * Records a tool call via elasticdash-sdk when running inside the worker
248
+ * subprocess. Silently skips in all other environments.
249
+ */
250
+ async function safeRecordToolCall(tool: string, input: any, result: any) {
251
+ if (!(globalThis as any).__ELASTICDASH_WORKER__) return
252
+ try {
253
+ const { recordToolCall } = await import('elasticdash-sdk')
254
+ recordToolCall(tool, input, result)
255
+ } catch (err: any) {
256
+ if (err?.code !== 'MODULE_NOT_FOUND') {
257
+ console.error('Logging Error in Tool:', err)
258
+ }
259
+ }
260
+ }
261
+
262
+ // ---------------------------------------------------------------------------
263
+ // Tools
264
+ // ---------------------------------------------------------------------------
265
+
266
+ export const chargeCard = async (input: any) => {
267
+ const mock = resolveMock('chargeCard')
268
+ if (mock.mocked) {
269
+ await safeRecordToolCall('chargeCard', input, mock.result)
270
+ return mock.result
271
+ }
272
+
273
+ return await chargeCardImpl(input)
274
+ .then(async (res: any) => {
275
+ await safeRecordToolCall('chargeCard', input, res)
276
+ return res
277
+ })
278
+ .catch(async (err: any) => {
279
+ await safeRecordToolCall('chargeCard', input, err)
280
+ throw err
281
+ })
282
+ }
283
+
284
+ export const getOrderDetails = async (input: any) => {
285
+ const mock = resolveMock('getOrderDetails')
286
+ if (mock.mocked) {
287
+ await safeRecordToolCall('getOrderDetails', input, mock.result)
288
+ return mock.result
289
+ }
290
+
291
+ return await getOrderDetailsImpl(input)
292
+ .then(async (res: any) => {
293
+ await safeRecordToolCall('getOrderDetails', input, res)
294
+ return res
295
+ })
296
+ .catch(async (err: any) => {
297
+ await safeRecordToolCall('getOrderDetails', input, err)
298
+ throw err
299
+ })
300
+ }
301
+ ```
302
+
303
+ **Pattern for each tool:**
304
+
305
+ 1. Call `resolveMock('toolName')` — if mocked, record and return the mock result immediately
306
+ 2. Otherwise run the real implementation with `.then()` / `.catch()`, recording in both paths
307
+ 3. Always use `safeRecordToolCall` (never import `elasticdash-sdk` directly) to avoid blocking production
308
+
309
+ **Important:** The name string passed to `resolveMock()` and `safeRecordToolCall()` must match the exported function name exactly (e.g., `resolveMock('chargeCard')` for `export const chargeCard`). The dashboard uses this name to identify tools for mocking and trace display.
310
+
311
+ **Next.js projects:** Add `elasticdash-sdk` to `serverExternalPackages` in your `next.config.ts` (or `next.config.js`):
312
+
313
+ ```ts
314
+ // next.config.ts
315
+ const nextConfig = {
316
+ serverExternalPackages: ['elasticdash-sdk'],
317
+ }
318
+ export default nextConfig
319
+ ```
320
+
321
+ This tells Next.js to skip bundling the package into its server-side webpack build and instead load it via Node.js `require()` at runtime. Without this:
322
+
323
+ - The `dynamic import()` in `safeRecordToolCall` would cause webpack to try resolving `elasticdash-sdk` at build time — even though it's an optional dev/test dependency that may not be installed in production
324
+ - Node.js-specific APIs used by the test framework (filesystem, child processes, etc.) are incompatible with webpack bundling
325
+ - By keeping it external, the `catch` block in `safeRecordToolCall` handles `MODULE_NOT_FOUND` silently at runtime instead of failing the build
326
+
327
+ ### Update Workflow Tool Calls
328
+
329
+ Change your workflows to import tools from `ed_tools.ts` instead of the original source files:
330
+
331
+ ```ts
332
+ // ❌ Before
333
+ import { chargeCard } from './services/payments'
334
+ import { getOrderDetails } from './services/orders'
335
+
336
+ export const checkoutFlow = async (orderId: string) => {
337
+ const order = await getOrderDetails({ orderId })
338
+ const payment = await chargeCard({ amount: order.total })
339
+ return { orderId, paymentId: payment.id }
340
+ }
341
+
342
+ // ✅ After
343
+ import { chargeCard, getOrderDetails } from './ed_tools'
344
+
345
+ export const checkoutFlow = async (orderId: string) => {
346
+ const order = await getOrderDetails({ orderId })
347
+ const payment = await chargeCard({ amount: order.total })
348
+ return { orderId, paymentId: payment.id }
349
+ }
350
+ ```
351
+
352
+ ### Add Dashboard Script to `package.json`
353
+
354
+ **Standard setup (most projects):**
355
+
356
+ ```json
357
+ {
358
+ "scripts": {
359
+ "dashboard:ai": "elasticdash dashboard"
360
+ }
361
+ }
362
+ ```
363
+
364
+ **Advanced setup (Next.js, TS path aliases, mixed ESM/CJS):**
365
+
366
+ If your project uses TypeScript path aliases (e.g., `@/lib/...`) or loads TypeScript at runtime with module complexity:
367
+
368
+ ```json
369
+ {
370
+ "scripts": {
371
+ "dashboard:ai": "NODE_OPTIONS='--import tsx/esm --require tsx/cjs --require tsconfig-paths/register' elasticdash dashboard"
372
+ }
373
+ }
374
+ ```
375
+
376
+ <details>
377
+ <summary>When do I need the advanced script?</summary>
378
+
379
+ **Why this works:**
380
+
381
+ - `tsx/esm` and `tsx/cjs` handle mixed ESM/CJS module loading at runtime
382
+ - `tsconfig-paths/register` resolves path aliases from your `tsconfig.json` (e.g., `@/lib` → `./src/lib`)
383
+
384
+
385
+ **How to check if you're using ESM or CJS:**
386
+
387
+ There are two main indicators for your module system:
388
+
389
+ 1. **`package.json`**
390
+ - `"type": "module"` → ESM (Node.js treats `.js` as ESM, uses `import`/`export`)
391
+ - `"type": "commonjs"` or no `type` field → CJS (Node.js treats `.js` as CommonJS, uses `require`/`module.exports`)
392
+
393
+ 2. **`tsconfig.json`** (for TypeScript projects)
394
+ - `"module": "commonjs"` → TypeScript emits CommonJS (`require`/`module.exports`)
395
+ - `"module": "esnext"`, `"es2020"`, etc. → TypeScript emits ESM (`import`/`export`)
396
+ - `"module": "nodenext"` or `"node16"` → Ambiguous; TypeScript output matches the `package.json` `type` field per file
397
+
398
+ **Quick reference table:**
399
+
400
+ | `package.json` `type` | `tsconfig.json` `module` | Resulting Module System |
401
+ |----------------------------|--------------------------|------------------------|
402
+ | `"module"` | `esnext`/`es2020` | ESM |
403
+ | `"module"` | `nodenext`/`node16` | ESM |
404
+ | `"commonjs"` or missing | `commonjs` | CJS |
405
+ | `"commonjs"` or missing | `nodenext`/`node16` | CJS |
406
+
407
+ > **Tip:** If you see `"module": "commonjs"` in your `tsconfig.json`, your project is almost certainly CJS, even if `package.json` has no `type` field.
408
+
409
+ **You need the advanced script when:**
410
+
411
+ - **Any file in your project uses path aliases** like `@/services/payment` instead of relative imports — **this is the main reason**
412
+ - Your project mixes ESM and CJS modules in complex ways
413
+ - You see `Cannot find module '@/...'` or `ERR_UNKNOWN_FILE_EXTENSION` errors
414
+
415
+ **Important:** Path alias resolution is transitive. Even if `ed_workflows.ts` doesn't directly use `@/`, the advanced script is still needed if it imports files that do.
416
+
417
+ **You DON'T need it when:**
418
+
419
+ - Plain JavaScript projects
420
+ - Basic TypeScript projects with only relative imports (`./` or `../`)
421
+ - Pure ESM or pure CJS projects without path aliases
422
+ - Pre-compiled projects where dashboard loads `.js` files
423
+
424
+ </details>
425
+
426
+ That's it for setup. Your project should now have these files:
427
+
428
+ ```
429
+ your-project/
430
+ ed_workflows.ts # workflow exports
431
+ ed_tools.ts # instrumented tool wrappers
432
+ package.json # dashboard script added
433
+ .gitignore # .temp/ added
434
+ ```
435
+
436
+ ---
437
+
438
+ ## Section 2: Usage & Example
439
+
440
+ ### End-to-End Example
441
+
442
+ Here is a complete example showing a workflow, its tools, and how they wire together.
443
+
444
+ **1. Tool implementations** (`src/services/orders.ts`):
445
+
446
+ ```ts
447
+ export async function getOrderDetails(input: { orderId: string }) {
448
+ // Real DB/API call
449
+ return { orderId: input.orderId, total: 49.99, items: ['widget-a'] }
450
+ }
451
+
452
+ export async function chargeCard(input: { amount: number }) {
453
+ // Real payment call
454
+ return { id: 'pay_abc123', amount: input.amount, status: 'succeeded' }
455
+ }
456
+ ```
457
+
458
+ **2. Instrumented tools** (`ed_tools.ts`):
459
+
460
+ ```ts
461
+ import { getOrderDetails as getOrderDetailsImpl } from './src/services/orders'
462
+ import { chargeCard as chargeCardImpl } from './src/services/orders'
463
+
464
+ // resolveMock() and safeRecordToolCall() helpers go here
465
+ // (see full definitions in Section 1 above)
466
+
467
+ export const getOrderDetails = async (input: any) => {
468
+ const mock = resolveMock('getOrderDetails')
469
+ if (mock.mocked) {
470
+ await safeRecordToolCall('getOrderDetails', input, mock.result)
471
+ return mock.result
472
+ }
473
+
474
+ return await getOrderDetailsImpl(input)
475
+ .then(async (res: any) => {
476
+ await safeRecordToolCall('getOrderDetails', input, res)
477
+ return res
478
+ })
479
+ .catch(async (err: any) => {
480
+ await safeRecordToolCall('getOrderDetails', input, err)
481
+ throw err
482
+ })
483
+ }
484
+
485
+ export const chargeCard = async (input: any) => {
486
+ const mock = resolveMock('chargeCard')
487
+ if (mock.mocked) {
488
+ await safeRecordToolCall('chargeCard', input, mock.result)
489
+ return mock.result
490
+ }
491
+
492
+ return await chargeCardImpl(input)
493
+ .then(async (res: any) => {
494
+ await safeRecordToolCall('chargeCard', input, res)
495
+ return res
496
+ })
497
+ .catch(async (err: any) => {
498
+ await safeRecordToolCall('chargeCard', input, err)
499
+ throw err
500
+ })
501
+ }
502
+ ```
503
+
504
+ **3. Workflow** (`ed_workflows.ts`):
505
+
506
+ ```ts
507
+ import { getOrderDetails, chargeCard } from './ed_tools'
508
+
509
+ export async function checkoutFlow(input: { orderId: string }) {
510
+ const order = await getOrderDetails({ orderId: input.orderId })
511
+ // LLM calls via fetch() are intercepted automatically — no wrapping needed
512
+ const payment = await chargeCard({ amount: order.total })
513
+ return { orderId: order.orderId, paymentId: payment.id, status: payment.status }
514
+ }
515
+ ```
516
+
517
+ **4. Test file** (`examples/checkout.ai.test.ts`):
518
+
519
+ ```ts
520
+ import 'elasticdash-sdk/test-setup'
521
+ import { expect } from 'expect'
522
+
523
+ aiTest('checkout flow charges the correct amount', async (ctx) => {
524
+ // Workflow runs; LLM calls and tool calls are recorded into ctx.trace automatically
525
+ const result = await checkoutFlow({ orderId: 'order-42' })
526
+
527
+ // Assert an LLM step occurred
528
+ expect(ctx.trace).toHaveLLMStep({ model: 'gpt-4' })
529
+
530
+ // Assert the chargeCard tool was called
531
+ expect(ctx.trace).toCallTool('chargeCard')
532
+
533
+ // Assert output makes sense semantically
534
+ expect(ctx.trace).toMatchSemanticOutput('payment succeeded')
535
+ })
536
+ ```
537
+
538
+ ### Open the Dashboard
539
+
540
+ ```bash
541
+ npm run dashboard
542
+ ```
543
+
544
+ ### Get Trace Data from Langfuse
545
+
546
+ When a workflow fails in production, fetch the trace to replay it locally.
547
+
548
+ **Note:** When fetching recent observations via the API, you may receive an empty array even though the observations are visible on the Langfuse dashboard. This is expected - the API data lags behind the dashboard. Wait a few minutes and retry before contacting Langfuse support.
549
+
550
+ **Using curl:**
551
+
552
+ ```bash
553
+ export LANGFUSE_PUBLIC_KEY="your_public_key"
554
+ export LANGFUSE_SECRET_KEY="your_secret_key"
555
+ TRACE_ID="your_trace_id"
556
+
557
+ curl "https://cloud.langfuse.com/api/public/v2/observations?traceId=${TRACE_ID}&fields=time,io,basic,model" \
558
+ -H "Authorization: Basic $(echo -n "${LANGFUSE_PUBLIC_KEY}:${LANGFUSE_SECRET_KEY}" | base64)" \
559
+ > trace.json
560
+ ```
561
+
562
+ **Using Postman:**
563
+
564
+ 1. `GET` `https://cloud.langfuse.com/api/public/v2/observations`
565
+ 2. Params: `traceId=<your_trace_id>`, `fields=time,io,basic,model`
566
+ 3. Auth tab: Basic Auth with public key + secret key
567
+ 4. Save the response as `trace.json`
568
+
569
+ ### Debug with the Dashboard
570
+
571
+ Once the dashboard is open:
572
+
573
+ 1. **Select the workflow** — find and click your workflow in the list
574
+ 2. **Upload the trace** — upload `trace.json` and wait for observations to load
575
+ 3. **Identify problematic steps** — review AI/tool observations and mark the step(s) that need fixes
576
+ 4. **Iterate on fixes** — re-run selected step(s) from the dashboard, update your code, re-run again until outputs match expectations
577
+ 5. **Validate end-to-end** — run `Validate with Live Data` to confirm the full workflow produces correct output
578
+
579
+ ### Capture Streaming Flows
580
+
581
+ ElasticDash can capture and replay non-AI HTTP streaming responses (e.g., SSE/NDJSON endpoints) automatically when your workflow uses normal `fetch`.
582
+
583
+ Checklist for streaming workflows:
584
+
585
+ 1. Keep stream requests on standard `fetch` calls so the HTTP interceptor can observe them
586
+ 2. Ensure the upstream response uses a streaming content type:
587
+ - `text/event-stream`
588
+ - `application/x-ndjson`
589
+ - `application/stream+json`
590
+ - `application/jsonl`
591
+ 3. Consume `Response.body` as a stream in your app logic
592
+ 4. Run the workflow once live to record stream payloads
593
+ 5. Re-run with replay to validate deterministic stream-content behavior
594
+
595
+ Minimal consumer example:
596
+
597
+ ```ts
598
+ const response = await fetch('https://example.com/stream')
599
+ if (!response.body) throw new Error('Missing stream body')
600
+
601
+ const reader = response.body.getReader()
602
+ const decoder = new TextDecoder()
603
+ let raw = ''
604
+
605
+ for (;;) {
606
+ const { done, value } = await reader.read()
607
+ if (done) break
608
+ raw += decoder.decode(value, { stream: true })
609
+ }
610
+
611
+ raw += decoder.decode()
612
+ ```
613
+
614
+ **Replay caveat:** ElasticDash preserves payload text and response metadata (status/statusText/headers) but does not preserve original chunk timing or chunk boundaries.
615
+
616
+ ### Next Steps
617
+
618
+ - `docs/tools.md` — advanced tool instrumentation patterns
619
+ - `docs/dashboard.md` — detailed dashboard and Langfuse API usage
620
+ - `docs/matchers.md` — matcher reference and provider env vars
621
+ - `docs/agents.md` — agent-specific replay features