elasticdash-test 0.1.18-alpha-12 → 0.1.18-alpha-14

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.
@@ -18,7 +18,6 @@ import 'dotenv/config';
18
18
  * will replay from frozen data instead of hitting real services.
19
19
  */
20
20
  import { pathToFileURL } from 'node:url';
21
- import { interceptFetch, restoreFetch } from './interceptors/http.js';
22
21
  const RESULT_PREFIX = '__ELASTICDASH_RESULT__:';
23
22
  function writeResult(result) {
24
23
  return new Promise((resolve, reject) => {
@@ -27,50 +26,73 @@ function writeResult(result) {
27
26
  }
28
27
  /**
29
28
  * Set up frozen event replay context.
30
- * - Sets the global HttpRunContext so wrapDB-wrapped clients replay frozen DB events
31
- * - Patches globalThis.fetch so HTTP calls replay frozen HTTP events
29
+ *
30
+ * For HTTP events: uses **URL-based matching** via a fetch wrapper. The
31
+ * ID-based matching in HttpRunContext doesn't work for observability-mode
32
+ * reruns because wrapTool/wrapAI also consume nextId() IDs, causing the
33
+ * sequential counter to drift away from the frozen event IDs.
34
+ *
35
+ * For DB events: uses the HttpRunContext frozen map so wrapDB-wrapped
36
+ * clients can replay them by sequential ID (DB calls don't share the
37
+ * counter with tool/AI wrappers in the same way).
32
38
  */
33
39
  async function setupFrozenContext(frozenEvents) {
34
40
  if (frozenEvents.length === 0)
35
41
  return;
36
- // Import the SDK's context setup
37
- try {
38
- const telemetryPush = await import('./interceptors/telemetry-push.js');
39
- // Build WorkflowEvent-compatible objects with sequential IDs
40
- const workflowEvents = frozenEvents.map((e, i) => ({
41
- id: i + 1,
42
- type: e.type,
43
- name: e.name,
44
- input: e.input,
45
- output: e.output,
46
- timestamp: e.timestamp,
47
- durationMs: e.durationMs ?? 0,
48
- streamed: e.streamed,
49
- streamRaw: e.streamRaw,
50
- }));
51
- // Set up the global context — this makes getHttpRunContext() return a context
52
- // with these frozen events, so wrapDB and fetch interceptors will use them.
53
- telemetryPush.setHttpRunContext('frozen-rerun', '');
54
- // Now set the frozen events on the global context
55
- const ctx = telemetryPush.getHttpRunContext();
56
- if (ctx) {
57
- for (const e of workflowEvents) {
58
- ctx.frozenEvents.set(e.id, e);
42
+ // Always install URL-based fetch replay for HTTP events — this is the
43
+ // reliable matching strategy for tool reruns in subprocesses.
44
+ installFrozenFetchFallback(frozenEvents);
45
+ // Also set up HttpRunContext for DB frozen events (wrapDB uses ID-based matching).
46
+ const dbEvents = frozenEvents.filter(e => e.type === 'db');
47
+ if (dbEvents.length > 0) {
48
+ try {
49
+ const telemetryPush = await import('./interceptors/telemetry-push.js');
50
+ const workflowEvents = dbEvents.map((e, i) => ({
51
+ id: i + 1,
52
+ type: e.type,
53
+ name: e.name,
54
+ input: e.input,
55
+ output: e.output,
56
+ timestamp: e.timestamp,
57
+ durationMs: e.durationMs ?? 0,
58
+ streamed: e.streamed,
59
+ streamRaw: e.streamRaw,
60
+ }));
61
+ telemetryPush.setHttpRunContext('frozen-rerun', '');
62
+ const ctx = telemetryPush.getHttpRunContext();
63
+ if (ctx) {
64
+ for (const e of workflowEvents) {
65
+ ctx.frozenEvents.set(e.id, e);
66
+ }
59
67
  }
60
68
  }
69
+ catch {
70
+ // DB replay not available — tool will hit real DB
71
+ }
61
72
  }
62
- catch {
63
- // SDK internals not available in this subprocess fall back to fetch-only interception
64
- installFrozenFetchFallback(frozenEvents);
73
+ }
74
+ /** Saved original fetch so we can restore after the tool runs. */
75
+ let savedOriginalFetch = null;
76
+ function restoreFrozenFetch() {
77
+ if (savedOriginalFetch) {
78
+ globalThis.fetch = savedOriginalFetch;
79
+ savedOriginalFetch = null;
65
80
  }
66
81
  }
67
82
  /**
68
- * Fallback: if SDK internals aren't importable, at least intercept fetch for HTTP events.
83
+ * Intercept fetch for HTTP frozen events using URL-based matching.
84
+ * This is the primary replay mechanism for tool reruns — it matches
85
+ * by request URL rather than sequential ID, which is robust against
86
+ * ID counter drift from wrapTool/wrapAI consuming IDs.
69
87
  */
70
88
  function installFrozenFetchFallback(frozenEvents) {
71
89
  const httpEvents = frozenEvents.filter(e => e.type === 'http');
72
- if (httpEvents.length === 0)
73
- return;
90
+ const frozenUrls = httpEvents.map(e => {
91
+ const inp = e.input;
92
+ return inp && typeof inp === 'object' && typeof inp.url === 'string' ? inp.url : '(no url)';
93
+ });
94
+ process.stderr.write(`[elasticdash-worker] Frozen HTTP events: ${httpEvents.length}, URLs: ${JSON.stringify(frozenUrls)}\n`);
95
+ savedOriginalFetch = globalThis.fetch;
74
96
  const originalFetch = globalThis.fetch;
75
97
  const usageCounts = new Map();
76
98
  globalThis.fetch = async (input, init) => {
@@ -85,13 +107,21 @@ function installFrozenFetchFallback(frozenEvents) {
85
107
  const callCount = usageCounts.get(url) || 0;
86
108
  const match = matches[Math.min(callCount, matches.length - 1)];
87
109
  usageCounts.set(url, callCount + 1);
110
+ process.stderr.write(`[elasticdash-worker] Frozen replay: ${url}\n`);
88
111
  const body = typeof match.output === 'string' ? match.output : JSON.stringify(match.output);
89
112
  return new Response(body, {
90
113
  status: 200,
91
114
  headers: { 'Content-Type': 'application/json', 'X-Frozen': 'true' }
92
115
  });
93
116
  }
94
- return originalFetch(input, init);
117
+ process.stderr.write(`[elasticdash-worker] Fetch pass-through (no frozen match): ${url}\n`);
118
+ try {
119
+ return await originalFetch(input, init);
120
+ }
121
+ catch (e) {
122
+ process.stderr.write(`[elasticdash-worker] Fetch FAILED for ${url}: ${e.stack || e.message || String(e)}\n`);
123
+ throw e;
124
+ }
95
125
  };
96
126
  }
97
127
  async function main() {
@@ -110,15 +140,12 @@ async function main() {
110
140
  return;
111
141
  }
112
142
  const { toolsModulePath, toolName, args, frozenEvents } = payload;
113
- // Set up frozen event context before running the tool
143
+ // Set up frozen event context before running the tool.
144
+ // Uses URL-based matching for HTTP events (reliable for observability reruns)
145
+ // and ID-based matching for DB events.
114
146
  const hasFrozen = frozenEvents && frozenEvents.length > 0;
115
147
  if (hasFrozen) {
116
148
  await setupFrozenContext(frozenEvents);
117
- // Install the full fetch interceptor so frozen HTTP events are replayed.
118
- // setupFrozenContext loads frozen events into HttpRunContext, but without
119
- // interceptFetch() the events are never read — fetch hits real services
120
- // which can fail (e.g. "Invalid URL" for relative URLs).
121
- interceptFetch();
122
149
  }
123
150
  try {
124
151
  let mod;
@@ -144,12 +171,13 @@ async function main() {
144
171
  catch (e) {
145
172
  const err = e;
146
173
  const errorMsg = err.stack || err.message || String(e);
174
+ process.stderr.write(`[elasticdash-worker] Tool execution failed:\n${errorMsg}\n`);
147
175
  await writeResult({ ok: false, error: errorMsg });
148
176
  originalExit(1);
149
177
  }
150
178
  finally {
151
179
  if (hasFrozen)
152
- restoreFetch();
180
+ restoreFrozenFetch();
153
181
  }
154
182
  }
155
183
  main();
@@ -1 +1 @@
1
- {"version":3,"file":"tool-runner-worker.js","sourceRoot":"","sources":["../src/tool-runner-worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-runner-worker.js","sourceRoot":"","sources":["../src/tool-runner-worker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elasticdash-test",
3
- "version": "0.1.18-alpha-12",
3
+ "version": "0.1.18-alpha-14",
4
4
  "description": "AI-native test runner for ElasticDash workflow testing",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,6 @@ import 'dotenv/config'
20
20
  */
21
21
 
22
22
  import { pathToFileURL } from 'node:url'
23
- import { interceptFetch, restoreFetch } from './interceptors/http.js'
24
23
 
25
24
  const RESULT_PREFIX = '__ELASTICDASH_RESULT__:'
26
25
 
@@ -46,53 +45,78 @@ interface FrozenEvent {
46
45
 
47
46
  /**
48
47
  * Set up frozen event replay context.
49
- * - Sets the global HttpRunContext so wrapDB-wrapped clients replay frozen DB events
50
- * - Patches globalThis.fetch so HTTP calls replay frozen HTTP events
48
+ *
49
+ * For HTTP events: uses **URL-based matching** via a fetch wrapper. The
50
+ * ID-based matching in HttpRunContext doesn't work for observability-mode
51
+ * reruns because wrapTool/wrapAI also consume nextId() IDs, causing the
52
+ * sequential counter to drift away from the frozen event IDs.
53
+ *
54
+ * For DB events: uses the HttpRunContext frozen map so wrapDB-wrapped
55
+ * clients can replay them by sequential ID (DB calls don't share the
56
+ * counter with tool/AI wrappers in the same way).
51
57
  */
52
58
  async function setupFrozenContext(frozenEvents: FrozenEvent[]): Promise<void> {
53
59
  if (frozenEvents.length === 0) return
54
60
 
55
- // Import the SDK's context setup
56
- try {
57
- const telemetryPush = await import('./interceptors/telemetry-push.js')
58
-
59
- // Build WorkflowEvent-compatible objects with sequential IDs
60
- const workflowEvents = frozenEvents.map((e, i) => ({
61
- id: i + 1,
62
- type: e.type as 'http' | 'db',
63
- name: e.name,
64
- input: e.input,
65
- output: e.output,
66
- timestamp: e.timestamp,
67
- durationMs: e.durationMs ?? 0,
68
- streamed: e.streamed,
69
- streamRaw: e.streamRaw,
70
- }))
71
-
72
- // Set up the global context — this makes getHttpRunContext() return a context
73
- // with these frozen events, so wrapDB and fetch interceptors will use them.
74
- telemetryPush.setHttpRunContext('frozen-rerun', '')
75
-
76
- // Now set the frozen events on the global context
77
- const ctx = telemetryPush.getHttpRunContext()
78
- if (ctx) {
79
- for (const e of workflowEvents) {
80
- ctx.frozenEvents.set(e.id, e as any)
61
+ // Always install URL-based fetch replay for HTTP events — this is the
62
+ // reliable matching strategy for tool reruns in subprocesses.
63
+ installFrozenFetchFallback(frozenEvents)
64
+
65
+ // Also set up HttpRunContext for DB frozen events (wrapDB uses ID-based matching).
66
+ const dbEvents = frozenEvents.filter(e => e.type === 'db')
67
+ if (dbEvents.length > 0) {
68
+ try {
69
+ const telemetryPush = await import('./interceptors/telemetry-push.js')
70
+ const workflowEvents = dbEvents.map((e, i) => ({
71
+ id: i + 1,
72
+ type: e.type as 'db',
73
+ name: e.name,
74
+ input: e.input,
75
+ output: e.output,
76
+ timestamp: e.timestamp,
77
+ durationMs: e.durationMs ?? 0,
78
+ streamed: e.streamed,
79
+ streamRaw: e.streamRaw,
80
+ }))
81
+ telemetryPush.setHttpRunContext('frozen-rerun', '')
82
+ const ctx = telemetryPush.getHttpRunContext()
83
+ if (ctx) {
84
+ for (const e of workflowEvents) {
85
+ ctx.frozenEvents.set(e.id, e as any)
86
+ }
81
87
  }
88
+ } catch {
89
+ // DB replay not available — tool will hit real DB
82
90
  }
83
- } catch {
84
- // SDK internals not available in this subprocess — fall back to fetch-only interception
85
- installFrozenFetchFallback(frozenEvents)
91
+ }
92
+ }
93
+
94
+ /** Saved original fetch so we can restore after the tool runs. */
95
+ let savedOriginalFetch: typeof globalThis.fetch | null = null
96
+
97
+ function restoreFrozenFetch(): void {
98
+ if (savedOriginalFetch) {
99
+ globalThis.fetch = savedOriginalFetch
100
+ savedOriginalFetch = null
86
101
  }
87
102
  }
88
103
 
89
104
  /**
90
- * Fallback: if SDK internals aren't importable, at least intercept fetch for HTTP events.
105
+ * Intercept fetch for HTTP frozen events using URL-based matching.
106
+ * This is the primary replay mechanism for tool reruns — it matches
107
+ * by request URL rather than sequential ID, which is robust against
108
+ * ID counter drift from wrapTool/wrapAI consuming IDs.
91
109
  */
92
110
  function installFrozenFetchFallback(frozenEvents: FrozenEvent[]): void {
93
111
  const httpEvents = frozenEvents.filter(e => e.type === 'http')
94
- if (httpEvents.length === 0) return
95
112
 
113
+ const frozenUrls = httpEvents.map(e => {
114
+ const inp = e.input as Record<string, unknown> | null
115
+ return inp && typeof inp === 'object' && typeof inp.url === 'string' ? inp.url : '(no url)'
116
+ })
117
+ process.stderr.write(`[elasticdash-worker] Frozen HTTP events: ${httpEvents.length}, URLs: ${JSON.stringify(frozenUrls)}\n`)
118
+
119
+ savedOriginalFetch = globalThis.fetch
96
120
  const originalFetch = globalThis.fetch
97
121
  const usageCounts = new Map<string, number>()
98
122
 
@@ -111,6 +135,7 @@ function installFrozenFetchFallback(frozenEvents: FrozenEvent[]): void {
111
135
  const match = matches[Math.min(callCount, matches.length - 1)]
112
136
  usageCounts.set(url, callCount + 1)
113
137
 
138
+ process.stderr.write(`[elasticdash-worker] Frozen replay: ${url}\n`)
114
139
  const body = typeof match.output === 'string' ? match.output : JSON.stringify(match.output)
115
140
  return new Response(body, {
116
141
  status: 200,
@@ -118,7 +143,13 @@ function installFrozenFetchFallback(frozenEvents: FrozenEvent[]): void {
118
143
  })
119
144
  }
120
145
 
121
- return originalFetch(input, init)
146
+ process.stderr.write(`[elasticdash-worker] Fetch pass-through (no frozen match): ${url}\n`)
147
+ try {
148
+ return await originalFetch(input, init)
149
+ } catch (e) {
150
+ process.stderr.write(`[elasticdash-worker] Fetch FAILED for ${url}: ${(e as Error).stack || (e as Error).message || String(e)}\n`)
151
+ throw e
152
+ }
122
153
  }
123
154
  }
124
155
 
@@ -141,15 +172,12 @@ async function main() {
141
172
 
142
173
  const { toolsModulePath, toolName, args, frozenEvents } = payload
143
174
 
144
- // Set up frozen event context before running the tool
175
+ // Set up frozen event context before running the tool.
176
+ // Uses URL-based matching for HTTP events (reliable for observability reruns)
177
+ // and ID-based matching for DB events.
145
178
  const hasFrozen = frozenEvents && frozenEvents.length > 0
146
179
  if (hasFrozen) {
147
180
  await setupFrozenContext(frozenEvents)
148
- // Install the full fetch interceptor so frozen HTTP events are replayed.
149
- // setupFrozenContext loads frozen events into HttpRunContext, but without
150
- // interceptFetch() the events are never read — fetch hits real services
151
- // which can fail (e.g. "Invalid URL" for relative URLs).
152
- interceptFetch()
153
181
  }
154
182
 
155
183
  try {
@@ -175,10 +203,11 @@ async function main() {
175
203
  } catch (e) {
176
204
  const err = e as Error
177
205
  const errorMsg = err.stack || err.message || String(e)
206
+ process.stderr.write(`[elasticdash-worker] Tool execution failed:\n${errorMsg}\n`)
178
207
  await writeResult({ ok: false, error: errorMsg })
179
208
  originalExit(1)
180
209
  } finally {
181
- if (hasFrozen) restoreFetch()
210
+ if (hasFrozen) restoreFrozenFetch()
182
211
  }
183
212
  }
184
213