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 +123 -4
- package/dist/capture/event.d.ts +6 -0
- package/dist/capture/event.d.ts.map +1 -1
- package/dist/dashboard-server.d.ts.map +1 -1
- package/dist/dashboard-server.js +240 -82
- package/dist/dashboard-server.js.map +1 -1
- package/dist/interceptors/ai-interceptor.d.ts.map +1 -1
- package/dist/interceptors/ai-interceptor.js +65 -10
- package/dist/interceptors/ai-interceptor.js.map +1 -1
- package/dist/interceptors/tool.d.ts.map +1 -1
- package/dist/interceptors/tool.js +12 -7
- package/dist/interceptors/tool.js.map +1 -1
- package/dist/matchers/index.d.ts +10 -1
- package/dist/matchers/index.d.ts.map +1 -1
- package/dist/matchers/index.js +57 -12
- package/dist/matchers/index.js.map +1 -1
- package/dist/tool-runner-worker.js +2 -2
- package/dist/tool-runner-worker.js.map +1 -1
- package/dist/trace-adapter/context.d.ts +2 -0
- package/dist/trace-adapter/context.d.ts.map +1 -1
- package/dist/trace-adapter/context.js +2 -2
- package/dist/trace-adapter/context.js.map +1 -1
- package/dist/tracing.d.ts +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/tracing.js +4 -4
- package/dist/tracing.js.map +1 -1
- package/dist/workflow-runner-worker.js +37 -23
- package/dist/workflow-runner-worker.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
package/dist/capture/event.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|