elasticdash-test 0.1.25-alpha-2 → 0.1.26-alpha-1

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.
@@ -5,6 +5,10 @@
5
5
  > **When to use**: Follow this guide step-by-step when a user asks you to "integrate ElasticDash", "set up ElasticDash testing", "implement elasticdash-test", or similar. Each step produces a specific file — work through them in order.
6
6
  >
7
7
  > **How to use**: Read the full guide first to understand the project's structure, then execute each step. Use the decision trees section to choose the right pattern for the user's project (framework type, module system, tool architecture).
8
+ >
9
+ > **Important**: Do NOT leave `YOUR_TOOL_NAME` / `YOUR_WORKFLOW_NAME` placeholders in generated files. Before creating `ed_tools.ts` and `ed_workflows.ts`, scan the user's codebase to identify their actual tool functions (functions that call external APIs, databases, or services) and workflow functions (async functions that orchestrate LLM calls and tool calls). Create the files with the real function names, imports, and types found in the codebase. If no tools or workflows exist yet, ask the user which functions to instrument rather than generating placeholder files.
10
+ >
11
+ > **Automation level**: Automatically implement all steps unless you are not confident about a specific change (e.g., unclear which function is a tool vs. a utility, or ambiguous workflow boundaries). In those cases, ask the user before proceeding. For straightforward tool/workflow functions, proceed without asking.
8
12
 
9
13
  ---
10
14
 
@@ -12,9 +16,18 @@
12
16
 
13
17
  - Node.js >= 20
14
18
  - npm, yarn, or pnpm
