elasticdash-test 0.1.10-alpha → 0.1.11-alpha-2

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 (112) hide show
  1. package/README.md +328 -0
  2. package/dist/browser-ui.js +0 -4
  3. package/dist/browser-ui.js.map +1 -1
  4. package/dist/capture/event.d.ts +19 -0
  5. package/dist/capture/event.d.ts.map +1 -0
  6. package/dist/capture/event.js +2 -0
  7. package/dist/capture/event.js.map +1 -0
  8. package/dist/capture/index.d.ts +4 -0
  9. package/dist/capture/index.d.ts.map +1 -0
  10. package/dist/capture/index.js +4 -0
  11. package/dist/capture/index.js.map +1 -0
  12. package/dist/capture/recorder.d.ts +19 -0
  13. package/dist/capture/recorder.d.ts.map +1 -0
  14. package/dist/capture/recorder.js +36 -0
  15. package/dist/capture/recorder.js.map +1 -0
  16. package/dist/capture/replay.d.ts +20 -0
  17. package/dist/capture/replay.d.ts.map +1 -0
  18. package/dist/capture/replay.js +47 -0
  19. package/dist/capture/replay.js.map +1 -0
  20. package/dist/cli.js +48 -2
  21. package/dist/cli.js.map +1 -1
  22. package/dist/core/agent-state.d.ts +47 -0
  23. package/dist/core/agent-state.d.ts.map +1 -0
  24. package/dist/core/agent-state.js +137 -0
  25. package/dist/core/agent-state.js.map +1 -0
  26. package/dist/dashboard-server.d.ts +36 -0
  27. package/dist/dashboard-server.d.ts.map +1 -0
  28. package/dist/dashboard-server.js +2658 -0
  29. package/dist/dashboard-server.js.map +1 -0
  30. package/dist/index.d.ts +17 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +13 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/interceptors/ai-interceptor.d.ts.map +1 -1
  35. package/dist/interceptors/ai-interceptor.js +221 -10
  36. package/dist/interceptors/ai-interceptor.js.map +1 -1
  37. package/dist/interceptors/db-auto.d.ts +8 -0
  38. package/dist/interceptors/db-auto.d.ts.map +1 -0
  39. package/dist/interceptors/db-auto.js +125 -0
  40. package/dist/interceptors/db-auto.js.map +1 -0
  41. package/dist/interceptors/db.d.ts +18 -0
  42. package/dist/interceptors/db.d.ts.map +1 -0
  43. package/dist/interceptors/db.js +59 -0
  44. package/dist/interceptors/db.js.map +1 -0
  45. package/dist/interceptors/http.d.ts +3 -0
  46. package/dist/interceptors/http.d.ts.map +1 -0
  47. package/dist/interceptors/http.js +172 -0
  48. package/dist/interceptors/http.js.map +1 -0
  49. package/dist/interceptors/side-effects.d.ts +7 -0
  50. package/dist/interceptors/side-effects.d.ts.map +1 -0
  51. package/dist/interceptors/side-effects.js +72 -0
  52. package/dist/interceptors/side-effects.js.map +1 -0
  53. package/dist/interceptors/tool.d.ts +2 -0
  54. package/dist/interceptors/tool.d.ts.map +1 -0
  55. package/dist/interceptors/tool.js +75 -0
  56. package/dist/interceptors/tool.js.map +1 -0
  57. package/dist/interceptors/workflow-ai.d.ts +2 -0
  58. package/dist/interceptors/workflow-ai.d.ts.map +1 -0
  59. package/dist/interceptors/workflow-ai.js +26 -0
  60. package/dist/interceptors/workflow-ai.js.map +1 -0
  61. package/dist/internals/async-storage.d.ts +21 -0
  62. package/dist/internals/async-storage.d.ts.map +1 -0
  63. package/dist/internals/async-storage.js +46 -0
  64. package/dist/internals/async-storage.js.map +1 -0
  65. package/dist/internals/conditional-recorder.d.ts +21 -0
  66. package/dist/internals/conditional-recorder.d.ts.map +1 -0
  67. package/dist/internals/conditional-recorder.js +54 -0
  68. package/dist/internals/conditional-recorder.js.map +1 -0
  69. package/dist/internals/node-crypto.d.ts +12 -0
  70. package/dist/internals/node-crypto.d.ts.map +1 -0
  71. package/dist/internals/node-crypto.js +38 -0
  72. package/dist/internals/node-crypto.js.map +1 -0
  73. package/dist/matchers/index.d.ts +2 -1
  74. package/dist/matchers/index.d.ts.map +1 -1
  75. package/dist/matchers/index.js +28 -1
  76. package/dist/matchers/index.js.map +1 -1
  77. package/dist/proxy/llm-capture.d.ts +14 -0
  78. package/dist/proxy/llm-capture.d.ts.map +1 -0
  79. package/dist/proxy/llm-capture.js +264 -0
  80. package/dist/proxy/llm-capture.js.map +1 -0
  81. package/dist/runWorkflowSubprocess.d.ts +14 -0
  82. package/dist/runWorkflowSubprocess.d.ts.map +1 -0
  83. package/dist/runWorkflowSubprocess.js +66 -0
  84. package/dist/runWorkflowSubprocess.js.map +1 -0
  85. package/dist/runner.d.ts.map +1 -1
  86. package/dist/runner.js +36 -2
  87. package/dist/runner.js.map +1 -1
  88. package/dist/tool-runner-worker.d.ts +12 -0
  89. package/dist/tool-runner-worker.d.ts.map +1 -0
  90. package/dist/tool-runner-worker.js +52 -0
  91. package/dist/tool-runner-worker.js.map +1 -0
  92. package/dist/trace-adapter/context.d.ts +2 -0
  93. package/dist/trace-adapter/context.d.ts.map +1 -1
  94. package/dist/trace-adapter/context.js +10 -4
  95. package/dist/trace-adapter/context.js.map +1 -1
  96. package/dist/tracing-universal.d.ts +13 -0
  97. package/dist/tracing-universal.d.ts.map +1 -0
  98. package/dist/tracing-universal.js +33 -0
  99. package/dist/tracing-universal.js.map +1 -0
  100. package/dist/tracing.d.ts +2 -0
  101. package/dist/tracing.d.ts.map +1 -0
  102. package/dist/tracing.js +57 -0
  103. package/dist/tracing.js.map +1 -0
  104. package/dist/workflow-runner-worker.d.ts +2 -0
  105. package/dist/workflow-runner-worker.d.ts.map +1 -0
  106. package/dist/workflow-runner-worker.js +253 -0
  107. package/dist/workflow-runner-worker.js.map +1 -0
  108. package/dist/workflow-runner.d.ts +14 -0
  109. package/dist/workflow-runner.d.ts.map +1 -0
  110. package/dist/workflow-runner.js +30 -0
  111. package/dist/workflow-runner.js.map +1 -0
  112. package/package.json +10 -4
