elasticdash-test 0.1.12 → 0.1.13-alpha

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.
package/README.md CHANGED
@@ -8,6 +8,8 @@ An AI-native test runner for ElasticDash workflow testing. Built for async AI pi
8
8
  - [Quick Start](#quick-start)
9
9
  - [Documentation](#documentation)
10
10
  - [Tool Recording](#tool-recording)
11
+ - [AI Call Recording](#ai-call-recording)
12
+ - [HTTP Workflow Mode](#http-workflow-mode)
11
13
  - [Configuration](#configuration)
12
14
 
13
15
  ### Open Detailed Docs
@@ -18,6 +20,8 @@ An AI-native test runner for ElasticDash workflow testing. Built for async AI pi
18
20
  - [Workflows Dashboard](docs/dashboard.md)
19
21
  - [Agent Mid-Trace Replay](docs/agents.md)
20
22
  - [Deno Support](docs/deno.md)
23
+ - [Instrumentation Guide](docs/instrumentation.md) — how to write `ed_tools.ts`, `ed_workflows.ts`, `ed_agents.ts`
24
+ - [Langfuse Trace Structure](docs/langfuse-trace-structure.md) — span structure required for dashboard replay
21
25
 
22
26
  ## Features
23
27
 
@@ -27,6 +31,7 @@ An AI-native test runner for ElasticDash workflow testing. Built for async AI pi
27
31
  - 🛠️ **Tool recording & replay** — automatically trace tool calls with checkpoint-based replay
28
32
  - 📊 **Interactive dashboard** — browse workflows, debug traces, validate fixes visually
29
33
  - 🤖 **Agent mid-trace replay** — resume long-running agents from any task without re-execution
34
+ - 🌐 **HTTP workflow mode** — run workflows against your live dev server for framework-heavy apps (Next.js, Remix, etc.) with full AI and tool call observability
30
35
 
31
36
  ---
32
37
 
@@ -98,11 +103,11 @@ Total: 3
98
103
  Duration: 3.4s
99
104
  ```
100
105
 
101
- **Workflow export requirements:**
106
+ **Workflow export requirements (subprocess mode):**
102
107
 
103
108
  - Export plain callable functions from `ed_workflows.ts/js`.
104
109
  - Use JSON-serializable inputs/outputs (object or array) so dashboard replay can pass args and read results.
105
- - Do not export framework-bound handlers directly (for example Next.js `NextRequest`/`NextResponse` route handlers).
110
+ - Do not export framework-bound handlers directly (for example Next.js `NextRequest`/`NextResponse` route handlers) — use [HTTP workflow mode](#http-workflow-mode) instead.
106
111
 
107
112
  ---
108
113
 
@@ -113,11 +118,15 @@ Duration: 3.4s
113
118
  - **[Test Matchers](docs/matchers.md)** — all available matchers with examples
114
119
  - **[Tool Recording & Replay](docs/tools.md)** — automatic tool tracing and checkpoint-based replay
115
120
 
116
- ### Advanced Features
121
+ ### Advanced Features
117
122
  - **[Workflows Dashboard](docs/dashboard.md)** — interactive workflow browser, debugger, and fetching traces from Langfuse
118
123
  - **[Agent Mid-Trace Replay](docs/agents.md)** — resume long-running agents from any task
119
124
  - **[Deno Support](docs/deno.md)** — using ElasticDash Test in Deno projects
120
125
 
126
+ ### Integration & Reference
127
+ - **[Instrumentation Guide](docs/instrumentation.md)** — how to write `ed_tools.ts`, `ed_workflows.ts`, and `ed_agents.ts` to connect your production code to ElasticDash
128
+ - **[Langfuse Trace Structure](docs/langfuse-trace-structure.md)** — Langfuse span structure required for dashboard replay and tool-level diffing
129
+
121
130
  ---
122
131
 
123
132
  ## Quick Reference
@@ -192,7 +201,18 @@ No code changes needed — just run your workflow and assertions work automatica
192
201
 
193
202
  ### Tool Recording
194
203
 
195
- Manual instrumentation pattern: isolate tracing in the service `.then/.catch` path so tracing failures never block business logic:
204
+ **Recommended: `wrapTool`** wraps a tool function and automatically records its name, input, output, duration, and any streaming output. Works in both subprocess mode and HTTP mode:
205
+
206
+ ```ts
207
+ import { wrapTool } from 'elasticdash-test/http'
208
+ import { runSelectQuery } from './services/dataService'
209
+
210
+ export const dataService = wrapTool('dataService', async (input: { query: string }) => {
211
+ return await runSelectQuery(input.query)
212
+ })
213
+ ```
214
+
215
+ **Manual pattern (legacy):** isolate tracing in the service `.then/.catch` path so tracing failures never block business logic:
196
216
 
197
217
  ```ts
198
218
  import { runSelectQuery } from './services/dataService'
@@ -225,6 +245,27 @@ In manual mode, always isolate tracing in a separate `try/catch` so trace loggin
225
245
 
226
246
  **→ See [Tool Recording & Replay](docs/tools.md) for checkpoint-based replay and freezing**
227
247
 
248
+ ### AI Call Recording
249
+
250
+ **`wrapAI`** wraps any AI call function and records its name, input, output, duration, and token usage (auto-detected for Anthropic, OpenAI, and Gemini SDK responses):
251
+
252
+ ```ts
253
+ import { wrapAI } from 'elasticdash-test/http'
254
+ import Anthropic from '@anthropic-ai/sdk'
255
+
256
+ const client = new Anthropic()
257
+
258
+ export const callClaude = wrapAI('claude-sonnet-4-5', async (messages: Anthropic.MessageParam[]) => {
259
+ return await client.messages.create({
260
+ model: 'claude-sonnet-4-5-20250929',
261
+ max_tokens: 1024,
262
+ messages,
263
+ })
264
+ })
265
+ ```
266
+
267
+ Use `wrapAI` when you have a custom AI wrapper or a provider not covered by automatic interception. For direct OpenAI/Anthropic/Gemini SDK calls inside a subprocess workflow, automatic interception via `installAIInterceptor` already handles recording without any code changes.
268
+
228
269
  ### HTTP Streaming Capture and Replay
229
270
 
230
271
  ElasticDash also captures non-AI `fetch` responses that stream over HTTP (for example SSE and NDJSON endpoints) in the HTTP interceptor.
@@ -266,6 +307,64 @@ buffer += decoder.decode()
266
307
 
267
308
  ---
268
309
 
310
+ ## HTTP Workflow Mode
311
+
312
+ For apps where subprocess import fails (Next.js, Remix, SvelteKit, etc.), configure workflows to call your running dev server directly instead of importing the handler:
313
+
314
+ ```ts
315
+ // elasticdash.config.ts
316
+ export default {
317
+ testMatch: ['**/*.ai.test.ts'],
318
+ workflows: {
319
+ runChat: {
320
+ mode: 'http',
321
+ url: 'http://localhost:3001/api/chat',
322
+ method: 'POST',
323
+ headers: {
324
+ 'Content-Type': 'application/json',
325
+ 'x-user-id': '{{env.DEV_USER_ID}}',
326
+ },
327
+ bodyTemplate: {
328
+ messages: [{ role: 'user', content: '{{input.message}}' }],
329
+ selectedModel: 'claude-sonnet-4-5-20250929',
330
+ },
331
+ responseFormat: 'vercel-ai-stream',
332
+ },
333
+ },
334
+ }
335
+ ```
336
+
337
+ To enable full AI and tool call observability in HTTP mode, install `elasticdash-test` in your app and import from the lightweight subpath (avoids bundling server-only deps into your framework build):
338
+
339
+ ```ts
340
+ // app/api/chat/route.ts
341
+ import { setHttpRunContext, wrapTool, wrapAI } from 'elasticdash-test/http'
342
+
343
+ export async function POST(req: Request) {
344
+ const runId = req.headers.get('x-elasticdash-run-id')
345
+ const serverUrl = req.headers.get('x-elasticdash-server')
346
+ if (runId && serverUrl) {
347
+ setHttpRunContext(runId, serverUrl)
348
+ }
349
+ // ... rest of handler
350
+ }
351
+ ```
352
+
353
+ The dashboard injects `x-elasticdash-run-id` and `x-elasticdash-server` headers automatically when triggering a run. Every `wrapAI` and `wrapTool` call downstream pushes telemetry events back to the dashboard in real time.
354
+
355
+ **Subprocess vs HTTP mode comparison:**
356
+
357
+ | | Subprocess (default) | HTTP mode |
358
+ |---|---|---|
359
+ | Works with simple apps | Yes | Yes |
360
+ | Works with Next.js / Remix | No | Yes |
361
+ | Requires dev server running | No | Yes |
362
+ | App code changes needed | Extract handler to `ed_workflows.ts` | Add `setHttpRunContext` to request handler |
363
+ | AI / tool call observability | Automatic via interceptors | Via `wrapAI` / `wrapTool` push |
364
+ | Tool replay | Yes | No (hits live server) |
365
+
366
+ ---
367
+
269
368
  ## Configuration
270
369
 
271
370
  Optional `elasticdash.config.ts` at project root:
@@ -277,6 +376,18 @@ export default {
277
376
  }
278
377
  ```
279
378
 
379
+ **Dashboard port:** defaults to `4573`. Override via CLI flag or `.env`:
380
+
381
+ ```bash
382
+ # .env
383
+ ELASTICDASH_PORT=5000
384
+ ```
385
+
386
+ ```bash
387
+ # or CLI flag
388
+ npx elasticdash dashboard --port 5000
389
+ ```
390
+
280
391
  Optional project file: `ed_workers.ts` can be used by your app architecture (for example, exporting worker handlers), but it is not required or discovered by the ElasticDash CLI/dashboard.
281
392
 
282
393
  ## TypeScript Setup
@@ -304,6 +415,14 @@ const results = await runFiles(['./tests/flow.ai.test.ts'])
304
415
  reportResults(results)
305
416
  ```
306
417
 
418
+ **HTTP mode context (call inside your request handler):**
419
+
420
+ ```ts
421
+ import { setHttpRunContext } from 'elasticdash-test/http'
422
+
423
+ setHttpRunContext(runId, dashboardUrl)
424
+ ```
425
+
307
426
  ---
308
427
 
309
428
  ## License
@@ -7,6 +7,12 @@ export interface WorkflowEvent {
7
7
  output: unknown;
8
8
  timestamp: number;
9
9
  durationMs: number;
10
+ /** Token usage for LLM (ai) events */
11
+ usage?: {
12
+ inputTokens?: number;
13
+ outputTokens?: number;
14
+ totalTokens?: number;
15
+ };
10
16
  /** Optional: ID of the agent task that produced this event */
11
17
  agentTaskId?: string;
12
18
  /** Optional: Zero-based index of the agent task that produced this event */
@@ -1 +1 @@
1
- {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/capture/event.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,aAAa,CAAA;AAE7E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,iBAAiB,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,aAAa,EAAE,CAAA;CACxB"}
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/capture/event.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,aAAa,CAAA;AAE7E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,iBAAiB,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,sCAAsC;IACtC,KAAK,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7E,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,aAAa,EAAE,CAAA;CACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,YAAY,EAAE,CAAA;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAuCD,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,oHAAoH;IACpH,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,eAAe,CAAA;IAC3C,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAA;CAClC;AAqpFD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,CA+S1B;AAiFD,eAAO,MAAM,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,CAAC"}
1
+ {"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,YAAY,EAAE,CAAA;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AA2CD,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,oHAAoH;IACpH,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,eAAe,CAAA;IAC3C,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAA;CAClC;AAuzFD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,CA2T1B;AAiFD,eAAO,MAAM,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,CAAC"}