15
- - At least one AI workflow function (a callable async function that makes LLM/tool calls)
16
19
  - LLM provider API keys for providers used in workflows (e.g., `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)
17
20
 
21
+ ### Identify tools and workflows in the codebase
22
+
23
+ Before starting, scan the project to find:
24
+
25
+ - **Tool functions**: Exported async functions that call external services (APIs, databases, search, etc.). Look for functions that use `fetch`, database clients, SDK calls, or any external I/O. These go in `ed_tools.ts`.
26
+ - **Workflow functions**: Exported async functions that orchestrate an AI pipeline — typically calling an LLM and one or more tools. Look for functions that call OpenAI, Anthropic, Gemini, or similar SDKs. These go in `ed_workflows.ts`.
27
+
28
+ If the project has no clear tool or workflow functions yet, ask the user:
29
+ > "Which functions in your codebase make external API/service calls (tools), and which functions orchestrate your AI workflow? I need to know so I can set up ElasticDash instrumentation."
30
+
18
31
  ### Detect project type before starting
19
32
 
20
33
  Check these before choosing patterns:
@@ -28,6 +41,26 @@ Check these before choosing patterns:
28
41
 
29
42
  ---
30
43
 
44
+ ## Step 0: Check environment variables (do this FIRST)
45
+
46
+ Before making any code changes, check the project's `.env` file for the required ElasticDash variables.
47
+
48
+ ### 1. Check for `ELASTICDASH_API_URL`
49
+
50
+ - If present: use the existing value.
51
+ - If missing: add `ELASTICDASH_API_URL=https://server.elasticdash.com` to `.env`.
52
+
53
+ ### 2. Check for `ELASTICDASH_API_KEY`
54
+
55
+ - If present: proceed to Step 1.
56
+ - If missing: **STOP and ask the user**:
57
+
58
+ > "Your project needs an ElasticDash API key. Please complete the onboarding at https://app.elasticdash.com to get your project API key, then add `ELASTICDASH_API_KEY=ed_your_key_here` to your `.env` file. Let me know when it's ready."
59
+
60
+ **Do not continue until `ELASTICDASH_API_KEY` is set.** The SDK cannot connect to the backend without it.
61
+
62
+ ---
63
+
31
64
  ## Step 1: Install
32
65
 
33
66
  ```bash
@@ -45,161 +78,63 @@ Add to `.gitignore`:
45
78
 
46
79
  ## Step 2: Create `ed_tools.ts`
47
80
 
48
- Create `ed_tools.ts` in the project root. This file wraps each tool function with tracing so ElasticDash can record and replay tool calls.
81
+ Create `ed_tools.ts` in the project root. This file wraps each tool function with `wrapTool()` for automatic tracing, mocking, and telemetry.
49
82
 
50
- ### Choose your pattern
83
+ **Before writing this file**: Search the codebase for functions that call external services (APIs, databases, SDKs). Each one becomes a wrapped export in `ed_tools.ts`. Replace all `YOUR_TOOL_*` and `YOUR_SOURCE_PATH_*` placeholders below with the actual function names and import paths found in the project.
51
84
 
52
- - **Pattern B (recommended for most projects)**: inline async with mock support. Use when tools are imported individually, no central dispatcher exists, or you want dashboard mock support.
53
- - **Pattern A**: `withTrace` HOF with central dispatcher. Use when tools are pure functions called through a single `dispatchTool(name, args)` dispatcher.
54
-
55
- ### Pattern B template (recommended)
85
+ ### Template
56
86
 
57
87
  ```ts
58
88
  // ed_tools.ts
59
- // Replace imports with your actual tool functions and source paths
60
- import { YOUR_TOOL_1 as YOUR_TOOL_1_impl } from './YOUR_SOURCE_PATH_1'
61
- import { YOUR_TOOL_2 as YOUR_TOOL_2_impl } from './YOUR_SOURCE_PATH_2'
89
+ import { setElasticDashModule } from './ed_workflows'
90
+
91
+ // Import original tool implementations from the actual source files
92
+ import { originalTool1 } from './services/YOUR_SOURCE_1'
93
+ import { originalTool2 } from './utils/YOUR_SOURCE_2'
62
94
 
63
95
  // ---------------------------------------------------------------------------
64
- // Helpers copy as-is, no customization needed
96
+ // Load elasticdash-test and share the module with ed_workflows.ts
65
97
  // ---------------------------------------------------------------------------
66
98
 
67
- function resolveMock(toolName: string): { mocked: true; result: unknown } | { mocked: false } {
68
- const g = globalThis as any
69
- const mocks = g.__ELASTICDASH_TOOL_MOCKS__
70
- if (!mocks) return { mocked: false }
71
-
72
- const entry = mocks[toolName]
73
- if (!entry || entry.mode === 'live') return { mocked: false }
74
-
75
- if (!g.__ELASTICDASH_TOOL_CALL_COUNTERS__) g.__ELASTICDASH_TOOL_CALL_COUNTERS__ = {}
76
- const counters = g.__ELASTICDASH_TOOL_CALL_COUNTERS__
77
- counters[toolName] = (counters[toolName] ?? 0) + 1
78
- const callNumber = counters[toolName]
79
-
80
- if (entry.mode === 'mock-all') {
81
- const data = entry.mockData ?? {}
82
- const result = data[callNumber] !== undefined ? data[callNumber] : data[0]
83
- return { mocked: true, result }
84
- }
85
-
86
- if (entry.mode === 'mock-specific') {
87
- const indices = entry.callIndices ?? []
88
- if (indices.includes(callNumber)) {
89
- return { mocked: true, result: (entry.mockData ?? {})[callNumber] }
90
- }
91
- return { mocked: false }
92
- }
93
-
94
- return { mocked: false }
95
- }
96
-
97
- async function safeRecordToolCall(tool: string, input: any, result: any) {
98
- if (!(globalThis as any).__ELASTICDASH_WORKER__) return
99
- try {
100
- const { recordToolCall } = await import('elasticdash-test')
101
- recordToolCall(tool, input, result)
102
- } catch { /* tracing must never block business logic */ }
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ type WrapToolFn = <T extends (...args: any[]) => any>(name: string, fn: T) => T
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ let wrapTool: WrapToolFn = (_name: string, fn: any) => fn
103
+
104
+ try {
105
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
106
+ const _edModule = (eval('require') as (id: string) => any)('elasticdash-test')
107
+ wrapTool = _edModule.wrapTool ?? wrapTool
108
+ // Share the module instance with ed_workflows.ts so trace hooks use the same context
109
+ setElasticDashModule(_edModule)
110
+ } catch {
111
+ // elasticdash-test not available — all wrappers pass through to original functions
103
112
  }
104
113
 
105
114
  // ---------------------------------------------------------------------------
106
- // Tools — one export per tool, following this pattern
115
+ // Wrapped tools — one export per tool
107
116
  // ---------------------------------------------------------------------------
108
117
 
109
- // TEMPLATE: Copy this block for each tool, replacing YOUR_TOOL_NAME and YOUR_TOOL_IMPL
110
- export const YOUR_TOOL_1 = async (input: any) => {
111
- const mock = resolveMock('YOUR_TOOL_1')
112
- if (mock.mocked) {
113
- await safeRecordToolCall('YOUR_TOOL_1', input, mock.result)
114
- return mock.result
115
- }
116
-
117
- return await YOUR_TOOL_1_impl(input)
118
- .then(async (res: any) => {
119
- await safeRecordToolCall('YOUR_TOOL_1', input, res)
120
- return res
121
- })
122
- .catch(async (err: any) => {
123
- await safeRecordToolCall('YOUR_TOOL_1', input, err)
124
- throw err
125
- })
126
- }
127
-
128
- export const YOUR_TOOL_2 = async (input: any) => {
129
- const mock = resolveMock('YOUR_TOOL_2')
130
- if (mock.mocked) {
131
- await safeRecordToolCall('YOUR_TOOL_2', input, mock.result)
132
- return mock.result
133
- }
134
-
135
- return await YOUR_TOOL_2_impl(input)
136
- .then(async (res: any) => {
137
- await safeRecordToolCall('YOUR_TOOL_2', input, res)
138
- return res
139
- })
140
- .catch(async (err: any) => {
141
- await safeRecordToolCall('YOUR_TOOL_2', input, err)
142
- throw err
143
- })
144
- }
145
- ```
146
-
147
- ### Pattern A template (dispatcher-based)
148
-
149
- ```ts
150
- // ed_tools.ts
151
- // Replace imports with your actual tool functions and source paths
152
- import {
153
- YOUR_TOOL_1 as _YOUR_TOOL_1,
154
- YOUR_TOOL_2 as _YOUR_TOOL_2,
155
- dispatchTool as _dispatchTool,
156
- } from './YOUR_TOOLS_SOURCE'
157
-
158
- async function withTrace<I, O>(
159
- toolName: string,
160
- input: I,
161
- fn: (input: I) => Promise<O>,
162
- ): Promise<O> {
163
- const result = await fn(input)
164
- try {
165
- const { recordToolCall } = await import('elasticdash-test')
166
- recordToolCall(toolName, input, result)
167
- } catch { /* tracing must never block business logic */ }
168
- return result
169
- }
170
-
171
- export function YOUR_TOOL_1(input: Parameters<typeof _YOUR_TOOL_1>[0]) {
172
- return withTrace('YOUR_TOOL_1', input, _YOUR_TOOL_1)
173
- }
174
-
175
- export function YOUR_TOOL_2(input: Parameters<typeof _YOUR_TOOL_2>[0]) {
176
- return withTrace('YOUR_TOOL_2', input, _YOUR_TOOL_2)
177
- }
118
+ export const myTool1 = wrapTool('myTool1', async (input: any) => {
119
+ return await originalTool1(input)
120
+ })
178
121
 
179
- export async function dispatchTool(name: string, args: Record<string, unknown>) {
180
- switch (name) {
181
- case 'YOUR_TOOL_1': return YOUR_TOOL_1(args as Parameters<typeof _YOUR_TOOL_1>[0])
182
- case 'YOUR_TOOL_2': return YOUR_TOOL_2(args as Parameters<typeof _YOUR_TOOL_2>[0])
183
- default: return _dispatchTool(name, args)
184
- }
185
- }
122
+ export const myTool2 = wrapTool('myTool2', async (input: any) => {
123
+ const { someField } = input as { someField: string }
124
+ return await originalTool2(someField)
125
+ })
186
126
  ```
187
127
 
188
- ### Alternative: `wrapTool` shorthand
189
-
190
- For simpler setups where you don't need mock support or dashboard replays:
191
-
192
- ```ts
193
- // ed_tools.ts
194
- import { wrapTool } from 'elasticdash-test'
195
- import { YOUR_TOOL_1 as YOUR_TOOL_1_impl } from './YOUR_SOURCE_PATH'
128
+ ### Key patterns
196
129
 
197
- export const YOUR_TOOL_1 = wrapTool('YOUR_TOOL_1', YOUR_TOOL_1_impl)
198
- ```
130
+ - **`wrapTool(name, fn)`** wraps the function with automatic tracing, mocking, and telemetry. Falls back to a passthrough if `elasticdash-test` is not installed.
131
+ - **`eval('require')`** is used instead of `import()` to share the same module instance across `ed_tools.ts` and `ed_workflows.ts`. This avoids ESM/CJS dual-instance issues.
132
+ - **`setElasticDashModule`** shares the loaded module with `ed_workflows.ts` so `edStartTrace`/`edEndTrace` use the same tracing context as `wrapTool`.
133
+ - The exported name (e.g., `myTool1`) can differ from the original function name (e.g., `originalTool1`). The call sites in existing source files will be updated to use the new name in Step 4.
199
134
 
200
135
  ### Important rules
201
136
 
202
- - The string name passed to `resolveMock()`, `safeRecordToolCall()`, or `wrapTool()` **must match** the exported function name exactly.
137
+ - The string name passed to `wrapTool()` **must match** the exported function name exactly.
203
138
  - Each tool function must accept a single input object and return a plain value (JSON-serializable).
204
139
  - Tool functions must not close over HTTP context, framework state, or database clients — extract pure logic first.
205
140
 
@@ -219,92 +154,173 @@ export default nextConfig
219
154
 
220
155
  ## Step 3: Create `ed_workflows.ts`
221
156
 
222
- Create `ed_workflows.ts` in the project root. This file exports workflow functions for the ElasticDash runner.
157
+ Create `ed_workflows.ts` in the project root. This file serves two purposes:
158
+
159
+ 1. **Trace lifecycle hooks** (`edStartTrace`, `edEndTrace`) — called from route handlers to mark workflow boundaries
160
+ 2. **Workflow exports** — callable functions for the ElasticDash dashboard and test runner
223
161
 
224
- ### Simple casedirect re-export
162
+ **Before writing this file**: Search the codebase for the main AI workflow functions async functions that orchestrate LLM calls and tool calls (e.g., a chat handler, an agent loop, a pipeline function). Replace all `YOUR_WORKFLOW` and `YOUR_SOURCE_PATH` placeholders below with the actual function names and import paths.
163
+
164
+ ### Trace lifecycle hooks
165
+
166
+ Every `ed_workflows.ts` should export `edStartTrace` and `edEndTrace`. These are called from route handlers (Step 4) to mark when a workflow starts and ends:
225
167
 
226
168
  ```ts
227
- // ed_workflows.ts
228
- // Replace with your actual workflow function and source path
229
- export { YOUR_WORKFLOW } from './YOUR_SOURCE_PATH'
169
+ // ed_workflows.ts — trace hooks (copy as-is)
170
+
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ let _ed: any = null
173
+
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ export function setElasticDashModule(mod: any): void {
176
+ _ed = mod
177
+ }
178
+
179
+ export const edStartTrace = async (workflowName: string): Promise<void> => {
180
+ if (!_ed) return
181
+ try {
182
+ await _ed.tryAutoInitHttpContext()
183
+ _ed.startTrace(workflowName)
184
+ } catch (err) {
185
+ console.error('[ed_workflows] edStartTrace error:', err)
186
+ }
187
+ }
188
+
189
+ export const edEndTrace = (): void => {
190
+ if (!_ed) return
191
+ try {
192
+ _ed.endTrace()
193
+ } catch (err) {
194
+ console.error('[ed_workflows] edEndTrace error:', err)
195
+ }
196
+ }
230
197
  ```
231
198
 
232
- ### Framework adapter case (Next.js / Remix)
199
+ ### Workflow exports simple case
233
200
 
234
- When the workflow lives inside a route handler, create a plain-value wrapper:
201
+ For non-framework projects where the workflow can be imported directly:
235
202
 
236
203
  ```ts
237
204
  // ed_workflows.ts
238
- import { YOUR_HANDLER as _YOUR_HANDLER } from './app/api/YOUR_ROUTE/route'
239
-
240
- export async function YOUR_WORKFLOW(input: { message: string; sessionId: string }) {
241
- return _YOUR_HANDLER(input)
242
- }
205
+ export { YOUR_WORKFLOW } from './YOUR_SOURCE_PATH'
243
206
  ```
244
207
 
245
- ### Streaming workflow case (Vercel AI SDK)
208
+ ### Workflow exports HTTP mode (Next.js / Remix / framework projects)
246
209
 
247
- Create a separate handler file that is only imported by `ed_workflows.ts`:
210
+ For framework projects, the workflow calls the running dev server instead of importing the route handler:
248
211
 
249
212
  ```ts
250
- // app/api/chat-stream/chatStreamHandler.ts
251
- import { NextRequest } from 'next/server'
252
- import { readVercelAIStream, recordToolCall } from 'elasticdash-test'
253
- import type { VercelAIStreamResult } from 'elasticdash-test'
254
- import { POST } from './route'
213
+ // ed_workflows.ts
214
+
215
+ const APP_URL = process.env.APP_URL ?? 'http://localhost:3000'
255
216
 
256
- export async function chatStreamHandler(args: {
217
+ export const YOUR_WORKFLOW = async (input: {
257
218
  messages: Array<{ role: string; content: string }>
258
219
  sessionId?: string
259
- }): Promise<VercelAIStreamResult> {
260
- const req = new NextRequest('http://localhost/api/chat-stream', {
220
+ }): Promise<unknown> => {
221
+ const response = await fetch(`${APP_URL}/api/YOUR_ENDPOINT`, {
261
222
  method: 'POST',
262
223
  headers: { 'Content-Type': 'application/json' },
263
- body: JSON.stringify(args),
224
+ body: JSON.stringify(input),
264
225
  })
265
226
 
266
- const response = await POST(req)
267
-
268
- if (response.headers.get('x-vercel-ai-data-stream') !== 'v1') {
269
- const errorMessage = await response.text().catch(() => `HTTP ${response.status}`)
270
- return { message: errorMessage, type: 'error', error: errorMessage }
227
+ if (!response.ok) {
228
+ const text = await response.text()
229
+ throw new Error(`HTTP ${response.status}: ${text}`)
271
230
  }
272
231
 
273
- const result = await readVercelAIStream(response)
274
- recordToolCall('chatStream', args, result)
275
- return result
232
+ return response.json()
276
233
  }
277
234
  ```
278
235
 
279
- Then re-export from `ed_workflows.ts`:
280
-
281
- ```ts
282
- // ed_workflows.ts
283
- export { chatStreamHandler } from './app/api/chat-stream/chatStreamHandler'
284
- ```
285
-
286
236
  ### Requirements for all workflow exports
287
237
 
288
238
  - Accept only JSON-serializable inputs (strings, numbers, arrays, plain objects)
289
239
  - Return only JSON-serializable outputs
290
240
  - Must not depend on framework runtime APIs, HTTP request context, or live service clients
291
- - If a dependency is non-serializable (e.g., database client), instantiate it inside `ed_workflows.ts`, not passed as a parameter
292
241
 
293
242
  ---
294
243
 
295
- ## Step 4: Update workflow imports
244
+ ## Step 4: Update existing source files to use `ed_tools`
245
+
246
+ > **This step modifies the user's existing source files.** The files that call tool functions must import from `ed_tools` instead of the original source, so that all tool calls go through the ElasticDash tracing layer. Similarly, workflow entry points (route handlers, etc.) must call `edStartTrace` / `edEndTrace` from `ed_workflows`.
247
+
248
+ ### Architecture after integration
296
249
 
297
- Change your workflow code to import tools from `ed_tools.ts` instead of the original source files:
250
+ ```
251
+ ed_tools.ts
252
+ ├── imports original functions from services/utils
253
+ ├── wraps each with wrapTool() for tracing
254
+ └── exports wrapped versions with the SAME or similar names
255
+
256
+ ed_workflows.ts
257
+ ├── exports edStartTrace / edEndTrace for workflow-level tracing
258
+ └── exports workflow functions (for dashboard/test runner)
259
+
260
+ Existing source files (MODIFIED):
261
+ app/api/chat/route.ts
262
+ ├── BEFORE: import { myTool } from '@/services/myService'
263
+ ├── AFTER: import { myTool } from '@/ed_tools'
264
+ ├── ADDED: import { edStartTrace, edEndTrace } from '@/ed_workflows'
265
+ └── ADDED: await edStartTrace('workflowName') at handler entry
266
+ edEndTrace() at handler exit
267
+ ```
268
+
269
+ ### What to do
270
+
271
+ **1. Find every file that calls a tool function and update its imports:**
272
+
273
+ For each tool exported from `ed_tools.ts`, search the codebase for files that import the original function. Update the import to come from `ed_tools` instead.
298
274
 
299
275
  ```ts
300
- // BEFORE
301
- import { YOUR_TOOL_1 } from './services/YOUR_SOURCE'
276
+ // BEFORE — app/api/chat/route.ts
277
+ import { clarifyAndRefineUserInput } from '@/utils/queryRefinement'
278
+ import { dynamicApiRequest } from '@/services/apiService'
302
279
 
303
- // AFTER
304
- import { YOUR_TOOL_1 } from './ed_tools'
280
+ // AFTER — app/api/chat/route.ts
281
+ import { queryRefinement } from '@/ed_tools'
282
+ import { apiService } from '@/ed_tools'
305
283
  ```
306
284
 
307
- This single import change makes all tool calls observable by ElasticDash.
285
+ ```ts
286
+ // BEFORE — app/api/chat/executor.ts
287
+ import { dynamicApiRequest } from '@/services/apiService'
288
+
289
+ // AFTER — app/api/chat/executor.ts
290
+ import { apiService } from '@/ed_tools'
291
+ ```
292
+
293
+ **2. Add trace hooks to route handlers / workflow entry points:**
294
+
295
+ At the top of each route handler or workflow entry function, add `edStartTrace` and `edEndTrace`:
296
+
297
+ ```ts
298
+ // app/api/chat/route.ts
299
+ import { edStartTrace, edEndTrace } from '@/ed_workflows'
300
+
301
+ export async function POST(req: Request) {
302
+ await edStartTrace('chatHandler')
303
+ try {
304
+ // ... existing handler logic, now using tools from ed_tools
305
+ } finally {
306
+ edEndTrace()
307
+ }
308
+ }
309
+ ```
310
+
311
+ **3. Update call sites if the wrapped function has a different name:**
312
+
313
+ If `ed_tools.ts` exports a tool under a different name than the original (e.g., `queryRefinement` wrapping `clarifyAndRefineUserInput`), update the call sites to use the new name.
314
+
315
+ ### What NOT to modify
316
+
317
+ - **Do not modify `ed_tools.ts` imports** — it must still import from the original source files.
318
+ - **Do not modify the original tool implementation files** (e.g., `services/apiService.ts`, `utils/queryRefinement.ts`) — they stay as-is.
319
+ - **Do not modify files that don't call tool functions** — only update files that directly import and call the wrapped tools.
320
+
321
+ ### When to ask the user
322
+
323
+ If you are not confident about a change — e.g., a function might be a utility rather than a tool, or it's unclear whether a file should use the wrapped version — **ask the user** before modifying that file. For clear tool calls (API services, database queries, LLM calls), proceed automatically.
308
324
 
309
325
  ---
310
326
 
@@ -425,7 +441,30 @@ npx ed ed-test --no-upload
425
441
 
426
442
  ---
427
443
 
428
- ## Step 8: Run and verify
444
+ ## Step 8: Validate the integration
445
+
446
+ After all files are created, validate the setup by establishing a socket.io connection to the ElasticDash backend. Run the `observe` command — it connects to the server, authenticates with the API key, and registers the project's tools and workflows:
447
+
448
+ ```bash
449
+ npx elasticdash observe
450
+ ```
451
+
452
+ **Expected output on success:**
453
+
454
+ ```
455
+ [elasticdash] Observability active
456
+ Session ID : <uuid>
457
+ Server : https://server.elasticdash.com
458
+ ```
459
+
460
+ This confirms:
461
+ - `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` are valid
462
+ - The socket.io connection to the backend is established
463
+ - The SDK can discover `ed_tools.ts` and `ed_workflows.ts`
464
+
465
+ **If it fails:** Check that `.env` has valid `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` values. If the API key is rejected, the user needs to get a new one from https://app.elasticdash.com.
466
+
467
+ After validation, stop the observe process (Ctrl+C) and inform the user that ElasticDash is integrated. Provide these commands for ongoing use:
429
468
 
430
469
  ```bash
431
470
  # Run aiTest tests
@@ -437,6 +476,9 @@ npx ed ed-test --no-upload
437
476
  # Open the dashboard
438
477
  npx elasticdash dashboard
439
478
 
479
+ # Start observability (connect to backend for live tracing)
480
+ npx elasticdash observe
481
+
440
482
  # Record a trace fixture
441
483
  ELASTICDASH_CAPTURE_TRACE=1 tsx your-workflow.ts
442
484
  ```
@@ -542,16 +584,12 @@ After integration, verify these files exist:
542
584
 
543
585
  ```
544
586
  your-project/
545
- ed_tools.ts # Instrumented tool wrappers
546
- ed_workflows.ts # Workflow exports
587
+ .env # ELASTICDASH_API_URL and ELASTICDASH_API_KEY set
588
+ ed_tools.ts # Instrumented tool wrappers (real function names, not placeholders)
589
+ ed_workflows.ts # Workflow exports using tools from ed_tools.ts
547
590
  elasticdash.config.ts # Test runner config
548
591
  package.json # dashboard:ai and test:ai scripts added
549
592
  .gitignore # .temp/ and .ed_traces/ added
550
593
  ```
551
594
 
552
- Verify with:
553
-
554
- ```bash
555
- npx elasticdash test # Should discover and run *.ai.test.ts files
556
- npx elasticdash dashboard # Should open the dashboard UI
557
- ```
595
+ Verify by running `npx elasticdash observe` — a successful socket.io connection confirms the integration is complete.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elasticdash-test",
3
- "version": "0.1.25-alpha-2",
3
+ "version": "0.1.26-alpha-1",
4
4
  "description": "AI-native test runner for ElasticDash workflow testing",
5
5
  "type": "module",
6
6
  "bin": {