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,243 @@
1
+ import { getCaptureContext } from '../capture/recorder.js'
2
+ import { getCurrentTrace } from '../trace-adapter/context.js'
3
+ import { rawDateNow } from './side-effects.js'
4
+ import { getHttpRunContext, getHttpFrozenEvent, pushTelemetryEvent, tryAutoInitHttpContext, getObservabilityContext } from './telemetry-push.js'
5
+
6
+ type AnyFn = (...args: unknown[]) => unknown
7
+
8
+ interface MethodPatch {
9
+ proto: Record<string, unknown>
10
+ method: string
11
+ original: AnyFn
12
+ }
13
+
14
+ const appliedPatches: MethodPatch[] = []
15
+
16
+ function toTraceArgs(input: unknown): Record<string, unknown> | undefined {
17
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
18
+ return input as Record<string, unknown>
19
+ }
20
+ if (input === undefined) return undefined
21
+ return { value: input }
22
+ }
23
+
24
+ function wrapProtoMethod(proto: object, method: string, eventName: string): void {
25
+ const p = proto as Record<string, unknown>
26
+ if (typeof p[method] !== 'function') return
27
+
28
+ const original = p[method] as AnyFn
29
+ appliedPatches.push({ proto: p, method, original })
30
+
31
+ p[method] = function (this: unknown, ...args: unknown[]) {
32
+ // Skip callback-style calls to avoid breaking legacy APIs
33
+ if (args.length > 0 && typeof args[args.length - 1] === 'function') {
34
+ return original.apply(this, args)
35
+ }
36
+
37
+ const ctx = getCaptureContext()
38
+ const httpCtx = getHttpRunContext()
39
+ const obsCtx = getObservabilityContext()
40
+ const input = args.length === 1 ? args[0] : args
41
+
42
+ if (!ctx && !httpCtx && !obsCtx) return original.apply(this, args)
43
+
44
+ // Observability-only mode: record and push, no mocks/replay
45
+ if (!ctx && !httpCtx && obsCtx) {
46
+ const id = obsCtx.nextId()
47
+ const start = rawDateNow()
48
+
49
+ let result: unknown
50
+ try {
51
+ result = original.apply(this, args)
52
+ } catch (err) {
53
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
54
+ throw err
55
+ }
56
+
57
+ if (result != null && typeof (result as Promise<unknown>).then === 'function') {
58
+ return (result as Promise<unknown>)
59
+ .then((output: unknown) => {
60
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output, timestamp: start, durationMs: rawDateNow() - start })
61
+ return output
62
+ })
63
+ .catch((err: unknown) => {
64
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
65
+ throw err
66
+ })
67
+ }
68
+
69
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: result, timestamp: start, durationMs: rawDateNow() - start })
70
+ return result
71
+ }
72
+
73
+ // HTTP mode (no capture context): replay frozen events or execute live
74
+ if (!ctx && httpCtx) {
75
+ const id = httpCtx.nextId()
76
+
77
+ // Replay frozen step
78
+ const frozen = getHttpFrozenEvent(id)
79
+ if (frozen && frozen.type === 'db') {
80
+ pushTelemetryEvent(frozen)
81
+ return Promise.resolve(frozen.output)
82
+ }
83
+
84
+ // Not frozen → execute live, push telemetry
85
+ const start = rawDateNow()
86
+
87
+ let result: unknown
88
+ try {
89
+ result = original.apply(this, args)
90
+ } catch (err) {
91
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
92
+ throw err
93
+ }
94
+
95
+ if (result != null && typeof (result as Promise<unknown>).then === 'function') {
96
+ return (result as Promise<unknown>)
97
+ .then((output: unknown) => {
98
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output, timestamp: start, durationMs: rawDateNow() - start })
99
+ return output
100
+ })
101
+ .catch((err: unknown) => {
102
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs: rawDateNow() - start })
103
+ throw err
104
+ })
105
+ }
106
+
107
+ pushTelemetryEvent({ id, type: 'db', name: eventName, input, output: result, timestamp: start, durationMs: rawDateNow() - start })
108
+ return result
109
+ }
110
+
111
+ // Capture mode (enhanced with telemetry + TraceHandle)
112
+ const trace = getCurrentTrace()
113
+ const { recorder, replay } = ctx!
114
+ const id = recorder.nextId()
115
+
116
+ if (replay.shouldReplay(id)) {
117
+ const historicalEvent = replay.getRecordedEvent(id)
118
+ if (historicalEvent) recorder.record(historicalEvent)
119
+ if (httpCtx) pushTelemetryEvent(historicalEvent ?? { id, type: 'db', name: eventName, input, output: null, timestamp: rawDateNow(), durationMs: 0 })
120
+ const replayed = replay.getRecordedResult(id)
121
+ if (trace && typeof trace.recordToolCall === 'function') {
122
+ trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: replayed, workflowEventId: id })
123
+ }
124
+ return Promise.resolve(replayed)
125
+ }
126
+
127
+ const start = rawDateNow()
128
+
129
+ let result: unknown
130
+ try {
131
+ result = original.apply(this, args)
132
+ } catch (err) {
133
+ const durationMs = rawDateNow() - start
134
+ const event = { id, type: 'db' as const, name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs }
135
+ recorder.record(event)
136
+ if (httpCtx) pushTelemetryEvent(event)
137
+ if (trace && typeof trace.recordToolCall === 'function') {
138
+ trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: { error: String(err) }, workflowEventId: id, durationMs })
139
+ }
140
+ throw err
141
+ }
142
+
143
+ if (result != null && typeof (result as Promise<unknown>).then === 'function') {
144
+ return (result as Promise<unknown>)
145
+ .then((output: unknown) => {
146
+ const durationMs = rawDateNow() - start
147
+ const event = { id, type: 'db' as const, name: eventName, input, output, timestamp: start, durationMs }
148
+ recorder.record(event)
149
+ if (httpCtx) pushTelemetryEvent(event)
150
+ if (trace && typeof trace.recordToolCall === 'function') {
151
+ trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: output, workflowEventId: id, durationMs })
152
+ }
153
+ return output
154
+ })
155
+ .catch((err: unknown) => {
156
+ const durationMs = rawDateNow() - start
157
+ const event = { id, type: 'db' as const, name: eventName, input, output: { error: String(err) }, timestamp: start, durationMs }
158
+ recorder.record(event)
159
+ if (httpCtx) pushTelemetryEvent(event)
160
+ if (trace && typeof trace.recordToolCall === 'function') {
161
+ trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result: { error: String(err) }, workflowEventId: id, durationMs })
162
+ }
163
+ throw err
164
+ })
165
+ }
166
+
167
+ // Sync return (rare for DB calls)
168
+ const durationMs = rawDateNow() - start
169
+ const event = { id, type: 'db' as const, name: eventName, input, output: result, timestamp: start, durationMs }
170
+ recorder.record(event)
171
+ if (httpCtx) pushTelemetryEvent(event)
172
+ if (trace && typeof trace.recordToolCall === 'function') {
173
+ trace.recordToolCall({ name: eventName, args: toTraceArgs(input), result, workflowEventId: id, durationMs })
174
+ }
175
+ return result
176
+ }
177
+ }
178
+
179
+ async function tryPatchPg(): Promise<void> {
180
+ // @ts-ignore — optional peer dependency
181
+ const pgMod = await import('pg') as Record<string, unknown>
182
+ const pg = (pgMod.default as Record<string, unknown> | undefined) ?? pgMod
183
+ const Client = pg.Client as { prototype: object } | undefined
184
+ // Patch Client.prototype only — Pool.query delegates to Client internally
185
+ if (Client?.prototype) {
186
+ wrapProtoMethod(Client.prototype, 'query', 'pg.query')
187
+ }
188
+ }
189
+
190
+ async function tryPatchMysql2(): Promise<void> {
191
+ // @ts-ignore — optional peer dependency
192
+ const mod = await import('mysql2/promise') as Record<string, unknown>
193
+ const mysql2 = (mod.default as Record<string, unknown> | undefined) ?? mod
194
+ const Connection = mysql2.Connection as { prototype: object } | undefined
195
+ if (Connection?.prototype) {
196
+ wrapProtoMethod(Connection.prototype, 'query', 'mysql2.query')
197
+ wrapProtoMethod(Connection.prototype, 'execute', 'mysql2.execute')
198
+ }
199
+ }
200
+
201
+ async function tryPatchMongodb(): Promise<void> {
202
+ // @ts-ignore — optional peer dependency
203
+ const mongMod = await import('mongodb') as Record<string, unknown>
204
+ const Collection = (
205
+ mongMod.Collection ??
206
+ (mongMod.default as Record<string, unknown> | undefined)?.Collection
207
+ ) as { prototype: object } | undefined
208
+ if (Collection?.prototype) {
209
+ for (const method of ['find', 'findOne', 'insertOne', 'updateOne', 'deleteOne', 'aggregate']) {
210
+ wrapProtoMethod(Collection.prototype, method, `mongodb.${method}`)
211
+ }
212
+ }
213
+ }
214
+
215
+ async function tryPatchIoredis(): Promise<void> {
216
+ // @ts-ignore — optional peer dependency
217
+ const mod = await import('ioredis') as Record<string, unknown>
218
+ const Redis = (mod.default ?? mod) as { prototype: object } | undefined
219
+ if (Redis?.prototype) {
220
+ wrapProtoMethod(Redis.prototype, 'call', 'redis.call')
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Auto-instruments common DB driver prototypes. Safe to call when drivers are
226
+ * not installed — missing modules are silently skipped.
227
+ */
228
+ export async function installDBAutoInterceptor(): Promise<void> {
229
+ await Promise.allSettled([
230
+ tryPatchPg(),
231
+ tryPatchMysql2(),
232
+ tryPatchMongodb(),
233
+ tryPatchIoredis(),
234
+ ])
235
+ }
236
+
237
+ /** Restores all patched DB driver prototypes to their originals. */
238
+ export function uninstallDBAutoInterceptor(): void {
239
+ for (const { proto, method, original } of appliedPatches) {
240
+ proto[method] = original
241
+ }
242
+ appliedPatches.length = 0
243
+ }
@@ -0,0 +1,156 @@
1
+ import { getCaptureContext } from '../capture/recorder.js'
2
+ import { getCurrentTrace } from '../trace-adapter/context.js'
3
+ import { rawDateNow } from './side-effects.js'
4
+ import { getHttpRunContext, getHttpFrozenEvent, pushTelemetryEvent, tryAutoInitHttpContext, getObservabilityContext } from './telemetry-push.js'
5
+
6
+ function toTraceArgs(input: unknown): Record<string, unknown> | undefined {
7
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
8
+ return input as Record<string, unknown>
9
+ }
10
+ if (input === undefined) return undefined
11
+ return { value: input }
12
+ }
13
+
14
+ /**
15
+ * Wraps named methods on a DB client instance in-place so their calls are
16
+ * recorded as "db" events. Returns the same client object.
17
+ *
18
+ * Supports all three execution modes:
19
+ * - Capture mode: replay from TraceRecorder / ReplayController
20
+ * - HTTP mode: replay frozen events from dashboard, push telemetry
21
+ * - Observability mode: record and push telemetry
22
+ *
23
+ * @param client Any DB client (pg.Client, redis client, mongoose Model, etc.)
24
+ * @param methodNames Method names to wrap
25
+ * @param label Optional label prefix for event names (defaults to constructor name)
26
+ */
27
+ export function wrapDB<T extends object>(
28
+ client: T,
29
+ methodNames: (keyof T & string)[],
30
+ label?: string,
31
+ ): T {
32
+ const prefix = label ?? (client.constructor?.name ?? 'db')
33
+
34
+ for (const method of methodNames) {
35
+ const original = (client as Record<string, unknown>)[method]
36
+ if (typeof original !== 'function') continue
37
+
38
+ ;(client as Record<string, unknown>)[method] = async (...args: unknown[]) => {
39
+ await tryAutoInitHttpContext()
40
+ const ctx = getCaptureContext()
41
+ const httpCtx = getHttpRunContext()
42
+ const obsCtx = getObservabilityContext()
43
+ const name = `${prefix}.${method}`
44
+ const input = args.length === 1 ? args[0] : args
45
+
46
+ if (!ctx && !httpCtx && !obsCtx) return (original as (...a: unknown[]) => unknown).apply(client, args)
47
+
48
+ // Observability-only mode: record and push, no mocks/replay
49
+ if (!ctx && !httpCtx && obsCtx) {
50
+ const id = obsCtx.nextId()
51
+ const start = rawDateNow()
52
+ try {
53
+ const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
54
+ pushTelemetryEvent({ id, type: 'db', name, input, output, timestamp: start, durationMs: rawDateNow() - start })
55
+ return output
56
+ } catch (e) {
57
+ pushTelemetryEvent({ id, type: 'db', name, input, output: { error: String(e) }, timestamp: start, durationMs: rawDateNow() - start })
58
+ throw e
59
+ }
60
+ }
61
+
62
+ // HTTP mode (no capture context): replay frozen events or execute live
63
+ if (!ctx && httpCtx) {
64
+ const id = httpCtx.nextId()
65
+
66
+ // Replay frozen step
67
+ const frozen = getHttpFrozenEvent(id)
68
+ if (frozen && frozen.type === 'db') {
69
+ pushTelemetryEvent(frozen)
70
+ return frozen.output
71
+ }
72
+
73
+ // Not frozen → execute live, push telemetry
74
+ const start = rawDateNow()
75
+ try {
76
+ const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
77
+ pushTelemetryEvent({ id, type: 'db', name, input, output, timestamp: start, durationMs: rawDateNow() - start })
78
+ return output
79
+ } catch (e) {
80
+ pushTelemetryEvent({ id, type: 'db', name, input, output: { error: String(e) }, timestamp: start, durationMs: rawDateNow() - start })
81
+ throw e
82
+ }
83
+ }
84
+
85
+ // Capture mode
86
+ const trace = getCurrentTrace()
87
+ const { recorder, replay } = ctx!
88
+ const id = recorder.nextId()
89
+
90
+ if (replay.shouldReplay(id)) {
91
+ const historical = replay.getRecordedEvent(id)
92
+ if (historical) recorder.record(historical)
93
+ if (httpCtx) pushTelemetryEvent(historical ?? { id, type: 'db', name, input, output: null, timestamp: rawDateNow(), durationMs: 0 })
94
+ const replayed = replay.getRecordedResult(id)
95
+ if (trace && typeof trace.recordToolCall === 'function') {
96
+ trace.recordToolCall({ name, args: toTraceArgs(input), result: replayed, workflowEventId: id })
97
+ }
98
+ return replayed
99
+ }
100
+
101
+ const start = rawDateNow()
102
+ try {
103
+ const output = await (original as (...a: unknown[]) => unknown).apply(client, args)
104
+ const durationMs = rawDateNow() - start
105
+ const event = { id, type: 'db' as const, name, input, output, timestamp: start, durationMs }
106
+ recorder.record(event)
107
+ if (httpCtx) pushTelemetryEvent(event)
108
+ if (trace && typeof trace.recordToolCall === 'function') {
109
+ trace.recordToolCall({ name, args: toTraceArgs(input), result: output, workflowEventId: id, durationMs })
110
+ }
111
+ return output
112
+ } catch (e) {
113
+ const durationMs = rawDateNow() - start
114
+ const event = { id, type: 'db' as const, name, input, output: { error: String(e) }, timestamp: start, durationMs }
115
+ recorder.record(event)
116
+ if (httpCtx) pushTelemetryEvent(event)
117
+ if (trace && typeof trace.recordToolCall === 'function') {
118
+ trace.recordToolCall({ name, args: toTraceArgs(input), result: { error: String(e) }, workflowEventId: id, durationMs })
119
+ }
120
+ throw e
121
+ }
122
+ }
123
+ }
124
+
125
+ return client
126
+ }
127
+
128
+ // --- Driver-specific convenience helpers ---
129
+
130
+ /** Wraps `query` on a pg.Client or pg.Pool instance. */
131
+ export function wrapPgClient<T extends object>(client: T): T {
132
+ return wrapDB(client, ['query'] as (keyof T & string)[], 'pg')
133
+ }
134
+
135
+ /** Wraps `raw` on a knex instance. */
136
+ export function wrapKnex<T extends object>(knex: T): T {
137
+ return wrapDB(knex, ['raw'] as (keyof T & string)[], 'knex')
138
+ }
139
+
140
+ /** Wraps common query methods on a MongoDB collection. */
141
+ export function wrapMongoCollection<T extends object>(collection: T): T {
142
+ return wrapDB(
143
+ collection,
144
+ ['find', 'findOne', 'insertOne', 'updateOne', 'deleteOne'] as (keyof T & string)[],
145
+ 'mongo',
146
+ )
147
+ }
148
+
149
+ /** Wraps common methods on a Redis client. */
150
+ export function wrapRedisClient<T extends object>(client: T): T {
151
+ return wrapDB(
152
+ client,
153
+ ['get', 'set', 'del', 'hget', 'hset'] as (keyof T & string)[],
154
+ 'redis',
155
+ )
156
+ }