package/README.md CHANGED
@@ -285,6 +285,99 @@ aiTest('user lookup flow', async (ctx) => {
285
285
 
286
286
  **Libraries using `https.request` directly** (older versions of some SDKs) are not covered by fetch interception. Use manual `ctx.trace.recordLLMStep()` for those.
287
287
 
288
+ ## Automatic Tool Recording
289
+
290
+ Tool functions are automatically wrapped and recorded when imported and called during workflow execution. This gives you tracing and replay support without any manual instrumentation.
291
+
292
+ ### How to use tools
293
+
294
+ Define your tools in `ed_tools.ts` and export them:
295
+
296
+ ```ts
297
+ // ed_tools.ts
298
+ import { wrapTool } from 'elasticdash-test'
299
+
300
+ export const chargeCard = wrapTool('chargeCard', async (input: { amount: number; cardToken: string }) => {
301
+ // your actual implementation
302
+ return { success: true, transactionId: 'txn-123' }
303
+ })
304
+
305
+ export const fetchOrderStatus = wrapTool('fetchOrderStatus', async (input: { orderId: string }) => {
306
+ // query your database or API
307
+ return { status: 'shipped', tracking: 'track-456' }
308
+ })
309
+ ```
310
+
311
+ In your workflows or agents, simply import and call them — the runner automatically wraps them and records each call:
312
+
313
+ ```ts
314
+ import { chargeCard, fetchOrderStatus } from './ed_tools'
315
+
316
+ export async function checkoutFlow(orderId: string, cardToken: string) {
317
+ const order = await fetchOrderStatus({ orderId })
318
+ const charge = await chargeCard({ amount: order.total, cardToken })
319
+ return { orderId, chargeId: charge.transactionId }
320
+ }
321
+ ```
322
+
323
+ Each call to a tool is automatically recorded to `ctx.trace` with:
324
+ - Tool name
325
+ - Input arguments
326
+ - Output result
327
+ - Timestamp and duration
328
+
329
+ No `ctx.trace.recordToolCall()` needed — it happens behind the scenes.
330
+
331
+ Runtime behavior by mode:
332
+ - Local/normal execution: wrapped tools run normally (no replay context).
333
+ - Trace capture mode: wrapped tools run normally and are recorded.
334
+ - Replay mode (`Run from here`): pre-checkpoint wrapped calls return historical results and skip tool body execution.
335
+
336
+ ### Tool replay and freezing in "Run from here"
337
+
338
+ When you use the dashboard's "Run from here" feature to replay a workflow from a checkpoint:
339
+
340
+ 1. **Tools called before the checkpoint** are **frozen** — their recorded results are returned instantly without re-executing the actual function.
341
+ 2. **Tools called after the checkpoint** execute normally with live dependencies.
342
+
343
+ This means:
344
+ - If a tool made 3 API calls before the checkpoint, those calls won't happen again on replay.
345
+ - If the tool is slow (waiting for external services), frozen calls are instant.
346
+ - The tool's frozen status is visible in the dashboard with a frozen tag/styling, matching AI step behavior.
347
+ - Side-effect replay is type-checked (`Date.now` vs `Math.random`) and malformed observation timestamps are auto-sanitized.
348
+
349
+ Example:
350
+ ```
351
+ Task 1: fetchOrderStatus() ↓ frozen (checkpoint before this)
352
+ │─ API call to orders service ↓ frozen
353
+ └─ Result: { status: shipped } ↓ frozen
354
+
355
+ → You modify input for Task 2 and re-run
356
+ The fetchOrderStatus API call is skipped; the original result is reused instantly.
357
+ ```
358
+
359
+ ### Recording tool calls manually
360
+
361
+ If you need to record tools outside the normal import flow or for providers not in `ed_tools.ts`, use:
362
+
363
+ ```ts
364
+ ctx.trace.recordToolCall({
365
+ name: 'customTool',
366
+ args: { param: 'value' },
367
+ result: { success: true }
368
+ })
369
+ ```
370
+
371
+ Manual recording is best-effort trace logging. For full replay/freeze semantics in "Run from here" (including frozen tool tags and deterministic replay), run tools through the replay-aware wrapper path (auto-wrapped worker tools or `wrapTool(...)`).
372
+
373
+ ```ts
374
+ import { wrapTool } from 'elasticdash-test'
375
+
376
+ export const customTool = wrapTool('customTool', async (input: { param: string }) => {
377
+ return await doWork(input)
378
+ })
379
+ ```
380
+
288
381
  ### Recording flow steps without passing `ctx.trace` (AsyncLocalStorage)
289
382
 
290
383
  The runner now sets a per-test `currentTrace` using Node’s `AsyncLocalStorage`, so your app code can record steps without threading `ctx.trace` through every function. This remains safe under parallel execution.
@@ -319,6 +412,12 @@ Notes:
319
412
  - Works per async context; if you spawn detached work (child processes/independent workers), pass `trace` explicitly there.
320
413
  - Still compatible with manual DI: you can continue passing `ctx.trace` explicitly if you prefer.
321
414
 
415
+ ### Optional local LLM capture proxy (for Supabase Edge / Deno)
416
+ - Opt in by setting `ELASTICDASH_LLM_PROXY=1` (optional: `ELASTICDASH_LLM_PROXY_PORT`, default `8787`). The runner will start a local proxy and generate a per-test `ELASTICDASH_TRACE_ID`.
417
+ - Point your LLM client at the proxy via base URL envs (e.g., `OPENAI_BASE_URL=http://localhost:8787/v1`, `ANTHROPIC_API_URL=http://localhost:8787`). No code changes in your workflow; only env overrides when running tests.
418
+ - Forward the trace ID to your Edge/Deno code (e.g., add `x-trace-id: process.env.ELASTICDASH_TRACE_ID` on the request to your Supabase Edge function). The proxy records model/prompt/completion (or `(streamed)`) and the runner folds captured steps back into `ctx.trace` after each test.
419
+ - When `ELASTICDASH_LLM_PROXY` is unset, behavior is unchanged and the existing Node fetch interceptor remains the default.
420
+
322
421
  ---
323
422
 
324
423
  ## Configuration
@@ -361,6 +460,207 @@ This gives you typed `aiTest`, `beforeAll`, `afterAll` globals and typed custom
361
460
 
362
461
  ---
363
462
 
463
+ ## Workflows Dashboard
464
+
465
+ Browse and search all available workflow functions in your project:
466
+
467
+ ```bash
468
+ elasticdash dashboard # open dashboard at http://localhost:4573
469
+ elasticdash dashboard --port 4572 # use custom port
470
+ elasticdash dashboard --no-open # skip auto-opening browser
471
+ ```
472
+
473
+ The dashboard scans your workflow/tool files and displays:
474
+ - If both `.ts` and `.js` versions of a file exist (e.g., `ed_workflows.ts` and `ed_workflows.js`), the dashboard will always use the `.ts` file.
475
+ - If only `.ts` exists, it will be automatically transpiled to `.js` before scanning/importing—no manual build step required.
476
+ - If only `.js` exists, it will be used directly.
477
+
478
+ This means you can write your workflows and tools in TypeScript, and the dashboard will handle transpilation automatically. You do not need to run `tsc` or build manually for dashboard usage.
479
+
480
+ **Example file selection logic:**
481
+ | Scenario | File Used |
482
+ |-------------------------|------------------|
483
+ | Only `ed_workflows.ts` | Transpiled `.ts` |
484
+ | Only `ed_workflows.js` | `.js` |
485
+ | Both exist | `.ts` (preferred)|
486
+
487
+ The dashboard displays:
488
+ - **Function names** — all exported functions in the module
489
+ - **Signatures** — function parameters and return types
490
+ - **Async indicator** — marks async vs sync functions
491
+ - **Source module** — where the function is imported from (if re-exported)
492
+ - **File path** — location of the workflow file
493
+
494
+ Use the search field to filter workflows by:
495
+ - **Name** — find workflow by function name (e.g., `checkoutFlow`)
496
+ - **Source module** — find all workflows from a specific module (e.g., `app.workflows`)
497
+ - **File path** — filter by location in your codebase
498
+
499
+ This is useful for discovering available workflows, understanding their signatures, and identifying where functions are defined before calling them in tests.
500
+
501
+ ### `ed_workflows.ts`, `ed_tools.ts`, `ed_agents.ts`
502
+
503
+ These optional files bundle and re-export existing functions from your codebase for use in tests.
504
+
505
+ #### `ed_workflows.ts`
506
+
507
+ Re-export workflow functions from your application:
508
+
509
+ ```ts
510
+ // ed_workflows.ts
511
+ export { orderWorkflow, refundWorkflow } from './src/workflows'
512
+ export { userLookupFlow } from './src/user-flows'
513
+ ```
514
+
515
+ Access in tests:
516
+
517
+ ```ts
518
+ import { orderWorkflow } from './ed_workflows'
519
+
520
+ aiTest('full order workflow', async (ctx) => {
521
+ const result = await orderWorkflow('order-123', 'cust-456')
522
+ expect(ctx.trace).toCallTool('chargeCard')
523
+ })
524
+ ```
525
+
526
+ #### `ed_tools.ts`
527
+
528
+ Re-export tool functions that agents or workflows can invoke:
529
+
530
+ ```ts
531
+ // ed_tools.ts
532
+ import { wrapTool } from 'elasticdash-test'
533
+ import { chargeCard as chargeCardRaw, fetchOrderStatus as fetchOrderStatusRaw, sendNotification as sendNotificationRaw } from './src/tools'
534
+
535
+ export const chargeCard = wrapTool('chargeCard', chargeCardRaw)
536
+ export const fetchOrderStatus = wrapTool('fetchOrderStatus', fetchOrderStatusRaw)
537
+ export const sendNotification = wrapTool('sendNotification', sendNotificationRaw)
538
+ ```
539
+
540
+ #### `ed_agents.ts`
541
+
542
+ Re-export agent functions or create a config object:
543
+
544
+ ```ts
545
+ // ed_agents.ts
546
+ export { checkoutAgent, paymentAgent } from './src/agents'
547
+
548
+ // Or as a config object:
549
+ export const agents = {
550
+ checkout: checkoutAgent,
551
+ payment: paymentAgent,
552
+ }
553
+ ```
554
+
555
+ The dashboard command will scan these files and display all exported functions with their signatures, making it easy to explore your workflow API.
556
+
557
+ ---
558
+
559
+ ## Agent Mid-Trace Replay
560
+
561
+ ElasticDash supports resuming a long-running agent from any task in its plan — without re-executing already-completed steps. This is useful for:
562
+
563
+ - **Resuming after failures**: If task 3 of 5 fails, fix the issue and re-run from task 3 only.
564
+ - **Pausing for approval**: Capture state after task 2, get human sign-off, then continue.
565
+ - **Debugging in isolation**: Re-run a single task with modified input to diagnose a problem.
566
+
567
+ ### How it works
568
+
569
+ Agents are structured as an **AgentPlan** — an ordered list of **AgentTask** objects, each with a tool name, input, and output. When serialized, the plan plus all captured trace events form an **AgentState** that can be saved to disk/database and replayed later.
570
+
571
+ ### Quick start
572
+
573
+ ```ts
574
+ import { plannerAgent, executorAgent, resumeAgentFromTrace } from './ed_agents'
575
+ import { serializeAgentState, deserializeAgentState } from 'elasticdash-test'
576
+ import fs from 'node:fs'
577
+
578
+ // 1. Generate a plan
579
+ const plan = await plannerAgent('Show me sales for Q1', { userToken: 'tok-abc' })
580
+
581
+ // 2. Execute the plan (runs all tasks sequentially)
582
+ const completedPlan = await executorAgent(plan)
583
+
584
+ // 3. Serialize and save state (e.g., after partial execution)
585
+ const state = serializeAgentState(completedPlan, [] /* pass recorder.events in worker context */)
586
+ fs.writeFileSync('agent-state.json', JSON.stringify(state, null, 2))
587
+
588
+ // 4. Later: load saved state and resume from task 2 (0-based index 1)
589
+ const savedState = JSON.parse(fs.readFileSync('agent-state.json', 'utf8'))
590
+ const stateToResume = deserializeAgentState({ ...savedState, resumeFromTaskIndex: 1 })
591
+ const resumedPlan = await resumeAgentFromTrace(stateToResume)
592
+
593
+ console.log('Resumed plan status:', resumedPlan.status)
594
+ console.log('Task outputs:', resumedPlan.tasks.map((t) => ({ id: t.id, status: t.status })))
595
+ ```
596
+
597
+ ### AgentState structure
598
+
599
+ ```ts
600
+ interface AgentState {
601
+ plan: AgentPlan // Full plan with all tasks (completed and pending)
602
+ trace: WorkflowEvent[] // Captured trace events from previous execution
603
+ resumeFromTaskIndex: number // Zero-based index — tasks before this are loaded from cache
604
+ }
605
+
606
+ interface AgentPlan {
607
+ id: string
608
+ tasks: AgentTask[]
609
+ status: 'planning' | 'executing' | 'completed' | 'failed' | 'paused'
610
+ currentTaskIndex: number
611
+ context: Record<string, unknown>
612
+ metadata: Record<string, unknown>
613
+ }
614
+
615
+ interface AgentTask {
616
+ id: string
617
+ status: 'pending' | 'in-progress' | 'completed' | 'failed'
618
+ description: string
619
+ tool: string // Name of the tool function to invoke
620
+ input: unknown // May contain { $ref: "task-N.output.fieldName" } placeholders
621
+ output?: unknown // Populated after execution
622
+ error?: string
623
+ startedAt?: number
624
+ completedAt?: number
625
+ }
626
+ ```
627
+
628
+ ### Task input placeholders
629
+
630
+ Task inputs can reference previous task outputs using `{ $ref: "taskId.output.fieldPath" }`:
631
+
632
+ ```ts
633
+ // task-2 uses the embedding produced by task-1
634
+ {
635
+ id: 'task-2',
636
+ tool: 'taskSelectorService',
637
+ input: {
638
+ queryEmbedding: { $ref: 'task-1.output.embedding' },
639
+ topK: 3,
640
+ }
641
+ }
642
+ ```
643
+
644
+ Placeholders are resolved at execution time by `resolveTaskInput()`.
645
+
646
+ ### Dashboard integration
647
+
648
+ When running an agent workflow through the dashboard:
649
+
650
+ 1. Agent task observations are **visually highlighted** with a purple background and left border, making them easy to identify.
651
+ 2. Each agent observation shows a **T1 / T2 / T3** badge indicating which task it belongs to.
652
+ 3. In the observation detail panel, a **"Resume from Task N"** button appears (agent steps only — non-agent steps have no button).
653
+ 4. Clicking it calls `/api/resume-agent-from-task` with the serialized `AgentState` and the chosen `taskIndex`, then adds the resumed run as a new trace in the comparison table.
654
+
655
+ ### Best practices for resumable agents
656
+
657
+ - **Keep tasks idempotent** where possible — if a task must be re-run, ensure it produces the same result.
658
+ - **Store minimal outputs** — only record what downstream tasks need, not full API responses.
659
+ - **Version your state schema** — if tool interfaces change, old states may need migration.
660
+ - **Use sequential tasks** — the current implementation runs tasks one-by-one; parallel task support is a planned future enhancement.
661
+
662
+ ---
663
+
364
664
  ## Project Structure
365
665
 
366
666
  ```
@@ -398,6 +698,34 @@ uninstallAIInterceptor() // restore original fetch when done
398
698
 
399
699
  ---
400
700
 
701
+ ## Recording Tool Calls Explicitly
702
+
703
+ If you want to ensure tool calls are always recorded in the workflow trace—regardless of how your tools are imported or used—you can use the `recordToolCall` utility provided by this SDK.
704
+
705
+ ### How to Use
706
+
707
+
708
+ 1. Import the function in your tool implementation:
709
+
710
+ ```ts
711
+ import { recordToolCall } from 'elasticdash-test'
712
+
713
+ export async function myTool(input: string) {
714
+ // ...tool logic...
715
+ const result = `Hello, ${input}!`
716
+ recordToolCall('myTool', { input }, result)
717
+ return result
718
+ }
719
+ ```
720
+
721
+ 2. When running under ElasticDash, all calls to `recordToolCall` will be captured in the workflow trace. When running locally or outside the runner, this function is a no-op and will not affect your code.
722
+
723
+ **This approach is robust and works with both imported and global tools.**
724
+
725
+ **Summary:**
726
+ - Use tools as globals (no import) for full traceability and automatic tool call capture.
727
+ - This approach keeps your workflow code clean and enables powerful debugging and analytics in ElasticDash.
728
+
401
729
  ## Non-Goals
402
730
 
403
731
  This runner intentionally does not support:
@@ -1,7 +1,5 @@
1
1
  import http from 'node:http';
2
- import path from 'node:path';
3
2
  import os from 'node:os';
4
- import { fileURLToPath } from 'node:url';
5
3
  import { spawn } from 'node:child_process';
6
4
  const defaultHtml = `<!doctype html>
7
5
  <html lang="en">
@@ -149,8 +147,6 @@ const defaultHtml = `<!doctype html>
149
147
  export async function startBrowserUiServer(opts = {}) {
150
148
  const autoOpen = opts.autoOpen !== false;
151
149
  let port = opts.port ?? 4571;
152
- // Ensure base dir for potential static assets (none now)
153
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
154
150
  const clients = [];
155
151
  const eventBuffer = [];
156
152
  const handler = (req, res) => {
@@ -1 +1 @@
1
- {"version":3,"file":"browser-ui.js","sourceRoot":"","sources":["../src/browser-ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-ui.js","sourceRoot":"","sources":["../src/browser-ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export type WorkflowEventType = 'ai' | 'tool' | 'http' | 'db' | 'side_effect';
2
+ export interface WorkflowEvent {
3
+ id: number;
4
+ type: WorkflowEventType;
5
+ name: string;
6
+ input: unknown;
7
+ output: unknown;
8
+ timestamp: number;
9
+ durationMs: number;
10
+ /** Optional: ID of the agent task that produced this event */
11
+ agentTaskId?: string;
12
+ /** Optional: Zero-based index of the agent task that produced this event */
13
+ agentTaskIndex?: number;
14
+ }
15
+ export interface WorkflowTrace {
16
+ traceId: string;
17
+ events: WorkflowEvent[];
18
+ }
19
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +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;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,aAAa,EAAE,CAAA;CACxB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/capture/event.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export * from './event.js';
2
+ export * from './recorder.js';
3
+ export * from './replay.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './event.js';
2
+ export * from './recorder.js';
3
+ export * from './replay.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,19 @@
1
+ import type { WorkflowEvent, WorkflowTrace } from './event.js';
2
+ import type { ReplayController } from './replay.js';
3
+ export declare class TraceRecorder {
4
+ events: WorkflowEvent[];
5
+ private _counter;
6
+ private _sideEffectCounter;
7
+ record(event: WorkflowEvent): void;
8
+ nextId(): number;
9
+ /** Separate counter for Date.now / Math.random — never shares IDs with main events. */
10
+ nextSideEffectId(): number;
11
+ toTrace(traceId?: string): WorkflowTrace;
12
+ }
13
+ export interface CaptureContext {
14
+ recorder: TraceRecorder;
15
+ replay: ReplayController;
16
+ }
17
+ export declare function setCaptureContext(ctx: CaptureContext | undefined): void;
18
+ export declare function getCaptureContext(): CaptureContext | undefined;
19
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/capture/recorder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,qBAAa,aAAa;IACxB,MAAM,EAAE,aAAa,EAAE,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,kBAAkB,CAAI;IAE9B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIlC,MAAM,IAAI,MAAM;IAIhB,uFAAuF;IACvF,gBAAgB,IAAI,MAAM;IAI1B,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa;CAMzC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AASD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAEvE;AAED,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
@@ -0,0 +1,36 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { randomUUID } from 'node:crypto';
3
+ export class TraceRecorder {
4
+ events = [];
5
+ _counter = 0;
6
+ _sideEffectCounter = 0;
7
+ record(event) {
8
+ this.events.push(event);
9
+ }
10
+ nextId() {
11
+ return ++this._counter;
12
+ }
13
+ /** Separate counter for Date.now / Math.random — never shares IDs with main events. */
14
+ nextSideEffectId() {
15
+ return ++this._sideEffectCounter;
16
+ }
17
+ toTrace(traceId) {
18
+ return {
19
+ traceId: traceId ?? randomUUID(),
20
+ events: [...this.events],
21
+ };
22
+ }
23
+ }
24
+ const g = globalThis;
25
+ const CAPTURE_ALS_KEY = '__elasticdash_capture_als__';
26
+ const captureAls = g[CAPTURE_ALS_KEY] ??
27
+ new AsyncLocalStorage();
28
+ if (!g[CAPTURE_ALS_KEY])
29
+ g[CAPTURE_ALS_KEY] = captureAls;
30
+ export function setCaptureContext(ctx) {
31
+ captureAls.enterWith(ctx);
32
+ }
33
+ export function getCaptureContext() {
34
+ return captureAls.getStore();
35
+ }
36
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/capture/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,MAAM,OAAO,aAAa;IACxB,MAAM,GAAoB,EAAE,CAAA;IACpB,QAAQ,GAAG,CAAC,CAAA;IACZ,kBAAkB,GAAG,CAAC,CAAA;IAE9B,MAAM,CAAC,KAAoB;QACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAA;IACxB,CAAC;IAED,uFAAuF;IACvF,gBAAgB;QACd,OAAO,EAAE,IAAI,CAAC,kBAAkB,CAAA;IAClC,CAAC;IAED,OAAO,CAAC,OAAgB;QACtB,OAAO;YACL,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE;YAChC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;SACzB,CAAA;IACH,CAAC;CACF;AAOD,MAAM,CAAC,GAAG,UAAqC,CAAA;AAC/C,MAAM,eAAe,GAAG,6BAA6B,CAAA;AACrD,MAAM,UAAU,GACb,CAAC,CAAC,eAAe,CAAmD;IACrE,IAAI,iBAAiB,EAA8B,CAAA;AACrD,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;IAAE,CAAC,CAAC,eAAe,CAAC,GAAG,UAAU,CAAA;AAExD,MAAM,UAAU,iBAAiB,CAAC,GAA+B;IAC/D,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAA;AAC9B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { WorkflowEvent } from './event.js';
2
+ export declare class ReplayController {
3
+ replayMode: boolean;
4
+ checkpoint: number;
5
+ history: WorkflowEvent[];
6
+ private historyMap;
7
+ /** Side effects keyed by their assigned sideEffectId, independent of main event IDs */
8
+ private sideEffectMap;
9
+ constructor(replayMode: boolean, checkpoint: number, history: WorkflowEvent[]);
10
+ shouldReplay(eventId: number): boolean;
11
+ getRecordedEvent(eventId: number): WorkflowEvent | undefined;
12
+ getRecordedResult(eventId: number): unknown;
13
+ /** Returns true if the side effect with this sideEffectId has a recorded value to replay */
14
+ shouldReplaySideEffect(n: number): boolean;
15
+ getSideEffectResult(n: number): unknown;
16
+ getRecordedSideEffectEvent(n: number): WorkflowEvent | undefined;
17
+ shouldReplaySideEffectOfType(n: number, expectedName: string): boolean;
18
+ getSideEffectResultOfType(n: number, expectedName: string): unknown;
19
+ }
20
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/capture/replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,qBAAa,gBAAgB;IAMlB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,aAAa,EAAE;IAPjC,OAAO,CAAC,UAAU,CAA4B;IAC9C,uFAAuF;IACvF,OAAO,CAAC,aAAa,CAA4B;gBAGxC,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,EAAE;IAQjC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAItC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI5D,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C,4FAA4F;IAC5F,sBAAsB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAI1C,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAIvC,0BAA0B,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIhE,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAMtE,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;CAKpE"}
@@ -0,0 +1,47 @@
1
+ export class ReplayController {
2
+ replayMode;
3
+ checkpoint;
4
+ history;
5
+ historyMap;
6
+ /** Side effects keyed by their assigned sideEffectId, independent of main event IDs */
7
+ sideEffectMap;
8
+ constructor(replayMode, checkpoint, history) {
9
+ this.replayMode = replayMode;
10
+ this.checkpoint = checkpoint;
11
+ this.history = history;
12
+ this.historyMap = new Map(history.map(e => [e.id, e]));
13
+ this.sideEffectMap = new Map(history.filter(e => e.type === 'side_effect').map(e => [e.id, e]));
14
+ }
15
+ shouldReplay(eventId) {
16
+ return this.replayMode && eventId <= this.checkpoint;
17
+ }
18
+ getRecordedEvent(eventId) {
19
+ return this.historyMap.get(eventId);
20
+ }
21
+ getRecordedResult(eventId) {
22
+ return this.historyMap.get(eventId)?.output;
23
+ }
24
+ /** Returns true if the side effect with this sideEffectId has a recorded value to replay */
25
+ shouldReplaySideEffect(n) {
26
+ return this.replayMode && this.sideEffectMap.has(n);
27
+ }
28
+ getSideEffectResult(n) {
29
+ return this.sideEffectMap.get(n)?.output;
30
+ }
31
+ getRecordedSideEffectEvent(n) {
32
+ return this.sideEffectMap.get(n);
33
+ }
34
+ shouldReplaySideEffectOfType(n, expectedName) {
35
+ if (!this.replayMode)
36
+ return false;
37
+ const event = this.sideEffectMap.get(n);
38
+ return !!event && event.type === 'side_effect' && event.name === expectedName;
39
+ }
40
+ getSideEffectResultOfType(n, expectedName) {
41
+ const event = this.sideEffectMap.get(n);
42
+ if (!event || event.type !== 'side_effect' || event.name !== expectedName)
43
+ return undefined;
44
+ return event.output;
45
+ }
46
+ }
47
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/capture/replay.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAgB;IAMlB;IACA;IACA;IAPD,UAAU,CAA4B;IAC9C,uFAAuF;IAC/E,aAAa,CAA4B;IAEjD,YACS,UAAmB,EACnB,UAAkB,EAClB,OAAwB;QAFxB,eAAU,GAAV,UAAU,CAAS;QACnB,eAAU,GAAV,UAAU,CAAQ;QAClB,YAAO,GAAP,OAAO,CAAiB;QAE/B,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtD,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,CAC1B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAClE,CAAA;IACH,CAAC;IAED,YAAY,CAAC,OAAe;QAC1B,OAAO,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,CAAA;IACtD,CAAC;IAED,gBAAgB,CAAC,OAAe;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACrC,CAAC;IAED,iBAAiB,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IAC7C,CAAC;IAED,4FAA4F;IAC5F,sBAAsB,CAAC,CAAS;QAC9B,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,mBAAmB,CAAC,CAAS;QAC3B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAA;IAC1C,CAAC;IAED,0BAA0B,CAAC,CAAS;QAClC,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,4BAA4B,CAAC,CAAS,EAAE,YAAoB;QAC1D,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAA;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACvC,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAA;IAC/E,CAAC;IAED,yBAAyB,CAAC,CAAS,EAAE,YAAoB;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO,SAAS,CAAA;QAC3F,OAAO,KAAK,CAAC,MAAM,CAAA;IACrB,CAAC;CACF"}
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { installAIInterceptor } from './interceptors/ai-interceptor.js';
10
10
  import { runFiles } from './runner.js';
11
11
  import { reportResults } from './reporter.js';
12
12
  import { startBrowserUiServer } from './browser-ui.js';
13
+ import { startDashboardServer } from './dashboard-server.js';
13
14
  function stripAnsi(input) {
14
15
  if (!input)
15
16
  return input;
@@ -20,8 +21,14 @@ async function loadConfig(cwd) {
20
21
  const configPathJs = path.join(cwd, 'elasticdash.config.js');
21
22
  for (const p of [configPath, configPathJs]) {
22
23
  if (existsSync(p)) {
23
- const mod = await import(pathToFileURL(p).href);
24
- return (mod.default ?? {});
24
+ try {
25
+ const mod = await import(pathToFileURL(p).href);
26
+ return (mod.default ?? {});
27
+ }
28
+ catch (error) {
29
+ // Skip this config file if it can't be imported (e.g., .ts when running from built dist)
30
+ continue;
31
+ }
25
32
  }
26
33
  }
27
34
  return {};
@@ -201,6 +208,45 @@ async function bootstrap() {
201
208
  }
202
209
  process.exit(anyFailed ? 1 : 0);
203
210
  });
211
+ // elasticdash dashboard
212
+ program
213
+ .command('dashboard')
214
+ .description('Browse and search workflow functions')
215
+ .option('--port <port>', 'Dashboard server port', (v) => Number(v), 4573)
216
+ .option('--no-open', 'Skip auto-opening browser')
217
+ .action(async (options) => {
218
+ console.log('[elasticdash] Starting dashboard server...');
219
+ const server = await startDashboardServer(cwd, {
220
+ port: options.port,
221
+ autoOpen: options.open,
222
+ });
223
+ console.log(`[elasticdash] Dashboard running at ${server.url}`);
224
+ console.log('[elasticdash] Press Ctrl+C to stop');
225
+ // Keep the process running with proper cleanup
226
+ let isShuttingDown = false;
227
+ const cleanup = async () => {
228
+ if (isShuttingDown) {
229
+ // Force exit on second Ctrl+C
230
+ console.log('\n[elasticdash] Force exiting...');
231
+ process.exit(1);
232
+ }
233
+ isShuttingDown = true;
234
+ console.log('\n[elasticdash] Shutting down dashboard server...');
235
+ try {
236
+ await Promise.race([
237
+ server.close(),
238
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
239
+ ]);
240
+ process.exit(0);
241
+ }
242
+ catch (error) {
243
+ console.error('[elasticdash] Error during shutdown:', error);
244
+ process.exit(1);
245
+ }
246
+ };
247
+ process.once('SIGINT', cleanup);
248
+ process.once('SIGTERM', cleanup);
249
+ });
204
250
  await program.parseAsync(process.argv);
205
251
  }
206
252
  bootstrap().catch((err) => {