@axlsdk/testing 0.22.3 → 0.23.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.
package/README.md CHANGED
@@ -30,6 +30,9 @@ const provider = MockProvider.sequence([
30
30
  { content: 'Hello!', usage: { prompt_tokens: 50, completion_tokens: 100, total_tokens: 150 }, cost: 0.003 },
31
31
  { content: 'World!' }, // uses defaults
32
32
  ]);
33
+ // `cost` reports identically on both paths: `chat()` returns it on the response,
34
+ // `stream()` carries it on the terminal `done` chunk. So a streamed ask feeds
35
+ // `agent_call_end.cost`, `runtime.totalCost()` and `ctx.budget` the same figure.
33
36
 
34
37
  // Per-response streaming chunks. Each response can carry an optional
35
38
  // `chunks?: string[]` that drives the streaming path one delta per chunk.
@@ -38,6 +41,15 @@ const provider = MockProvider.sequence([
38
41
  { content: 'Hello world', chunks: ['Hel', 'lo ', 'world'] },
39
42
  ]);
40
43
 
44
+ // Deterministic provider timing. An optional `timing` block (`CallTiming`)
45
+ // is returned on `ProviderResponse.timing` and on the streamed `done` chunk,
46
+ // so `agent_call_end.timing`, the `TimeoutError` breakdown, and the eval
47
+ // per-model rollup get exact integers instead of measured deltas. Omit it and
48
+ // the mock looks like an uninstrumented provider — the key is absent, not zero.
49
+ const provider = MockProvider.sequence([
50
+ { content: 'Hi', timing: { queuedMs: 40, attempts: 1, retryMs: 0, ttfbMs: 12, wireMs: 90 } },
51
+ ]);
52
+
41
53
  // Chunked mode — convenience over `sequence()`. Takes plain content
42
54
  // strings and splits each into fixed-size chunks (default 4 chars ≈
43
55
  // 1 token). Use to exercise partial-JSON parsing, structural-boundary
@@ -128,6 +140,11 @@ second callback declares model capabilities. Inline bytes, provider options,
128
140
  and results are defensively copied; recorded requests omit `AbortSignal`, while
129
141
  the fixture callback receives the original signal so cancellation is testable.
130
142
 
143
+ Separately, `MockProvider.chat()` and `.stream()` honor `ChatOptions.signal`:
144
+ pre-abort prevents fixture work and an in-flight abort rejects with the exact reason. This supports
145
+ deterministic tests of ask-local `signal` and `stallTimeout`; it is not a
146
+ certification of a real provider's network-abort behavior.
147
+
131
148
  ### `MockTool`
132
149
 
133
150
  Create a mock tool to intercept and record calls:
package/dist/index.cjs CHANGED
@@ -116,6 +116,52 @@ function cloneOptions(options) {
116
116
  ...options.providerOptions ? { providerOptions: cloneValue(options.providerOptions) } : {}
117
117
  };
118
118
  }
119
+ function throwIfAborted(signal) {
120
+ if (signal?.aborted) throw signal.reason;
121
+ }
122
+ async function awaitWithSignal(value, signal) {
123
+ if (!signal) return await value;
124
+ throwIfAborted(signal);
125
+ return await new Promise((resolve, reject) => {
126
+ const onAbort = () => reject(signal.reason);
127
+ signal.addEventListener("abort", onAbort, { once: true });
128
+ Promise.resolve(value).then(
129
+ (result) => {
130
+ signal.removeEventListener("abort", onAbort);
131
+ resolve(result);
132
+ },
133
+ (error) => {
134
+ signal.removeEventListener("abort", onAbort);
135
+ reject(error);
136
+ }
137
+ );
138
+ });
139
+ }
140
+ async function abortableDelay(ms, signal) {
141
+ if (ms <= 0) {
142
+ throwIfAborted(signal);
143
+ return;
144
+ }
145
+ if (!signal) {
146
+ await new Promise((resolve) => setTimeout(resolve, ms));
147
+ return;
148
+ }
149
+ throwIfAborted(signal);
150
+ await new Promise((resolve, reject) => {
151
+ let timer = setTimeout(() => {
152
+ timer = void 0;
153
+ signal.removeEventListener("abort", onAbort);
154
+ resolve();
155
+ }, ms);
156
+ const onAbort = () => {
157
+ if (timer !== void 0) clearTimeout(timer);
158
+ timer = void 0;
159
+ signal.removeEventListener("abort", onAbort);
160
+ reject(signal.reason);
161
+ };
162
+ signal.addEventListener("abort", onAbort, { once: true });
163
+ });
164
+ }
119
165
  function richParts(input) {
120
166
  return typeof input === "string" ? [] : input;
121
167
  }
@@ -208,8 +254,13 @@ var MockProvider = class _MockProvider {
208
254
  }
209
255
  async chat(messages, options) {
210
256
  const recordedMessages = cloneMessages(messages);
257
+ const callIndex = this._calls.length;
211
258
  this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });
212
- return await this.responseFn(cloneMessages(messages), this._calls.length - 1);
259
+ throwIfAborted(options.signal);
260
+ const response = Promise.resolve().then(
261
+ () => this.responseFn(cloneMessages(messages), callIndex)
262
+ );
263
+ return await awaitWithSignal(response, options.signal);
213
264
  }
214
265
  inputCapabilities(_model) {
215
266
  return { image: { sources: ["url", "bytes", "base64", "provider-file"] } };
@@ -247,6 +298,7 @@ var MockProvider = class _MockProvider {
247
298
  async *stream(messages, options) {
248
299
  const callIndex = this._calls.length;
249
300
  const response = await this.chat(messages, options);
301
+ throwIfAborted(options.signal);
250
302
  const chunks = this.chunkSequence?.[callIndex];
251
303
  if (chunks && chunks.length > 0) {
252
304
  const joined = chunks.join("");
@@ -256,16 +308,19 @@ var MockProvider = class _MockProvider {
256
308
  );
257
309
  }
258
310
  for (let i = 0; i < chunks.length; i++) {
311
+ throwIfAborted(options.signal);
259
312
  yield { type: "text_delta", content: chunks[i] };
260
313
  if (this.chunkDelayMs > 0 && i < chunks.length - 1) {
261
- await new Promise((resolve) => setTimeout(resolve, this.chunkDelayMs));
314
+ await abortableDelay(this.chunkDelayMs, options.signal);
262
315
  }
263
316
  }
264
317
  } else if (response.content) {
318
+ throwIfAborted(options.signal);
265
319
  yield { type: "text_delta", content: response.content };
266
320
  }
267
321
  if (response.tool_calls) {
268
322
  for (const tc of response.tool_calls) {
323
+ throwIfAborted(options.signal);
269
324
  yield {
270
325
  type: "tool_call_delta",
271
326
  id: tc.id,
@@ -274,7 +329,21 @@ var MockProvider = class _MockProvider {
274
329
  };
275
330
  }
276
331
  }
277
- yield { type: "done", usage: response.usage, providerMetadata: response.providerMetadata };
332
+ throwIfAborted(options.signal);
333
+ yield {
334
+ type: "done",
335
+ usage: response.usage,
336
+ // Forwarded like `chat()` does. Without it a streamed mock ask reported no
337
+ // cost at all, so `ctx.budget` and every cost assertion silently saw zero
338
+ // on the streaming path while the same fixture priced correctly on the
339
+ // non-streaming one — a mock that disagrees with itself between paths.
340
+ cost: response.cost,
341
+ providerMetadata: response.providerMetadata,
342
+ // Conditional spread, not `timing: response.timing` — a mock response with
343
+ // no timing must produce a `done` chunk with NO `timing` key at all, so a
344
+ // consumer's `'timing' in chunk` check reads the same as a real adapter's.
345
+ ...response.timing ? { timing: response.timing } : {}
346
+ };
278
347
  }
279
348
  static sequence(responses) {
280
349
  const provider = new _MockProvider((_messages, callIndex) => {
@@ -289,7 +358,11 @@ var MockProvider = class _MockProvider {
289
358
  tool_calls: resp.tool_calls,
290
359
  providerMetadata: resp.providerMetadata,
291
360
  usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
292
- cost: resp.cost ?? 0
361
+ cost: resp.cost ?? 0,
362
+ // Copied, not shared: the caller keeps ownership of the fixture, so a
363
+ // consumer that mutates the returned block cannot corrupt a later call's
364
+ // expectation (same rule the message/options clones follow).
365
+ ...resp.timing ? { timing: { ...resp.timing } } : {}
293
366
  };
294
367
  });
295
368
  provider.chunkSequence = responses.map((r) => r.chunks);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/mock-provider.ts","../src/mock-tool.ts","../src/mock-transcription-provider.ts","../src/test-runtime.ts"],"sourcesContent":["export { AxlTestRuntime } from './test-runtime.js';\nexport type { RecordedToolCall, RecordedAgentCall, RecordedStep } from './test-runtime.js';\nexport { MockProvider } from './mock-provider.js';\nexport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nexport { MockTool } from './mock-tool.js';\n","import { readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport type {\n ChatMessage,\n ChatOptions,\n InputContentPart,\n InputMediaSource,\n ModelInput,\n ToolCallMessage,\n ProviderResponse,\n StreamChunk,\n Provider,\n ProviderInputValidationRequest,\n ProviderInputValidationResult,\n EffortResolution,\n} from '@axlsdk/axl';\nimport { inputText, UnsupportedModelInputError } from '@axlsdk/axl';\n\nfunction cloneValue(value: unknown): unknown {\n if (value instanceof Uint8Array) return value.slice();\n if (Array.isArray(value)) return value.map(cloneValue);\n if (value && typeof value === 'object') {\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneValue(item)]));\n }\n return value;\n}\n\nfunction cloneModelInput(input: ModelInput): ModelInput {\n if (typeof input === 'string') return input;\n return input.map((part): InputContentPart => {\n if (part.type === 'text') return { type: 'text', text: part.text };\n const { source } = part;\n switch (source.type) {\n case 'bytes':\n return {\n type: 'image',\n source: { type: 'bytes', data: source.data.slice(), mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'url':\n return {\n type: 'image',\n source: {\n type: 'url',\n url: source.url,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'base64':\n return {\n type: 'image',\n source: { type: 'base64', data: source.data, mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'provider-file':\n return {\n type: 'image',\n source: {\n type: 'provider-file',\n provider: source.provider,\n reference: source.reference,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n }\n });\n}\n\nfunction cloneMessages(messages: readonly ChatMessage[]): ChatMessage[] {\n return messages.map((message) => ({\n ...message,\n content: cloneModelInput(message.content),\n ...(message.tool_calls\n ? {\n tool_calls: message.tool_calls.map((toolCall) => ({\n ...toolCall,\n function: { ...toolCall.function },\n })),\n }\n : {}),\n ...(message.providerMetadata\n ? { providerMetadata: cloneValue(message.providerMetadata) as Record<string, unknown> }\n : {}),\n }));\n}\n\nfunction cloneOptions(options: ChatOptions): ChatOptions {\n return {\n ...options,\n ...(options.tools\n ? {\n tools: options.tools.map((tool) => ({\n ...tool,\n function: { ...tool.function, parameters: cloneValue(tool.function.parameters) },\n })),\n }\n : {}),\n ...(options.providerOptions\n ? { providerOptions: cloneValue(options.providerOptions) as Record<string, unknown> }\n : {}),\n };\n}\n\nfunction richParts(input: ModelInput): readonly InputContentPart[] {\n return typeof input === 'string' ? [] : input;\n}\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: unknown): unknown {\n if (schema instanceof z.ZodString) return randomAlphanumeric(8 + Math.floor(Math.random() * 13));\n if (schema instanceof z.ZodNumber) {\n const min = Number.isFinite(schema.minValue) ? schema.minValue! : 0;\n const max = Number.isFinite(schema.maxValue) ? schema.maxValue! : 100;\n return min + Math.random() * (max - min);\n }\n if (schema instanceof z.ZodBoolean) return Math.random() < 0.5;\n if (schema instanceof z.ZodArray) {\n const count = 1 + Math.floor(Math.random() * 3);\n return Array.from({ length: count }, () => generateFromSchema(schema.element));\n }\n if (schema instanceof z.ZodObject) {\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema.shape)) obj[key] = generateFromSchema(value);\n return obj;\n }\n if (schema instanceof z.ZodOptional) {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(schema.unwrap());\n }\n if (schema instanceof z.ZodDefault) return schema.parse(undefined);\n if (schema instanceof z.ZodEnum) {\n const values = schema.options;\n return values.length > 0 ? values[Math.floor(Math.random() * values.length)] : '';\n }\n if (schema instanceof z.ZodLiteral) return [...schema.values][0];\n if (schema instanceof z.ZodNullable) return null;\n if (schema instanceof z.ZodUnion) {\n const options = schema.options;\n return generateFromSchema(options[Math.floor(Math.random() * options.length)]);\n }\n return {};\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: ChatOptions }[] = [];\n /** Per-call optional chunk arrays, set by `sequence()` / `chunked()` so\n * `stream()` can yield one `text_delta` per chunk. Per-call indexed\n * alongside the response sequence. */\n private chunkSequence?: Array<string[] | undefined>;\n /** Optional ms delay between successive chunks during `stream()`. Useful\n * for dev fixtures that need to demonstrate streaming UX visually\n * (without a delay, mock chunks fire synchronously and complete in\n * microseconds — invisible to a human watching). Default 0 keeps\n * every existing test fast.\n *\n * Set via `provider.chunkDelayMs = 50` after construction. Not part of\n * any factory's surface to keep the test API minimal. */\n chunkDelayMs = 0;\n\n /** How this mock reports the unified `effort` it was asked for. Undefined (the\n * default) means the mock implements no `effortResolution` at all, so the\n * runtime emits no `provider_diagnostic`. Set it with\n * {@link MockProvider.withEffortResolution} to drive the clamp-reporting path. */\n effortResolution?: (\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined;\n\n /**\n * Make this mock report an effort clamp, so a test can exercise the runtime's\n * `provider_diagnostic` path without a real adapter. Pass a fixed\n * {@link EffortResolution} or a function of the request knobs.\n *\n * ```ts\n * const provider = MockProvider.echo().withEffortResolution({\n * requested: 'none',\n * effective: 'minimal',\n * clamped: true,\n * cause: 'this model cannot disable thinking',\n * });\n * ```\n */\n withEffortResolution(\n resolution:\n | EffortResolution\n | undefined\n | ((\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined),\n ): this {\n this.effortResolution = typeof resolution === 'function' ? resolution : () => resolution;\n return this;\n }\n\n private constructor(\n private responseFn: (\n messages: ChatMessage[],\n callIndex: number,\n ) => ProviderResponse | Promise<ProviderResponse>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse> {\n // Each consumer gets independent ownership: a handler can inspect or mutate\n // its copy without changing the durable assertion record (including bytes).\n const recordedMessages = cloneMessages(messages);\n this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });\n return await this.responseFn(cloneMessages(messages), this._calls.length - 1);\n }\n\n inputCapabilities(_model: string): { image: { sources: readonly InputMediaSource['type'][] } } {\n return { image: { sources: ['url', 'bytes', 'base64', 'provider-file'] } };\n }\n\n validateInput(request: ProviderInputValidationRequest): ProviderInputValidationResult {\n const effectiveModel =\n typeof request.providerOptions?.model === 'string'\n ? request.providerOptions.model\n : request.model;\n const fail = (source?: string, feature?: string): never => {\n throw new UnsupportedModelInputError({\n provider: this.name,\n model: effectiveModel || request.model,\n modality: 'image',\n ...(source ? { source } : {}),\n ...(feature ? { feature } : {}),\n });\n };\n\n if (request.providerOptions && 'input' in request.providerOptions) {\n fail(undefined, 'raw input-container providerOptions');\n }\n for (const message of request.history) {\n if (typeof message.content === 'string') continue;\n if (message.role !== 'user') fail(undefined, 'rich non-user history');\n for (const part of richParts(message.content)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n }\n for (const part of richParts(request.input)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n return { effectiveModel };\n }\n\n async *stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk> {\n const callIndex = this._calls.length;\n const response = await this.chat(messages, options);\n const chunks = this.chunkSequence?.[callIndex];\n if (chunks && chunks.length > 0) {\n // Sanity guard — if a caller passes chunks AND content, they MUST\n // match. Otherwise tests pass while the real prod content silently\n // diverges from what the streaming path observes.\n const joined = chunks.join('');\n if (joined !== response.content) {\n throw new Error(\n `MockProvider.stream: chunks.join('') !== content. ` +\n `chunks=\"${joined}\" content=\"${response.content}\"`,\n );\n }\n for (let i = 0; i < chunks.length; i++) {\n yield { type: 'text_delta', content: chunks[i] };\n if (this.chunkDelayMs > 0 && i < chunks.length - 1) {\n await new Promise((resolve) => setTimeout(resolve, this.chunkDelayMs));\n }\n }\n } else if (response.content) {\n yield { type: 'text_delta', content: response.content };\n }\n if (response.tool_calls) {\n for (const tc of response.tool_calls) {\n yield {\n type: 'tool_call_delta',\n id: tc.id,\n name: tc.function.name,\n arguments: tc.function.arguments,\n };\n }\n }\n yield { type: 'done', usage: response.usage, providerMetadata: response.providerMetadata };\n }\n\n static sequence(\n responses: Array<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** When set, `stream()` yields one `text_delta` per entry instead\n * of one big delta with the full content. Use to exercise\n * partial-JSON parsing, structural-boundary throttling, and\n * cross-attempt token retention in tests. Must satisfy\n * `chunks.join('') === content`. */\n chunks?: string[];\n }>,\n ): MockProvider {\n const provider = new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n const resp = responses[callIndex];\n return {\n content: resp.content,\n tool_calls: resp.tool_calls,\n providerMetadata: resp.providerMetadata,\n usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: resp.cost ?? 0,\n };\n });\n provider.chunkSequence = responses.map((r) => r.chunks);\n return provider;\n }\n\n /**\n * Convenience: build a `sequence()` from plain content strings, splitting\n * each one into fixed-size chunks for the streaming path. Default\n * `chunkSize` is 4 chars (≈1 token).\n */\n static chunked(contents: string[], chunkSize = 4): MockProvider {\n const responses = contents.map((content) => {\n const chunks: string[] = [];\n for (let i = 0; i < content.length; i += chunkSize) {\n chunks.push(content.slice(i, i + chunkSize));\n }\n return { content, chunks };\n });\n return MockProvider.sequence(responses);\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser ? inputText(lastUser.content) : '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: unknown): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) =>\n | {\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n }\n | Promise<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n }>,\n ): MockProvider {\n return new MockProvider(async (messages, callIndex) => {\n const result = await handler(messages, callIndex);\n return {\n ...result,\n usage: result.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: result.cost ?? 0,\n };\n });\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: unknown) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: unknown) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import type {\n TranscriptionCapabilities,\n TranscriptionProvider,\n TranscriptionProviderRequest,\n TranscriptionProviderResult,\n} from '@axlsdk/axl';\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction cloneRequest(\n request: TranscriptionProviderRequest,\n includeSignal: boolean,\n): TranscriptionProviderRequest {\n const audio =\n request.audio.type === 'bytes'\n ? { ...request.audio, data: request.audio.data.slice() }\n : { ...request.audio };\n return {\n model: request.model,\n audio,\n ...(request.language !== undefined ? { language: request.language } : {}),\n ...(request.timestamps !== undefined ? { timestamps: request.timestamps } : {}),\n ...(request.diarization !== undefined ? { diarization: request.diarization } : {}),\n ...(request.providerOptions ? { providerOptions: clone(request.providerOptions) } : {}),\n ...(includeSignal && request.signal ? { signal: request.signal } : {}),\n };\n}\n\n/** Dedicated transcription mock. It intentionally does not extend MockProvider,\n * so chat fixtures cannot accidentally become transcription adapters. */\nexport class MockTranscriptionProvider implements TranscriptionProvider {\n private _calls: TranscriptionProviderRequest[] = [];\n\n constructor(\n private readonly response: (\n request: TranscriptionProviderRequest,\n callIndex: number,\n ) => TranscriptionProviderResult | Promise<TranscriptionProviderResult>,\n private readonly capabilitiesFor: (\n model: string,\n ) => TranscriptionCapabilities | undefined = () => ({\n sources: ['bytes', 'base64', 'provider-file'],\n timestamps: ['segment', 'word'],\n diarization: true,\n }),\n ) {}\n\n get calls(): readonly TranscriptionProviderRequest[] {\n return this._calls.map(clone);\n }\n\n capabilities(model: string): TranscriptionCapabilities | undefined {\n return this.capabilitiesFor(model);\n }\n\n async transcribe(request: TranscriptionProviderRequest): Promise<TranscriptionProviderResult> {\n // AbortSignal cannot be structured-cloned. Keep it only on the callback\n // request so fixtures can observe cancellation; recorded calls omit it.\n const snapshot = cloneRequest(request, false);\n this._calls.push(snapshot);\n return clone(await this.response(cloneRequest(request, true), this._calls.length - 1));\n }\n\n static text(text: string): MockTranscriptionProvider {\n return new MockTranscriptionProvider(() => ({ transcript: { text } }));\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nimport type {\n AxlEvent,\n AwaitHumanOptions,\n HumanDecision,\n ProviderResponse,\n AgentCallInfo,\n AxlConfig,\n AnyWorkflow,\n ToolCallOutcome,\n ObservationStatus,\n} from '@axlsdk/axl';\nimport {\n WorkflowContext,\n MemoryStore,\n ProviderRegistry,\n TranscriptionProviderRegistry,\n eventCostContribution,\n isUnpricedLeaf,\n} from '@axlsdk/axl';\nimport type { EventStreamOptions, WorkflowContextInit } from '@axlsdk/axl';\n\n// `AnyWorkflow` is a re-exported alias for `Workflow<any, any>` — see\n// the JSDoc on `AnyWorkflow` in `@axlsdk/axl/workflow.ts`. The bivariant\n// `any` is load-bearing for satisfying narrowly-typed handlers (e.g., a\n// `Workflow<TInput=MsgType>` whose handler takes `WorkflowContext<MsgType>`).\n\n/** One accepted provider-issued tool invocation, recorded from its terminal v2 event.\n *\n * Narrow `outcome.status` before reading status-specific fields. Correlation uses\n * `(executionId, askId, callId)`; `name` is the canonical tool name.\n */\nexport type RecordedToolCall = {\n name: string;\n executionId: string;\n askId: string;\n callId: string;\n args: unknown;\n requestedTool?: string;\n outcome: ToolCallOutcome;\n};\nexport type RecordedAgentCall = Partial<AgentCallInfo> &\n Pick<AgentCallInfo, 'agent' | 'prompt' | 'response'>;\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction preserveDrainCause(error: Error, cause: unknown): Error {\n if (cause === undefined || cause === error || 'cause' in error) return error;\n Object.defineProperty(error, 'cause', {\n configurable: true,\n enumerable: false,\n value: cause,\n });\n return error;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n /** AxlConfig for the test runtime. Controls trace level/redact, context management, etc.\n * Defaults to `{}`. Set `{ trace: { level: 'full' } }` to verify verbose-mode snapshots\n * in tests, or `{ trace: { redact: true } }` to verify redaction policy in tests. */\n config?: AxlConfig;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, AnyWorkflow>();\n private mockProviders = new Map<string, MockProvider>();\n private mockTranscriptionProviders = new Map<string, MockTranscriptionProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: AxlEvent[] = [];\n private _totalCost = 0;\n private _unpriced = false;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n private _config: AxlConfig;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n this._config = options?.config ?? {};\n }\n\n register(workflow: AnyWorkflow): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTranscriptionProvider(name: string, provider: MockTranscriptionProvider): void {\n this.mockTranscriptionProviders.set(name, provider);\n }\n\n mockTool<TInput = unknown>(\n name: string,\n handler: (input: TInput) => unknown | Promise<unknown>,\n ): void {\n // The MockTool registry is type-erased (`(input: unknown) => unknown`)\n // because the runtime can't statically know each tool's input shape; tests\n // opt in to a typed input via the generic, and we cast on storage.\n this.mockToolMap.set(name, MockTool.create(name, handler as (input: unknown) => unknown));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: {\n metadata?: Record<string, unknown>;\n events?: EventStreamOptions;\n branchDrainTimeoutMs?: number;\n },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._unpriced = false;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider);\n }\n const transcriptionRegistry = new TranscriptionProviderRegistry();\n for (const [name, provider] of this.mockTranscriptionProviders) {\n transcriptionRegistry.registerInstance(name, provider);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Previously `_recordStep('workflow_start', ...)` was called here with\n // raw `{ workflow, input }` data, AND `ctx._emitWorkflowStart` below\n // pushed through the onTrace handler (which also bumps `_stepCounter`\n // and appends to `_steps`). Result: two entries in `steps()` per\n // workflow_start, with the first bypassing `config.trace.redact`.\n // The `_recordStep` call is removed — onTrace is the single source\n // of truth for `_steps`, matching the behavior of every other event\n // type in the test runtime. Reviewer architecture §5.\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n // Thread the constructor-provided config so tests can exercise\n // trace.level === 'full' and trace.redact behavior end-to-end.\n config: this._config,\n // Thread `events` config so tests can exercise the queue cap and\n // overflow policy on `ctx.events`. Mirrors the production runtime.\n eventStreamOptions: options?.events,\n branchDrainTimeoutMs: options?.branchDrainTimeoutMs,\n // Production runtime threads this (see `runtime.ts:594` in\n // `execute()` and `:765` in `stream()`). `emitEvent` auto-stamps\n // `event.workflow` from it, so consumers grouping by workflow\n // (Cost Dashboard `byWorkflow`, `trackExecution.metadata.workflows`,\n // any eval runner) see the same attribution in tests and prod.\n workflowName,\n providerRegistry: registry,\n transcriptionProviderRegistry: transcriptionRegistry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: AxlEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({\n step: this._stepCounter,\n type: event.type,\n data: (event as { data?: unknown }).data,\n });\n\n if (event.type === 'tool_call_end') {\n const { args, requestedTool, outcome } = event.data;\n this._toolCalls.push({\n name: event.tool,\n executionId: event.executionId,\n askId: event.askId,\n callId: event.callId,\n args,\n ...(requestedTool !== undefined ? { requestedTool } : {}),\n outcome,\n });\n }\n\n // Single-source-of-truth cost accumulator. Skips ask_end\n // rollups (decision 10) and guards NaN/Infinity.\n this._totalCost += eventCostContribution(event);\n // Honest aggregate: one unpriced leaf makes `totalCost()` a lower bound.\n if (isUnpricedLeaf(event)) this._unpriced = true;\n\n // Track provider responses for recording\n if (event.type === 'agent_call_end' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: event.tokens\n ? {\n prompt_tokens: event.tokens.input ?? 0,\n completion_tokens: event.tokens.output ?? 0,\n total_tokens:\n (event.tokens.input ?? 0) +\n (event.tokens.output ?? 0) +\n (event.tokens.reasoning ?? 0),\n ...(event.tokens.reasoning !== undefined\n ? { reasoning_tokens: event.tokens.reasoning }\n : {}),\n }\n : { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n ...(event.cost !== undefined ? { cost: event.cost } : {}),\n });\n }\n }\n },\n onAgentCallComplete: (call: AgentCallInfo) => {\n this._agentCalls.push(call);\n },\n };\n\n const ctx = new WorkflowContext(init);\n\n // Emit workflow_start through the same pipeline production uses,\n // AFTER ctx exists so `emitEvent`'s redact/step/timestamp handling\n // applies. Previously emitted via `_pushTrace` which bypassed\n // redaction — `config: { trace: { redact: true } }` on the test\n // runtime would scrub intermediate events but leak `input`/`result`\n // through the synthesized workflow_start/end.\n const startedAt = Date.now();\n ctx._emitWorkflowStart(validated);\n try {\n const result = await workflow.handler(ctx);\n const observation = await ctx._drainBranchWork();\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n ctx._emitWorkflowEnd({\n status: 'completed',\n duration: Date.now() - startedAt,\n result,\n observation,\n });\n // `_steps` is populated by onTrace for every event (including\n // workflow_end); no separate `_recordStep` call needed — that\n // would double-count.\n\n if (this.recordPath) await this._writeRecording();\n return result;\n } catch (err) {\n let workflowError = err;\n let observation: ObservationStatus = { complete: true };\n try {\n observation = await ctx._drainBranchWork();\n } catch (drainError) {\n workflowError =\n drainError instanceof Error ? preserveDrainCause(drainError, workflowError) : drainError;\n }\n // Parity with production: failed workflows get a terminal\n // `workflow_end(status: 'failed')` event so consumers counting\n // workflow_start ↔ workflow_end pairs never see an unclosed one.\n // `aborted` flag mirrors the runtime.ts AbortError detection.\n const aborted =\n typeof workflowError === 'object' &&\n workflowError !== null &&\n (workflowError as { name?: unknown }).name === 'AbortError';\n ctx._emitWorkflowEnd({\n status: 'failed',\n duration: Date.now() - startedAt,\n error: workflowError instanceof Error ? workflowError.message : String(workflowError),\n ...(aborted ? { aborted: true } : {}),\n observation,\n });\n // `_steps` populated by onTrace (see comment on the success path).\n throw workflowError;\n }\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n /** True when any cost-bearing call was unpriced — `totalCost()` is then a\n * LOWER BOUND. Mirrors `ExecutionInfo.unpriced` / `BudgetResult.unpriced`. */\n unpriced(): boolean {\n return this._unpriced;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): AxlEvent[] {\n return this._traceLog;\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["import_axl"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/mock-provider.ts","../src/mock-tool.ts","../src/mock-transcription-provider.ts","../src/test-runtime.ts"],"sourcesContent":["export { AxlTestRuntime } from './test-runtime.js';\nexport type { RecordedToolCall, RecordedAgentCall, RecordedStep } from './test-runtime.js';\nexport { MockProvider } from './mock-provider.js';\nexport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nexport { MockTool } from './mock-tool.js';\n","import { readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport type {\n CallTiming,\n ChatMessage,\n ChatOptions,\n InputContentPart,\n InputMediaSource,\n ModelInput,\n ToolCallMessage,\n ProviderResponse,\n StreamChunk,\n Provider,\n ProviderInputValidationRequest,\n ProviderInputValidationResult,\n EffortResolution,\n} from '@axlsdk/axl';\nimport { inputText, UnsupportedModelInputError } from '@axlsdk/axl';\n\nfunction cloneValue(value: unknown): unknown {\n if (value instanceof Uint8Array) return value.slice();\n if (Array.isArray(value)) return value.map(cloneValue);\n if (value && typeof value === 'object') {\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneValue(item)]));\n }\n return value;\n}\n\nfunction cloneModelInput(input: ModelInput): ModelInput {\n if (typeof input === 'string') return input;\n return input.map((part): InputContentPart => {\n if (part.type === 'text') return { type: 'text', text: part.text };\n const { source } = part;\n switch (source.type) {\n case 'bytes':\n return {\n type: 'image',\n source: { type: 'bytes', data: source.data.slice(), mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'url':\n return {\n type: 'image',\n source: {\n type: 'url',\n url: source.url,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'base64':\n return {\n type: 'image',\n source: { type: 'base64', data: source.data, mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'provider-file':\n return {\n type: 'image',\n source: {\n type: 'provider-file',\n provider: source.provider,\n reference: source.reference,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n }\n });\n}\n\nfunction cloneMessages(messages: readonly ChatMessage[]): ChatMessage[] {\n return messages.map((message) => ({\n ...message,\n content: cloneModelInput(message.content),\n ...(message.tool_calls\n ? {\n tool_calls: message.tool_calls.map((toolCall) => ({\n ...toolCall,\n function: { ...toolCall.function },\n })),\n }\n : {}),\n ...(message.providerMetadata\n ? { providerMetadata: cloneValue(message.providerMetadata) as Record<string, unknown> }\n : {}),\n }));\n}\n\nfunction cloneOptions(options: ChatOptions): ChatOptions {\n return {\n ...options,\n ...(options.tools\n ? {\n tools: options.tools.map((tool) => ({\n ...tool,\n function: { ...tool.function, parameters: cloneValue(tool.function.parameters) },\n })),\n }\n : {}),\n ...(options.providerOptions\n ? { providerOptions: cloneValue(options.providerOptions) as Record<string, unknown> }\n : {}),\n };\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw signal.reason;\n}\n\nasync function awaitWithSignal<T>(\n value: PromiseLike<T> | T,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (!signal) return await value;\n throwIfAborted(signal);\n\n return await new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n Promise.resolve(value).then(\n (result) => {\n signal.removeEventListener('abort', onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nasync function abortableDelay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n if (ms <= 0) {\n throwIfAborted(signal);\n return;\n }\n if (!signal) {\n await new Promise<void>((resolve) => setTimeout(resolve, ms));\n return;\n }\n throwIfAborted(signal);\n\n await new Promise<void>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined = setTimeout(() => {\n timer = undefined;\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n signal.removeEventListener('abort', onAbort);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nfunction richParts(input: ModelInput): readonly InputContentPart[] {\n return typeof input === 'string' ? [] : input;\n}\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: unknown): unknown {\n if (schema instanceof z.ZodString) return randomAlphanumeric(8 + Math.floor(Math.random() * 13));\n if (schema instanceof z.ZodNumber) {\n const min = Number.isFinite(schema.minValue) ? schema.minValue! : 0;\n const max = Number.isFinite(schema.maxValue) ? schema.maxValue! : 100;\n return min + Math.random() * (max - min);\n }\n if (schema instanceof z.ZodBoolean) return Math.random() < 0.5;\n if (schema instanceof z.ZodArray) {\n const count = 1 + Math.floor(Math.random() * 3);\n return Array.from({ length: count }, () => generateFromSchema(schema.element));\n }\n if (schema instanceof z.ZodObject) {\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema.shape)) obj[key] = generateFromSchema(value);\n return obj;\n }\n if (schema instanceof z.ZodOptional) {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(schema.unwrap());\n }\n if (schema instanceof z.ZodDefault) return schema.parse(undefined);\n if (schema instanceof z.ZodEnum) {\n const values = schema.options;\n return values.length > 0 ? values[Math.floor(Math.random() * values.length)] : '';\n }\n if (schema instanceof z.ZodLiteral) return [...schema.values][0];\n if (schema instanceof z.ZodNullable) return null;\n if (schema instanceof z.ZodUnion) {\n const options = schema.options;\n return generateFromSchema(options[Math.floor(Math.random() * options.length)]);\n }\n return {};\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: ChatOptions }[] = [];\n /** Per-call optional chunk arrays, set by `sequence()` / `chunked()` so\n * `stream()` can yield one `text_delta` per chunk. Per-call indexed\n * alongside the response sequence. */\n private chunkSequence?: Array<string[] | undefined>;\n /** Optional ms delay between successive chunks during `stream()`. Useful\n * for dev fixtures that need to demonstrate streaming UX visually\n * (without a delay, mock chunks fire synchronously and complete in\n * microseconds — invisible to a human watching). Default 0 keeps\n * every existing test fast.\n *\n * Set via `provider.chunkDelayMs = 50` after construction. Not part of\n * any factory's surface to keep the test API minimal. */\n chunkDelayMs = 0;\n\n /** How this mock reports the unified `effort` it was asked for. Undefined (the\n * default) means the mock implements no `effortResolution` at all, so the\n * runtime emits no `provider_diagnostic`. Set it with\n * {@link MockProvider.withEffortResolution} to drive the clamp-reporting path. */\n effortResolution?: (\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined;\n\n /**\n * Make this mock report an effort clamp, so a test can exercise the runtime's\n * `provider_diagnostic` path without a real adapter. Pass a fixed\n * {@link EffortResolution} or a function of the request knobs.\n *\n * ```ts\n * const provider = MockProvider.echo().withEffortResolution({\n * requested: 'none',\n * effective: 'minimal',\n * clamped: true,\n * cause: 'this model cannot disable thinking',\n * });\n * ```\n */\n withEffortResolution(\n resolution:\n | EffortResolution\n | undefined\n | ((\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined),\n ): this {\n this.effortResolution = typeof resolution === 'function' ? resolution : () => resolution;\n return this;\n }\n\n private constructor(\n private responseFn: (\n messages: ChatMessage[],\n callIndex: number,\n ) => ProviderResponse | Promise<ProviderResponse>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse> {\n // Each consumer gets independent ownership: a handler can inspect or mutate\n // its copy without changing the durable assertion record (including bytes).\n const recordedMessages = cloneMessages(messages);\n const callIndex = this._calls.length;\n this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });\n throwIfAborted(options.signal);\n const response = Promise.resolve().then(() =>\n this.responseFn(cloneMessages(messages), callIndex),\n );\n return await awaitWithSignal(response, options.signal);\n }\n\n inputCapabilities(_model: string): { image: { sources: readonly InputMediaSource['type'][] } } {\n return { image: { sources: ['url', 'bytes', 'base64', 'provider-file'] } };\n }\n\n validateInput(request: ProviderInputValidationRequest): ProviderInputValidationResult {\n const effectiveModel =\n typeof request.providerOptions?.model === 'string'\n ? request.providerOptions.model\n : request.model;\n const fail = (source?: string, feature?: string): never => {\n throw new UnsupportedModelInputError({\n provider: this.name,\n model: effectiveModel || request.model,\n modality: 'image',\n ...(source ? { source } : {}),\n ...(feature ? { feature } : {}),\n });\n };\n\n if (request.providerOptions && 'input' in request.providerOptions) {\n fail(undefined, 'raw input-container providerOptions');\n }\n for (const message of request.history) {\n if (typeof message.content === 'string') continue;\n if (message.role !== 'user') fail(undefined, 'rich non-user history');\n for (const part of richParts(message.content)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n }\n for (const part of richParts(request.input)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n return { effectiveModel };\n }\n\n async *stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk> {\n const callIndex = this._calls.length;\n const response = await this.chat(messages, options);\n throwIfAborted(options.signal);\n const chunks = this.chunkSequence?.[callIndex];\n if (chunks && chunks.length > 0) {\n // Sanity guard — if a caller passes chunks AND content, they MUST\n // match. Otherwise tests pass while the real prod content silently\n // diverges from what the streaming path observes.\n const joined = chunks.join('');\n if (joined !== response.content) {\n throw new Error(\n `MockProvider.stream: chunks.join('') !== content. ` +\n `chunks=\"${joined}\" content=\"${response.content}\"`,\n );\n }\n for (let i = 0; i < chunks.length; i++) {\n throwIfAborted(options.signal);\n yield { type: 'text_delta', content: chunks[i] };\n if (this.chunkDelayMs > 0 && i < chunks.length - 1) {\n await abortableDelay(this.chunkDelayMs, options.signal);\n }\n }\n } else if (response.content) {\n throwIfAborted(options.signal);\n yield { type: 'text_delta', content: response.content };\n }\n if (response.tool_calls) {\n for (const tc of response.tool_calls) {\n throwIfAborted(options.signal);\n yield {\n type: 'tool_call_delta',\n id: tc.id,\n name: tc.function.name,\n arguments: tc.function.arguments,\n };\n }\n }\n throwIfAborted(options.signal);\n yield {\n type: 'done',\n usage: response.usage,\n // Forwarded like `chat()` does. Without it a streamed mock ask reported no\n // cost at all, so `ctx.budget` and every cost assertion silently saw zero\n // on the streaming path while the same fixture priced correctly on the\n // non-streaming one — a mock that disagrees with itself between paths.\n cost: response.cost,\n providerMetadata: response.providerMetadata,\n // Conditional spread, not `timing: response.timing` — a mock response with\n // no timing must produce a `done` chunk with NO `timing` key at all, so a\n // consumer's `'timing' in chunk` check reads the same as a real adapter's.\n ...(response.timing ? { timing: response.timing } : {}),\n };\n }\n\n static sequence(\n responses: Array<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Deterministic provider-call timing, surfaced on `ProviderResponse.timing`\n * and on the streamed terminal `done` chunk. Lets a test drive\n * `agent_call_end.timing`, the `TimeoutError` breakdown, and the eval\n * per-model rollup without real clocks or a real transport. Omit it and\n * the mock behaves exactly like an uninstrumented custom provider. */\n timing?: CallTiming;\n /** When set, `stream()` yields one `text_delta` per entry instead\n * of one big delta with the full content. Use to exercise\n * partial-JSON parsing, structural-boundary throttling, and\n * cross-attempt token retention in tests. Must satisfy\n * `chunks.join('') === content`. */\n chunks?: string[];\n }>,\n ): MockProvider {\n const provider = new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n const resp = responses[callIndex];\n return {\n content: resp.content,\n tool_calls: resp.tool_calls,\n providerMetadata: resp.providerMetadata,\n usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: resp.cost ?? 0,\n // Copied, not shared: the caller keeps ownership of the fixture, so a\n // consumer that mutates the returned block cannot corrupt a later call's\n // expectation (same rule the message/options clones follow).\n ...(resp.timing ? { timing: { ...resp.timing } } : {}),\n };\n });\n provider.chunkSequence = responses.map((r) => r.chunks);\n return provider;\n }\n\n /**\n * Convenience: build a `sequence()` from plain content strings, splitting\n * each one into fixed-size chunks for the streaming path. Default\n * `chunkSize` is 4 chars (≈1 token).\n */\n static chunked(contents: string[], chunkSize = 4): MockProvider {\n const responses = contents.map((content) => {\n const chunks: string[] = [];\n for (let i = 0; i < content.length; i += chunkSize) {\n chunks.push(content.slice(i, i + chunkSize));\n }\n return { content, chunks };\n });\n return MockProvider.sequence(responses);\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser ? inputText(lastUser.content) : '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: unknown): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) =>\n | {\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */\n timing?: CallTiming;\n }\n | Promise<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */\n timing?: CallTiming;\n }>,\n ): MockProvider {\n return new MockProvider(async (messages, callIndex) => {\n const result = await handler(messages, callIndex);\n return {\n ...result,\n usage: result.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: result.cost ?? 0,\n };\n });\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: unknown) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: unknown) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import type {\n TranscriptionCapabilities,\n TranscriptionProvider,\n TranscriptionProviderRequest,\n TranscriptionProviderResult,\n} from '@axlsdk/axl';\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction cloneRequest(\n request: TranscriptionProviderRequest,\n includeSignal: boolean,\n): TranscriptionProviderRequest {\n const audio =\n request.audio.type === 'bytes'\n ? { ...request.audio, data: request.audio.data.slice() }\n : { ...request.audio };\n return {\n model: request.model,\n audio,\n ...(request.language !== undefined ? { language: request.language } : {}),\n ...(request.timestamps !== undefined ? { timestamps: request.timestamps } : {}),\n ...(request.diarization !== undefined ? { diarization: request.diarization } : {}),\n ...(request.providerOptions ? { providerOptions: clone(request.providerOptions) } : {}),\n ...(includeSignal && request.signal ? { signal: request.signal } : {}),\n };\n}\n\n/** Dedicated transcription mock. It intentionally does not extend MockProvider,\n * so chat fixtures cannot accidentally become transcription adapters. */\nexport class MockTranscriptionProvider implements TranscriptionProvider {\n private _calls: TranscriptionProviderRequest[] = [];\n\n constructor(\n private readonly response: (\n request: TranscriptionProviderRequest,\n callIndex: number,\n ) => TranscriptionProviderResult | Promise<TranscriptionProviderResult>,\n private readonly capabilitiesFor: (\n model: string,\n ) => TranscriptionCapabilities | undefined = () => ({\n sources: ['bytes', 'base64', 'provider-file'],\n timestamps: ['segment', 'word'],\n diarization: true,\n }),\n ) {}\n\n get calls(): readonly TranscriptionProviderRequest[] {\n return this._calls.map(clone);\n }\n\n capabilities(model: string): TranscriptionCapabilities | undefined {\n return this.capabilitiesFor(model);\n }\n\n async transcribe(request: TranscriptionProviderRequest): Promise<TranscriptionProviderResult> {\n // AbortSignal cannot be structured-cloned. Keep it only on the callback\n // request so fixtures can observe cancellation; recorded calls omit it.\n const snapshot = cloneRequest(request, false);\n this._calls.push(snapshot);\n return clone(await this.response(cloneRequest(request, true), this._calls.length - 1));\n }\n\n static text(text: string): MockTranscriptionProvider {\n return new MockTranscriptionProvider(() => ({ transcript: { text } }));\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nimport type {\n AxlEvent,\n AwaitHumanOptions,\n HumanDecision,\n ProviderResponse,\n AgentCallInfo,\n AxlConfig,\n AnyWorkflow,\n ToolCallOutcome,\n ObservationStatus,\n} from '@axlsdk/axl';\nimport {\n WorkflowContext,\n MemoryStore,\n ProviderRegistry,\n TranscriptionProviderRegistry,\n eventCostContribution,\n isUnpricedLeaf,\n} from '@axlsdk/axl';\nimport type { EventStreamOptions, WorkflowContextInit } from '@axlsdk/axl';\n\n// `AnyWorkflow` is a re-exported alias for `Workflow<any, any>` — see\n// the JSDoc on `AnyWorkflow` in `@axlsdk/axl/workflow.ts`. The bivariant\n// `any` is load-bearing for satisfying narrowly-typed handlers (e.g., a\n// `Workflow<TInput=MsgType>` whose handler takes `WorkflowContext<MsgType>`).\n\n/** One accepted provider-issued tool invocation, recorded from its terminal v2 event.\n *\n * Narrow `outcome.status` before reading status-specific fields. Correlation uses\n * `(executionId, askId, callId)`; `name` is the canonical tool name.\n */\nexport type RecordedToolCall = {\n name: string;\n executionId: string;\n askId: string;\n callId: string;\n args: unknown;\n requestedTool?: string;\n outcome: ToolCallOutcome;\n};\nexport type RecordedAgentCall = Partial<AgentCallInfo> &\n Pick<AgentCallInfo, 'agent' | 'prompt' | 'response'>;\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction preserveDrainCause(error: Error, cause: unknown): Error {\n if (cause === undefined || cause === error || 'cause' in error) return error;\n Object.defineProperty(error, 'cause', {\n configurable: true,\n enumerable: false,\n value: cause,\n });\n return error;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n /** AxlConfig for the test runtime. Controls trace level/redact, context management, etc.\n * Defaults to `{}`. Set `{ trace: { level: 'full' } }` to verify verbose-mode snapshots\n * in tests, or `{ trace: { redact: true } }` to verify redaction policy in tests. */\n config?: AxlConfig;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, AnyWorkflow>();\n private mockProviders = new Map<string, MockProvider>();\n private mockTranscriptionProviders = new Map<string, MockTranscriptionProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: AxlEvent[] = [];\n private _totalCost = 0;\n private _unpriced = false;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n private _config: AxlConfig;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n this._config = options?.config ?? {};\n }\n\n register(workflow: AnyWorkflow): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTranscriptionProvider(name: string, provider: MockTranscriptionProvider): void {\n this.mockTranscriptionProviders.set(name, provider);\n }\n\n mockTool<TInput = unknown>(\n name: string,\n handler: (input: TInput) => unknown | Promise<unknown>,\n ): void {\n // The MockTool registry is type-erased (`(input: unknown) => unknown`)\n // because the runtime can't statically know each tool's input shape; tests\n // opt in to a typed input via the generic, and we cast on storage.\n this.mockToolMap.set(name, MockTool.create(name, handler as (input: unknown) => unknown));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: {\n metadata?: Record<string, unknown>;\n events?: EventStreamOptions;\n branchDrainTimeoutMs?: number;\n },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._unpriced = false;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider);\n }\n const transcriptionRegistry = new TranscriptionProviderRegistry();\n for (const [name, provider] of this.mockTranscriptionProviders) {\n transcriptionRegistry.registerInstance(name, provider);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Previously `_recordStep('workflow_start', ...)` was called here with\n // raw `{ workflow, input }` data, AND `ctx._emitWorkflowStart` below\n // pushed through the onTrace handler (which also bumps `_stepCounter`\n // and appends to `_steps`). Result: two entries in `steps()` per\n // workflow_start, with the first bypassing `config.trace.redact`.\n // The `_recordStep` call is removed — onTrace is the single source\n // of truth for `_steps`, matching the behavior of every other event\n // type in the test runtime. Reviewer architecture §5.\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n // Thread the constructor-provided config so tests can exercise\n // trace.level === 'full' and trace.redact behavior end-to-end.\n config: this._config,\n // Thread `events` config so tests can exercise the queue cap and\n // overflow policy on `ctx.events`. Mirrors the production runtime.\n eventStreamOptions: options?.events,\n branchDrainTimeoutMs: options?.branchDrainTimeoutMs,\n // Production runtime threads this (see `runtime.ts:594` in\n // `execute()` and `:765` in `stream()`). `emitEvent` auto-stamps\n // `event.workflow` from it, so consumers grouping by workflow\n // (Cost Dashboard `byWorkflow`, `trackExecution.metadata.workflows`,\n // any eval runner) see the same attribution in tests and prod.\n workflowName,\n providerRegistry: registry,\n transcriptionProviderRegistry: transcriptionRegistry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: AxlEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({\n step: this._stepCounter,\n type: event.type,\n data: (event as { data?: unknown }).data,\n });\n\n if (event.type === 'tool_call_end') {\n const { args, requestedTool, outcome } = event.data;\n this._toolCalls.push({\n name: event.tool,\n executionId: event.executionId,\n askId: event.askId,\n callId: event.callId,\n args,\n ...(requestedTool !== undefined ? { requestedTool } : {}),\n outcome,\n });\n }\n\n // Single-source-of-truth cost accumulator. Skips ask_end\n // rollups (decision 10) and guards NaN/Infinity.\n this._totalCost += eventCostContribution(event);\n // Honest aggregate: one unpriced leaf makes `totalCost()` a lower bound.\n if (isUnpricedLeaf(event)) this._unpriced = true;\n\n // Track provider responses for recording\n if (event.type === 'agent_call_end' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: event.tokens\n ? {\n prompt_tokens: event.tokens.input ?? 0,\n completion_tokens: event.tokens.output ?? 0,\n total_tokens:\n (event.tokens.input ?? 0) +\n (event.tokens.output ?? 0) +\n (event.tokens.reasoning ?? 0),\n ...(event.tokens.reasoning !== undefined\n ? { reasoning_tokens: event.tokens.reasoning }\n : {}),\n }\n : { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n ...(event.cost !== undefined ? { cost: event.cost } : {}),\n });\n }\n }\n },\n onAgentCallComplete: (call: AgentCallInfo) => {\n this._agentCalls.push(call);\n },\n };\n\n const ctx = new WorkflowContext(init);\n\n // Emit workflow_start through the same pipeline production uses,\n // AFTER ctx exists so `emitEvent`'s redact/step/timestamp handling\n // applies. Previously emitted via `_pushTrace` which bypassed\n // redaction — `config: { trace: { redact: true } }` on the test\n // runtime would scrub intermediate events but leak `input`/`result`\n // through the synthesized workflow_start/end.\n const startedAt = Date.now();\n ctx._emitWorkflowStart(validated);\n try {\n const result = await workflow.handler(ctx);\n const observation = await ctx._drainBranchWork();\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n ctx._emitWorkflowEnd({\n status: 'completed',\n duration: Date.now() - startedAt,\n result,\n observation,\n });\n // `_steps` is populated by onTrace for every event (including\n // workflow_end); no separate `_recordStep` call needed — that\n // would double-count.\n\n if (this.recordPath) await this._writeRecording();\n return result;\n } catch (err) {\n let workflowError = err;\n let observation: ObservationStatus = { complete: true };\n try {\n observation = await ctx._drainBranchWork();\n } catch (drainError) {\n workflowError =\n drainError instanceof Error ? preserveDrainCause(drainError, workflowError) : drainError;\n }\n // Parity with production: failed workflows get a terminal\n // `workflow_end(status: 'failed')` event so consumers counting\n // workflow_start ↔ workflow_end pairs never see an unclosed one.\n // `aborted` flag mirrors the runtime.ts AbortError detection.\n const aborted =\n typeof workflowError === 'object' &&\n workflowError !== null &&\n (workflowError as { name?: unknown }).name === 'AbortError';\n ctx._emitWorkflowEnd({\n status: 'failed',\n duration: Date.now() - startedAt,\n error: workflowError instanceof Error ? workflowError.message : String(workflowError),\n ...(aborted ? { aborted: true } : {}),\n observation,\n });\n // `_steps` populated by onTrace (see comment on the success path).\n throw workflowError;\n }\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n /** True when any cost-bearing call was unpriced — `totalCost()` is then a\n * LOWER BOUND. Mirrors `ExecutionInfo.unpriced` / `BudgetResult.unpriced`. */\n unpriced(): boolean {\n return this._unpriced;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): AxlEvent[] {\n return this._traceLog;\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":["import_axl"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Provider, ChatOptions, EffortResolution, ChatMessage, ProviderResponse, InputMediaSource, ProviderInputValidationRequest, ProviderInputValidationResult, StreamChunk, ToolCallMessage, TranscriptionProvider, TranscriptionProviderRequest, TranscriptionProviderResult, TranscriptionCapabilities, AwaitHumanOptions, HumanDecision, AxlConfig, AnyWorkflow, EventStreamOptions, ToolCallOutcome, AgentCallInfo, AxlEvent } from '@axlsdk/axl';
1
+ import { Provider, ChatOptions, EffortResolution, ChatMessage, ProviderResponse, InputMediaSource, ProviderInputValidationRequest, ProviderInputValidationResult, StreamChunk, ToolCallMessage, CallTiming, TranscriptionProvider, TranscriptionProviderRequest, TranscriptionProviderResult, TranscriptionCapabilities, AwaitHumanOptions, HumanDecision, AxlConfig, AnyWorkflow, EventStreamOptions, ToolCallOutcome, AgentCallInfo, AxlEvent } from '@axlsdk/axl';
2
2
 
3
3
  declare class MockProvider implements Provider {
4
4
  private responseFn;
@@ -60,6 +60,12 @@ declare class MockProvider implements Provider {
60
60
  total_tokens: number;
61
61
  };
62
62
  cost?: number;
63
+ /** Deterministic provider-call timing, surfaced on `ProviderResponse.timing`
64
+ * and on the streamed terminal `done` chunk. Lets a test drive
65
+ * `agent_call_end.timing`, the `TimeoutError` breakdown, and the eval
66
+ * per-model rollup without real clocks or a real transport. Omit it and
67
+ * the mock behaves exactly like an uninstrumented custom provider. */
68
+ timing?: CallTiming;
63
69
  /** When set, `stream()` yields one `text_delta` per entry instead
64
70
  * of one big delta with the full content. Use to exercise
65
71
  * partial-JSON parsing, structural-boundary throttling, and
@@ -86,6 +92,8 @@ declare class MockProvider implements Provider {
86
92
  total_tokens: number;
87
93
  };
88
94
  cost?: number;
95
+ /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */
96
+ timing?: CallTiming;
89
97
  } | Promise<{
90
98
  content: string;
91
99
  tool_calls?: ToolCallMessage[];
@@ -96,6 +104,8 @@ declare class MockProvider implements Provider {
96
104
  total_tokens: number;
97
105
  };
98
106
  cost?: number;
107
+ /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */
108
+ timing?: CallTiming;
99
109
  }>): MockProvider;
100
110
  }
101
111
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Provider, ChatOptions, EffortResolution, ChatMessage, ProviderResponse, InputMediaSource, ProviderInputValidationRequest, ProviderInputValidationResult, StreamChunk, ToolCallMessage, TranscriptionProvider, TranscriptionProviderRequest, TranscriptionProviderResult, TranscriptionCapabilities, AwaitHumanOptions, HumanDecision, AxlConfig, AnyWorkflow, EventStreamOptions, ToolCallOutcome, AgentCallInfo, AxlEvent } from '@axlsdk/axl';
1
+ import { Provider, ChatOptions, EffortResolution, ChatMessage, ProviderResponse, InputMediaSource, ProviderInputValidationRequest, ProviderInputValidationResult, StreamChunk, ToolCallMessage, CallTiming, TranscriptionProvider, TranscriptionProviderRequest, TranscriptionProviderResult, TranscriptionCapabilities, AwaitHumanOptions, HumanDecision, AxlConfig, AnyWorkflow, EventStreamOptions, ToolCallOutcome, AgentCallInfo, AxlEvent } from '@axlsdk/axl';
2
2
 
3
3
  declare class MockProvider implements Provider {
4
4
  private responseFn;
@@ -60,6 +60,12 @@ declare class MockProvider implements Provider {
60
60
  total_tokens: number;
61
61
  };
62
62
  cost?: number;
63
+ /** Deterministic provider-call timing, surfaced on `ProviderResponse.timing`
64
+ * and on the streamed terminal `done` chunk. Lets a test drive
65
+ * `agent_call_end.timing`, the `TimeoutError` breakdown, and the eval
66
+ * per-model rollup without real clocks or a real transport. Omit it and
67
+ * the mock behaves exactly like an uninstrumented custom provider. */
68
+ timing?: CallTiming;
63
69
  /** When set, `stream()` yields one `text_delta` per entry instead
64
70
  * of one big delta with the full content. Use to exercise
65
71
  * partial-JSON parsing, structural-boundary throttling, and
@@ -86,6 +92,8 @@ declare class MockProvider implements Provider {
86
92
  total_tokens: number;
87
93
  };
88
94
  cost?: number;
95
+ /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */
96
+ timing?: CallTiming;
89
97
  } | Promise<{
90
98
  content: string;
91
99
  tool_calls?: ToolCallMessage[];
@@ -96,6 +104,8 @@ declare class MockProvider implements Provider {
96
104
  total_tokens: number;
97
105
  };
98
106
  cost?: number;
107
+ /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */
108
+ timing?: CallTiming;
99
109
  }>): MockProvider;
100
110
  }
101
111
 
package/dist/index.js CHANGED
@@ -77,6 +77,52 @@ function cloneOptions(options) {
77
77
  ...options.providerOptions ? { providerOptions: cloneValue(options.providerOptions) } : {}
78
78
  };
79
79
  }
80
+ function throwIfAborted(signal) {
81
+ if (signal?.aborted) throw signal.reason;
82
+ }
83
+ async function awaitWithSignal(value, signal) {
84
+ if (!signal) return await value;
85
+ throwIfAborted(signal);
86
+ return await new Promise((resolve, reject) => {
87
+ const onAbort = () => reject(signal.reason);
88
+ signal.addEventListener("abort", onAbort, { once: true });
89
+ Promise.resolve(value).then(
90
+ (result) => {
91
+ signal.removeEventListener("abort", onAbort);
92
+ resolve(result);
93
+ },
94
+ (error) => {
95
+ signal.removeEventListener("abort", onAbort);
96
+ reject(error);
97
+ }
98
+ );
99
+ });
100
+ }
101
+ async function abortableDelay(ms, signal) {
102
+ if (ms <= 0) {
103
+ throwIfAborted(signal);
104
+ return;
105
+ }
106
+ if (!signal) {
107
+ await new Promise((resolve) => setTimeout(resolve, ms));
108
+ return;
109
+ }
110
+ throwIfAborted(signal);
111
+ await new Promise((resolve, reject) => {
112
+ let timer = setTimeout(() => {
113
+ timer = void 0;
114
+ signal.removeEventListener("abort", onAbort);
115
+ resolve();
116
+ }, ms);
117
+ const onAbort = () => {
118
+ if (timer !== void 0) clearTimeout(timer);
119
+ timer = void 0;
120
+ signal.removeEventListener("abort", onAbort);
121
+ reject(signal.reason);
122
+ };
123
+ signal.addEventListener("abort", onAbort, { once: true });
124
+ });
125
+ }
80
126
  function richParts(input) {
81
127
  return typeof input === "string" ? [] : input;
82
128
  }
@@ -169,8 +215,13 @@ var MockProvider = class _MockProvider {
169
215
  }
170
216
  async chat(messages, options) {
171
217
  const recordedMessages = cloneMessages(messages);
218
+ const callIndex = this._calls.length;
172
219
  this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });
173
- return await this.responseFn(cloneMessages(messages), this._calls.length - 1);
220
+ throwIfAborted(options.signal);
221
+ const response = Promise.resolve().then(
222
+ () => this.responseFn(cloneMessages(messages), callIndex)
223
+ );
224
+ return await awaitWithSignal(response, options.signal);
174
225
  }
175
226
  inputCapabilities(_model) {
176
227
  return { image: { sources: ["url", "bytes", "base64", "provider-file"] } };
@@ -208,6 +259,7 @@ var MockProvider = class _MockProvider {
208
259
  async *stream(messages, options) {
209
260
  const callIndex = this._calls.length;
210
261
  const response = await this.chat(messages, options);
262
+ throwIfAborted(options.signal);
211
263
  const chunks = this.chunkSequence?.[callIndex];
212
264
  if (chunks && chunks.length > 0) {
213
265
  const joined = chunks.join("");
@@ -217,16 +269,19 @@ var MockProvider = class _MockProvider {
217
269
  );
218
270
  }
219
271
  for (let i = 0; i < chunks.length; i++) {
272
+ throwIfAborted(options.signal);
220
273
  yield { type: "text_delta", content: chunks[i] };
221
274
  if (this.chunkDelayMs > 0 && i < chunks.length - 1) {
222
- await new Promise((resolve) => setTimeout(resolve, this.chunkDelayMs));
275
+ await abortableDelay(this.chunkDelayMs, options.signal);
223
276
  }
224
277
  }
225
278
  } else if (response.content) {
279
+ throwIfAborted(options.signal);
226
280
  yield { type: "text_delta", content: response.content };
227
281
  }
228
282
  if (response.tool_calls) {
229
283
  for (const tc of response.tool_calls) {
284
+ throwIfAborted(options.signal);
230
285
  yield {
231
286
  type: "tool_call_delta",
232
287
  id: tc.id,
@@ -235,7 +290,21 @@ var MockProvider = class _MockProvider {
235
290
  };
236
291
  }
237
292
  }
238
- yield { type: "done", usage: response.usage, providerMetadata: response.providerMetadata };
293
+ throwIfAborted(options.signal);
294
+ yield {
295
+ type: "done",
296
+ usage: response.usage,
297
+ // Forwarded like `chat()` does. Without it a streamed mock ask reported no
298
+ // cost at all, so `ctx.budget` and every cost assertion silently saw zero
299
+ // on the streaming path while the same fixture priced correctly on the
300
+ // non-streaming one — a mock that disagrees with itself between paths.
301
+ cost: response.cost,
302
+ providerMetadata: response.providerMetadata,
303
+ // Conditional spread, not `timing: response.timing` — a mock response with
304
+ // no timing must produce a `done` chunk with NO `timing` key at all, so a
305
+ // consumer's `'timing' in chunk` check reads the same as a real adapter's.
306
+ ...response.timing ? { timing: response.timing } : {}
307
+ };
239
308
  }
240
309
  static sequence(responses) {
241
310
  const provider = new _MockProvider((_messages, callIndex) => {
@@ -250,7 +319,11 @@ var MockProvider = class _MockProvider {
250
319
  tool_calls: resp.tool_calls,
251
320
  providerMetadata: resp.providerMetadata,
252
321
  usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },
253
- cost: resp.cost ?? 0
322
+ cost: resp.cost ?? 0,
323
+ // Copied, not shared: the caller keeps ownership of the fixture, so a
324
+ // consumer that mutates the returned block cannot corrupt a later call's
325
+ // expectation (same rule the message/options clones follow).
326
+ ...resp.timing ? { timing: { ...resp.timing } } : {}
254
327
  };
255
328
  });
256
329
  provider.chunkSequence = responses.map((r) => r.chunks);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mock-provider.ts","../src/mock-tool.ts","../src/mock-transcription-provider.ts","../src/test-runtime.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport type {\n ChatMessage,\n ChatOptions,\n InputContentPart,\n InputMediaSource,\n ModelInput,\n ToolCallMessage,\n ProviderResponse,\n StreamChunk,\n Provider,\n ProviderInputValidationRequest,\n ProviderInputValidationResult,\n EffortResolution,\n} from '@axlsdk/axl';\nimport { inputText, UnsupportedModelInputError } from '@axlsdk/axl';\n\nfunction cloneValue(value: unknown): unknown {\n if (value instanceof Uint8Array) return value.slice();\n if (Array.isArray(value)) return value.map(cloneValue);\n if (value && typeof value === 'object') {\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneValue(item)]));\n }\n return value;\n}\n\nfunction cloneModelInput(input: ModelInput): ModelInput {\n if (typeof input === 'string') return input;\n return input.map((part): InputContentPart => {\n if (part.type === 'text') return { type: 'text', text: part.text };\n const { source } = part;\n switch (source.type) {\n case 'bytes':\n return {\n type: 'image',\n source: { type: 'bytes', data: source.data.slice(), mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'url':\n return {\n type: 'image',\n source: {\n type: 'url',\n url: source.url,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'base64':\n return {\n type: 'image',\n source: { type: 'base64', data: source.data, mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'provider-file':\n return {\n type: 'image',\n source: {\n type: 'provider-file',\n provider: source.provider,\n reference: source.reference,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n }\n });\n}\n\nfunction cloneMessages(messages: readonly ChatMessage[]): ChatMessage[] {\n return messages.map((message) => ({\n ...message,\n content: cloneModelInput(message.content),\n ...(message.tool_calls\n ? {\n tool_calls: message.tool_calls.map((toolCall) => ({\n ...toolCall,\n function: { ...toolCall.function },\n })),\n }\n : {}),\n ...(message.providerMetadata\n ? { providerMetadata: cloneValue(message.providerMetadata) as Record<string, unknown> }\n : {}),\n }));\n}\n\nfunction cloneOptions(options: ChatOptions): ChatOptions {\n return {\n ...options,\n ...(options.tools\n ? {\n tools: options.tools.map((tool) => ({\n ...tool,\n function: { ...tool.function, parameters: cloneValue(tool.function.parameters) },\n })),\n }\n : {}),\n ...(options.providerOptions\n ? { providerOptions: cloneValue(options.providerOptions) as Record<string, unknown> }\n : {}),\n };\n}\n\nfunction richParts(input: ModelInput): readonly InputContentPart[] {\n return typeof input === 'string' ? [] : input;\n}\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: unknown): unknown {\n if (schema instanceof z.ZodString) return randomAlphanumeric(8 + Math.floor(Math.random() * 13));\n if (schema instanceof z.ZodNumber) {\n const min = Number.isFinite(schema.minValue) ? schema.minValue! : 0;\n const max = Number.isFinite(schema.maxValue) ? schema.maxValue! : 100;\n return min + Math.random() * (max - min);\n }\n if (schema instanceof z.ZodBoolean) return Math.random() < 0.5;\n if (schema instanceof z.ZodArray) {\n const count = 1 + Math.floor(Math.random() * 3);\n return Array.from({ length: count }, () => generateFromSchema(schema.element));\n }\n if (schema instanceof z.ZodObject) {\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema.shape)) obj[key] = generateFromSchema(value);\n return obj;\n }\n if (schema instanceof z.ZodOptional) {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(schema.unwrap());\n }\n if (schema instanceof z.ZodDefault) return schema.parse(undefined);\n if (schema instanceof z.ZodEnum) {\n const values = schema.options;\n return values.length > 0 ? values[Math.floor(Math.random() * values.length)] : '';\n }\n if (schema instanceof z.ZodLiteral) return [...schema.values][0];\n if (schema instanceof z.ZodNullable) return null;\n if (schema instanceof z.ZodUnion) {\n const options = schema.options;\n return generateFromSchema(options[Math.floor(Math.random() * options.length)]);\n }\n return {};\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: ChatOptions }[] = [];\n /** Per-call optional chunk arrays, set by `sequence()` / `chunked()` so\n * `stream()` can yield one `text_delta` per chunk. Per-call indexed\n * alongside the response sequence. */\n private chunkSequence?: Array<string[] | undefined>;\n /** Optional ms delay between successive chunks during `stream()`. Useful\n * for dev fixtures that need to demonstrate streaming UX visually\n * (without a delay, mock chunks fire synchronously and complete in\n * microseconds — invisible to a human watching). Default 0 keeps\n * every existing test fast.\n *\n * Set via `provider.chunkDelayMs = 50` after construction. Not part of\n * any factory's surface to keep the test API minimal. */\n chunkDelayMs = 0;\n\n /** How this mock reports the unified `effort` it was asked for. Undefined (the\n * default) means the mock implements no `effortResolution` at all, so the\n * runtime emits no `provider_diagnostic`. Set it with\n * {@link MockProvider.withEffortResolution} to drive the clamp-reporting path. */\n effortResolution?: (\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined;\n\n /**\n * Make this mock report an effort clamp, so a test can exercise the runtime's\n * `provider_diagnostic` path without a real adapter. Pass a fixed\n * {@link EffortResolution} or a function of the request knobs.\n *\n * ```ts\n * const provider = MockProvider.echo().withEffortResolution({\n * requested: 'none',\n * effective: 'minimal',\n * clamped: true,\n * cause: 'this model cannot disable thinking',\n * });\n * ```\n */\n withEffortResolution(\n resolution:\n | EffortResolution\n | undefined\n | ((\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined),\n ): this {\n this.effortResolution = typeof resolution === 'function' ? resolution : () => resolution;\n return this;\n }\n\n private constructor(\n private responseFn: (\n messages: ChatMessage[],\n callIndex: number,\n ) => ProviderResponse | Promise<ProviderResponse>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse> {\n // Each consumer gets independent ownership: a handler can inspect or mutate\n // its copy without changing the durable assertion record (including bytes).\n const recordedMessages = cloneMessages(messages);\n this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });\n return await this.responseFn(cloneMessages(messages), this._calls.length - 1);\n }\n\n inputCapabilities(_model: string): { image: { sources: readonly InputMediaSource['type'][] } } {\n return { image: { sources: ['url', 'bytes', 'base64', 'provider-file'] } };\n }\n\n validateInput(request: ProviderInputValidationRequest): ProviderInputValidationResult {\n const effectiveModel =\n typeof request.providerOptions?.model === 'string'\n ? request.providerOptions.model\n : request.model;\n const fail = (source?: string, feature?: string): never => {\n throw new UnsupportedModelInputError({\n provider: this.name,\n model: effectiveModel || request.model,\n modality: 'image',\n ...(source ? { source } : {}),\n ...(feature ? { feature } : {}),\n });\n };\n\n if (request.providerOptions && 'input' in request.providerOptions) {\n fail(undefined, 'raw input-container providerOptions');\n }\n for (const message of request.history) {\n if (typeof message.content === 'string') continue;\n if (message.role !== 'user') fail(undefined, 'rich non-user history');\n for (const part of richParts(message.content)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n }\n for (const part of richParts(request.input)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n return { effectiveModel };\n }\n\n async *stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk> {\n const callIndex = this._calls.length;\n const response = await this.chat(messages, options);\n const chunks = this.chunkSequence?.[callIndex];\n if (chunks && chunks.length > 0) {\n // Sanity guard — if a caller passes chunks AND content, they MUST\n // match. Otherwise tests pass while the real prod content silently\n // diverges from what the streaming path observes.\n const joined = chunks.join('');\n if (joined !== response.content) {\n throw new Error(\n `MockProvider.stream: chunks.join('') !== content. ` +\n `chunks=\"${joined}\" content=\"${response.content}\"`,\n );\n }\n for (let i = 0; i < chunks.length; i++) {\n yield { type: 'text_delta', content: chunks[i] };\n if (this.chunkDelayMs > 0 && i < chunks.length - 1) {\n await new Promise((resolve) => setTimeout(resolve, this.chunkDelayMs));\n }\n }\n } else if (response.content) {\n yield { type: 'text_delta', content: response.content };\n }\n if (response.tool_calls) {\n for (const tc of response.tool_calls) {\n yield {\n type: 'tool_call_delta',\n id: tc.id,\n name: tc.function.name,\n arguments: tc.function.arguments,\n };\n }\n }\n yield { type: 'done', usage: response.usage, providerMetadata: response.providerMetadata };\n }\n\n static sequence(\n responses: Array<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** When set, `stream()` yields one `text_delta` per entry instead\n * of one big delta with the full content. Use to exercise\n * partial-JSON parsing, structural-boundary throttling, and\n * cross-attempt token retention in tests. Must satisfy\n * `chunks.join('') === content`. */\n chunks?: string[];\n }>,\n ): MockProvider {\n const provider = new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n const resp = responses[callIndex];\n return {\n content: resp.content,\n tool_calls: resp.tool_calls,\n providerMetadata: resp.providerMetadata,\n usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: resp.cost ?? 0,\n };\n });\n provider.chunkSequence = responses.map((r) => r.chunks);\n return provider;\n }\n\n /**\n * Convenience: build a `sequence()` from plain content strings, splitting\n * each one into fixed-size chunks for the streaming path. Default\n * `chunkSize` is 4 chars (≈1 token).\n */\n static chunked(contents: string[], chunkSize = 4): MockProvider {\n const responses = contents.map((content) => {\n const chunks: string[] = [];\n for (let i = 0; i < content.length; i += chunkSize) {\n chunks.push(content.slice(i, i + chunkSize));\n }\n return { content, chunks };\n });\n return MockProvider.sequence(responses);\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser ? inputText(lastUser.content) : '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: unknown): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) =>\n | {\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n }\n | Promise<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n }>,\n ): MockProvider {\n return new MockProvider(async (messages, callIndex) => {\n const result = await handler(messages, callIndex);\n return {\n ...result,\n usage: result.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: result.cost ?? 0,\n };\n });\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: unknown) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: unknown) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import type {\n TranscriptionCapabilities,\n TranscriptionProvider,\n TranscriptionProviderRequest,\n TranscriptionProviderResult,\n} from '@axlsdk/axl';\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction cloneRequest(\n request: TranscriptionProviderRequest,\n includeSignal: boolean,\n): TranscriptionProviderRequest {\n const audio =\n request.audio.type === 'bytes'\n ? { ...request.audio, data: request.audio.data.slice() }\n : { ...request.audio };\n return {\n model: request.model,\n audio,\n ...(request.language !== undefined ? { language: request.language } : {}),\n ...(request.timestamps !== undefined ? { timestamps: request.timestamps } : {}),\n ...(request.diarization !== undefined ? { diarization: request.diarization } : {}),\n ...(request.providerOptions ? { providerOptions: clone(request.providerOptions) } : {}),\n ...(includeSignal && request.signal ? { signal: request.signal } : {}),\n };\n}\n\n/** Dedicated transcription mock. It intentionally does not extend MockProvider,\n * so chat fixtures cannot accidentally become transcription adapters. */\nexport class MockTranscriptionProvider implements TranscriptionProvider {\n private _calls: TranscriptionProviderRequest[] = [];\n\n constructor(\n private readonly response: (\n request: TranscriptionProviderRequest,\n callIndex: number,\n ) => TranscriptionProviderResult | Promise<TranscriptionProviderResult>,\n private readonly capabilitiesFor: (\n model: string,\n ) => TranscriptionCapabilities | undefined = () => ({\n sources: ['bytes', 'base64', 'provider-file'],\n timestamps: ['segment', 'word'],\n diarization: true,\n }),\n ) {}\n\n get calls(): readonly TranscriptionProviderRequest[] {\n return this._calls.map(clone);\n }\n\n capabilities(model: string): TranscriptionCapabilities | undefined {\n return this.capabilitiesFor(model);\n }\n\n async transcribe(request: TranscriptionProviderRequest): Promise<TranscriptionProviderResult> {\n // AbortSignal cannot be structured-cloned. Keep it only on the callback\n // request so fixtures can observe cancellation; recorded calls omit it.\n const snapshot = cloneRequest(request, false);\n this._calls.push(snapshot);\n return clone(await this.response(cloneRequest(request, true), this._calls.length - 1));\n }\n\n static text(text: string): MockTranscriptionProvider {\n return new MockTranscriptionProvider(() => ({ transcript: { text } }));\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nimport type {\n AxlEvent,\n AwaitHumanOptions,\n HumanDecision,\n ProviderResponse,\n AgentCallInfo,\n AxlConfig,\n AnyWorkflow,\n ToolCallOutcome,\n ObservationStatus,\n} from '@axlsdk/axl';\nimport {\n WorkflowContext,\n MemoryStore,\n ProviderRegistry,\n TranscriptionProviderRegistry,\n eventCostContribution,\n isUnpricedLeaf,\n} from '@axlsdk/axl';\nimport type { EventStreamOptions, WorkflowContextInit } from '@axlsdk/axl';\n\n// `AnyWorkflow` is a re-exported alias for `Workflow<any, any>` — see\n// the JSDoc on `AnyWorkflow` in `@axlsdk/axl/workflow.ts`. The bivariant\n// `any` is load-bearing for satisfying narrowly-typed handlers (e.g., a\n// `Workflow<TInput=MsgType>` whose handler takes `WorkflowContext<MsgType>`).\n\n/** One accepted provider-issued tool invocation, recorded from its terminal v2 event.\n *\n * Narrow `outcome.status` before reading status-specific fields. Correlation uses\n * `(executionId, askId, callId)`; `name` is the canonical tool name.\n */\nexport type RecordedToolCall = {\n name: string;\n executionId: string;\n askId: string;\n callId: string;\n args: unknown;\n requestedTool?: string;\n outcome: ToolCallOutcome;\n};\nexport type RecordedAgentCall = Partial<AgentCallInfo> &\n Pick<AgentCallInfo, 'agent' | 'prompt' | 'response'>;\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction preserveDrainCause(error: Error, cause: unknown): Error {\n if (cause === undefined || cause === error || 'cause' in error) return error;\n Object.defineProperty(error, 'cause', {\n configurable: true,\n enumerable: false,\n value: cause,\n });\n return error;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n /** AxlConfig for the test runtime. Controls trace level/redact, context management, etc.\n * Defaults to `{}`. Set `{ trace: { level: 'full' } }` to verify verbose-mode snapshots\n * in tests, or `{ trace: { redact: true } }` to verify redaction policy in tests. */\n config?: AxlConfig;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, AnyWorkflow>();\n private mockProviders = new Map<string, MockProvider>();\n private mockTranscriptionProviders = new Map<string, MockTranscriptionProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: AxlEvent[] = [];\n private _totalCost = 0;\n private _unpriced = false;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n private _config: AxlConfig;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n this._config = options?.config ?? {};\n }\n\n register(workflow: AnyWorkflow): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTranscriptionProvider(name: string, provider: MockTranscriptionProvider): void {\n this.mockTranscriptionProviders.set(name, provider);\n }\n\n mockTool<TInput = unknown>(\n name: string,\n handler: (input: TInput) => unknown | Promise<unknown>,\n ): void {\n // The MockTool registry is type-erased (`(input: unknown) => unknown`)\n // because the runtime can't statically know each tool's input shape; tests\n // opt in to a typed input via the generic, and we cast on storage.\n this.mockToolMap.set(name, MockTool.create(name, handler as (input: unknown) => unknown));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: {\n metadata?: Record<string, unknown>;\n events?: EventStreamOptions;\n branchDrainTimeoutMs?: number;\n },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._unpriced = false;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider);\n }\n const transcriptionRegistry = new TranscriptionProviderRegistry();\n for (const [name, provider] of this.mockTranscriptionProviders) {\n transcriptionRegistry.registerInstance(name, provider);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Previously `_recordStep('workflow_start', ...)` was called here with\n // raw `{ workflow, input }` data, AND `ctx._emitWorkflowStart` below\n // pushed through the onTrace handler (which also bumps `_stepCounter`\n // and appends to `_steps`). Result: two entries in `steps()` per\n // workflow_start, with the first bypassing `config.trace.redact`.\n // The `_recordStep` call is removed — onTrace is the single source\n // of truth for `_steps`, matching the behavior of every other event\n // type in the test runtime. Reviewer architecture §5.\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n // Thread the constructor-provided config so tests can exercise\n // trace.level === 'full' and trace.redact behavior end-to-end.\n config: this._config,\n // Thread `events` config so tests can exercise the queue cap and\n // overflow policy on `ctx.events`. Mirrors the production runtime.\n eventStreamOptions: options?.events,\n branchDrainTimeoutMs: options?.branchDrainTimeoutMs,\n // Production runtime threads this (see `runtime.ts:594` in\n // `execute()` and `:765` in `stream()`). `emitEvent` auto-stamps\n // `event.workflow` from it, so consumers grouping by workflow\n // (Cost Dashboard `byWorkflow`, `trackExecution.metadata.workflows`,\n // any eval runner) see the same attribution in tests and prod.\n workflowName,\n providerRegistry: registry,\n transcriptionProviderRegistry: transcriptionRegistry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: AxlEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({\n step: this._stepCounter,\n type: event.type,\n data: (event as { data?: unknown }).data,\n });\n\n if (event.type === 'tool_call_end') {\n const { args, requestedTool, outcome } = event.data;\n this._toolCalls.push({\n name: event.tool,\n executionId: event.executionId,\n askId: event.askId,\n callId: event.callId,\n args,\n ...(requestedTool !== undefined ? { requestedTool } : {}),\n outcome,\n });\n }\n\n // Single-source-of-truth cost accumulator. Skips ask_end\n // rollups (decision 10) and guards NaN/Infinity.\n this._totalCost += eventCostContribution(event);\n // Honest aggregate: one unpriced leaf makes `totalCost()` a lower bound.\n if (isUnpricedLeaf(event)) this._unpriced = true;\n\n // Track provider responses for recording\n if (event.type === 'agent_call_end' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: event.tokens\n ? {\n prompt_tokens: event.tokens.input ?? 0,\n completion_tokens: event.tokens.output ?? 0,\n total_tokens:\n (event.tokens.input ?? 0) +\n (event.tokens.output ?? 0) +\n (event.tokens.reasoning ?? 0),\n ...(event.tokens.reasoning !== undefined\n ? { reasoning_tokens: event.tokens.reasoning }\n : {}),\n }\n : { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n ...(event.cost !== undefined ? { cost: event.cost } : {}),\n });\n }\n }\n },\n onAgentCallComplete: (call: AgentCallInfo) => {\n this._agentCalls.push(call);\n },\n };\n\n const ctx = new WorkflowContext(init);\n\n // Emit workflow_start through the same pipeline production uses,\n // AFTER ctx exists so `emitEvent`'s redact/step/timestamp handling\n // applies. Previously emitted via `_pushTrace` which bypassed\n // redaction — `config: { trace: { redact: true } }` on the test\n // runtime would scrub intermediate events but leak `input`/`result`\n // through the synthesized workflow_start/end.\n const startedAt = Date.now();\n ctx._emitWorkflowStart(validated);\n try {\n const result = await workflow.handler(ctx);\n const observation = await ctx._drainBranchWork();\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n ctx._emitWorkflowEnd({\n status: 'completed',\n duration: Date.now() - startedAt,\n result,\n observation,\n });\n // `_steps` is populated by onTrace for every event (including\n // workflow_end); no separate `_recordStep` call needed — that\n // would double-count.\n\n if (this.recordPath) await this._writeRecording();\n return result;\n } catch (err) {\n let workflowError = err;\n let observation: ObservationStatus = { complete: true };\n try {\n observation = await ctx._drainBranchWork();\n } catch (drainError) {\n workflowError =\n drainError instanceof Error ? preserveDrainCause(drainError, workflowError) : drainError;\n }\n // Parity with production: failed workflows get a terminal\n // `workflow_end(status: 'failed')` event so consumers counting\n // workflow_start ↔ workflow_end pairs never see an unclosed one.\n // `aborted` flag mirrors the runtime.ts AbortError detection.\n const aborted =\n typeof workflowError === 'object' &&\n workflowError !== null &&\n (workflowError as { name?: unknown }).name === 'AbortError';\n ctx._emitWorkflowEnd({\n status: 'failed',\n duration: Date.now() - startedAt,\n error: workflowError instanceof Error ? workflowError.message : String(workflowError),\n ...(aborted ? { aborted: true } : {}),\n observation,\n });\n // `_steps` populated by onTrace (see comment on the success path).\n throw workflowError;\n }\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n /** True when any cost-bearing call was unpriced — `totalCost()` is then a\n * LOWER BOUND. Mirrors `ExecutionInfo.unpriced` / `BudgetResult.unpriced`. */\n unpriced(): boolean {\n return this._unpriced;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): AxlEvent[] {\n return this._traceLog;\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mock-provider.ts","../src/mock-tool.ts","../src/mock-transcription-provider.ts","../src/test-runtime.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport type {\n CallTiming,\n ChatMessage,\n ChatOptions,\n InputContentPart,\n InputMediaSource,\n ModelInput,\n ToolCallMessage,\n ProviderResponse,\n StreamChunk,\n Provider,\n ProviderInputValidationRequest,\n ProviderInputValidationResult,\n EffortResolution,\n} from '@axlsdk/axl';\nimport { inputText, UnsupportedModelInputError } from '@axlsdk/axl';\n\nfunction cloneValue(value: unknown): unknown {\n if (value instanceof Uint8Array) return value.slice();\n if (Array.isArray(value)) return value.map(cloneValue);\n if (value && typeof value === 'object') {\n return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneValue(item)]));\n }\n return value;\n}\n\nfunction cloneModelInput(input: ModelInput): ModelInput {\n if (typeof input === 'string') return input;\n return input.map((part): InputContentPart => {\n if (part.type === 'text') return { type: 'text', text: part.text };\n const { source } = part;\n switch (source.type) {\n case 'bytes':\n return {\n type: 'image',\n source: { type: 'bytes', data: source.data.slice(), mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'url':\n return {\n type: 'image',\n source: {\n type: 'url',\n url: source.url,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'base64':\n return {\n type: 'image',\n source: { type: 'base64', data: source.data, mediaType: source.mediaType },\n ...(part.label ? { label: part.label } : {}),\n };\n case 'provider-file':\n return {\n type: 'image',\n source: {\n type: 'provider-file',\n provider: source.provider,\n reference: source.reference,\n ...(source.mediaType ? { mediaType: source.mediaType } : {}),\n },\n ...(part.label ? { label: part.label } : {}),\n };\n }\n });\n}\n\nfunction cloneMessages(messages: readonly ChatMessage[]): ChatMessage[] {\n return messages.map((message) => ({\n ...message,\n content: cloneModelInput(message.content),\n ...(message.tool_calls\n ? {\n tool_calls: message.tool_calls.map((toolCall) => ({\n ...toolCall,\n function: { ...toolCall.function },\n })),\n }\n : {}),\n ...(message.providerMetadata\n ? { providerMetadata: cloneValue(message.providerMetadata) as Record<string, unknown> }\n : {}),\n }));\n}\n\nfunction cloneOptions(options: ChatOptions): ChatOptions {\n return {\n ...options,\n ...(options.tools\n ? {\n tools: options.tools.map((tool) => ({\n ...tool,\n function: { ...tool.function, parameters: cloneValue(tool.function.parameters) },\n })),\n }\n : {}),\n ...(options.providerOptions\n ? { providerOptions: cloneValue(options.providerOptions) as Record<string, unknown> }\n : {}),\n };\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw signal.reason;\n}\n\nasync function awaitWithSignal<T>(\n value: PromiseLike<T> | T,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (!signal) return await value;\n throwIfAborted(signal);\n\n return await new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n Promise.resolve(value).then(\n (result) => {\n signal.removeEventListener('abort', onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nasync function abortableDelay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n if (ms <= 0) {\n throwIfAborted(signal);\n return;\n }\n if (!signal) {\n await new Promise<void>((resolve) => setTimeout(resolve, ms));\n return;\n }\n throwIfAborted(signal);\n\n await new Promise<void>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined = setTimeout(() => {\n timer = undefined;\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n signal.removeEventListener('abort', onAbort);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nfunction richParts(input: ModelInput): readonly InputContentPart[] {\n return typeof input === 'string' ? [] : input;\n}\n\nfunction randomAlphanumeric(length: number): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars[Math.floor(Math.random() * chars.length)];\n }\n return result;\n}\n\nfunction generateFromSchema(schema: unknown): unknown {\n if (schema instanceof z.ZodString) return randomAlphanumeric(8 + Math.floor(Math.random() * 13));\n if (schema instanceof z.ZodNumber) {\n const min = Number.isFinite(schema.minValue) ? schema.minValue! : 0;\n const max = Number.isFinite(schema.maxValue) ? schema.maxValue! : 100;\n return min + Math.random() * (max - min);\n }\n if (schema instanceof z.ZodBoolean) return Math.random() < 0.5;\n if (schema instanceof z.ZodArray) {\n const count = 1 + Math.floor(Math.random() * 3);\n return Array.from({ length: count }, () => generateFromSchema(schema.element));\n }\n if (schema instanceof z.ZodObject) {\n const obj: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema.shape)) obj[key] = generateFromSchema(value);\n return obj;\n }\n if (schema instanceof z.ZodOptional) {\n if (Math.random() < 0.5) return undefined;\n return generateFromSchema(schema.unwrap());\n }\n if (schema instanceof z.ZodDefault) return schema.parse(undefined);\n if (schema instanceof z.ZodEnum) {\n const values = schema.options;\n return values.length > 0 ? values[Math.floor(Math.random() * values.length)] : '';\n }\n if (schema instanceof z.ZodLiteral) return [...schema.values][0];\n if (schema instanceof z.ZodNullable) return null;\n if (schema instanceof z.ZodUnion) {\n const options = schema.options;\n return generateFromSchema(options[Math.floor(Math.random() * options.length)]);\n }\n return {};\n}\n\nexport class MockProvider implements Provider {\n readonly name = 'mock';\n private _calls: { messages: ChatMessage[]; options: ChatOptions }[] = [];\n /** Per-call optional chunk arrays, set by `sequence()` / `chunked()` so\n * `stream()` can yield one `text_delta` per chunk. Per-call indexed\n * alongside the response sequence. */\n private chunkSequence?: Array<string[] | undefined>;\n /** Optional ms delay between successive chunks during `stream()`. Useful\n * for dev fixtures that need to demonstrate streaming UX visually\n * (without a delay, mock chunks fire synchronously and complete in\n * microseconds — invisible to a human watching). Default 0 keeps\n * every existing test fast.\n *\n * Set via `provider.chunkDelayMs = 50` after construction. Not part of\n * any factory's surface to keep the test API minimal. */\n chunkDelayMs = 0;\n\n /** How this mock reports the unified `effort` it was asked for. Undefined (the\n * default) means the mock implements no `effortResolution` at all, so the\n * runtime emits no `provider_diagnostic`. Set it with\n * {@link MockProvider.withEffortResolution} to drive the clamp-reporting path. */\n effortResolution?: (\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined;\n\n /**\n * Make this mock report an effort clamp, so a test can exercise the runtime's\n * `provider_diagnostic` path without a real adapter. Pass a fixed\n * {@link EffortResolution} or a function of the request knobs.\n *\n * ```ts\n * const provider = MockProvider.echo().withEffortResolution({\n * requested: 'none',\n * effective: 'minimal',\n * clamped: true,\n * cause: 'this model cannot disable thinking',\n * });\n * ```\n */\n withEffortResolution(\n resolution:\n | EffortResolution\n | undefined\n | ((\n options: Pick<\n ChatOptions,\n 'model' | 'effort' | 'thinkingBudget' | 'includeThoughts' | 'providerOptions'\n >,\n ) => EffortResolution | undefined),\n ): this {\n this.effortResolution = typeof resolution === 'function' ? resolution : () => resolution;\n return this;\n }\n\n private constructor(\n private responseFn: (\n messages: ChatMessage[],\n callIndex: number,\n ) => ProviderResponse | Promise<ProviderResponse>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse> {\n // Each consumer gets independent ownership: a handler can inspect or mutate\n // its copy without changing the durable assertion record (including bytes).\n const recordedMessages = cloneMessages(messages);\n const callIndex = this._calls.length;\n this._calls.push({ messages: recordedMessages, options: cloneOptions(options) });\n throwIfAborted(options.signal);\n const response = Promise.resolve().then(() =>\n this.responseFn(cloneMessages(messages), callIndex),\n );\n return await awaitWithSignal(response, options.signal);\n }\n\n inputCapabilities(_model: string): { image: { sources: readonly InputMediaSource['type'][] } } {\n return { image: { sources: ['url', 'bytes', 'base64', 'provider-file'] } };\n }\n\n validateInput(request: ProviderInputValidationRequest): ProviderInputValidationResult {\n const effectiveModel =\n typeof request.providerOptions?.model === 'string'\n ? request.providerOptions.model\n : request.model;\n const fail = (source?: string, feature?: string): never => {\n throw new UnsupportedModelInputError({\n provider: this.name,\n model: effectiveModel || request.model,\n modality: 'image',\n ...(source ? { source } : {}),\n ...(feature ? { feature } : {}),\n });\n };\n\n if (request.providerOptions && 'input' in request.providerOptions) {\n fail(undefined, 'raw input-container providerOptions');\n }\n for (const message of request.history) {\n if (typeof message.content === 'string') continue;\n if (message.role !== 'user') fail(undefined, 'rich non-user history');\n for (const part of richParts(message.content)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n }\n for (const part of richParts(request.input)) {\n if (\n part.type === 'image' &&\n part.source.type === 'provider-file' &&\n part.source.provider !== this.name\n ) {\n fail('provider-file');\n }\n }\n return { effectiveModel };\n }\n\n async *stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk> {\n const callIndex = this._calls.length;\n const response = await this.chat(messages, options);\n throwIfAborted(options.signal);\n const chunks = this.chunkSequence?.[callIndex];\n if (chunks && chunks.length > 0) {\n // Sanity guard — if a caller passes chunks AND content, they MUST\n // match. Otherwise tests pass while the real prod content silently\n // diverges from what the streaming path observes.\n const joined = chunks.join('');\n if (joined !== response.content) {\n throw new Error(\n `MockProvider.stream: chunks.join('') !== content. ` +\n `chunks=\"${joined}\" content=\"${response.content}\"`,\n );\n }\n for (let i = 0; i < chunks.length; i++) {\n throwIfAborted(options.signal);\n yield { type: 'text_delta', content: chunks[i] };\n if (this.chunkDelayMs > 0 && i < chunks.length - 1) {\n await abortableDelay(this.chunkDelayMs, options.signal);\n }\n }\n } else if (response.content) {\n throwIfAborted(options.signal);\n yield { type: 'text_delta', content: response.content };\n }\n if (response.tool_calls) {\n for (const tc of response.tool_calls) {\n throwIfAborted(options.signal);\n yield {\n type: 'tool_call_delta',\n id: tc.id,\n name: tc.function.name,\n arguments: tc.function.arguments,\n };\n }\n }\n throwIfAborted(options.signal);\n yield {\n type: 'done',\n usage: response.usage,\n // Forwarded like `chat()` does. Without it a streamed mock ask reported no\n // cost at all, so `ctx.budget` and every cost assertion silently saw zero\n // on the streaming path while the same fixture priced correctly on the\n // non-streaming one — a mock that disagrees with itself between paths.\n cost: response.cost,\n providerMetadata: response.providerMetadata,\n // Conditional spread, not `timing: response.timing` — a mock response with\n // no timing must produce a `done` chunk with NO `timing` key at all, so a\n // consumer's `'timing' in chunk` check reads the same as a real adapter's.\n ...(response.timing ? { timing: response.timing } : {}),\n };\n }\n\n static sequence(\n responses: Array<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Deterministic provider-call timing, surfaced on `ProviderResponse.timing`\n * and on the streamed terminal `done` chunk. Lets a test drive\n * `agent_call_end.timing`, the `TimeoutError` breakdown, and the eval\n * per-model rollup without real clocks or a real transport. Omit it and\n * the mock behaves exactly like an uninstrumented custom provider. */\n timing?: CallTiming;\n /** When set, `stream()` yields one `text_delta` per entry instead\n * of one big delta with the full content. Use to exercise\n * partial-JSON parsing, structural-boundary throttling, and\n * cross-attempt token retention in tests. Must satisfy\n * `chunks.join('') === content`. */\n chunks?: string[];\n }>,\n ): MockProvider {\n const provider = new MockProvider((_messages, callIndex) => {\n if (callIndex >= responses.length) {\n throw new Error(\n `MockProvider.sequence: no response for call index ${callIndex}. Only ${responses.length} responses defined.`,\n );\n }\n const resp = responses[callIndex];\n return {\n content: resp.content,\n tool_calls: resp.tool_calls,\n providerMetadata: resp.providerMetadata,\n usage: resp.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: resp.cost ?? 0,\n // Copied, not shared: the caller keeps ownership of the fixture, so a\n // consumer that mutates the returned block cannot corrupt a later call's\n // expectation (same rule the message/options clones follow).\n ...(resp.timing ? { timing: { ...resp.timing } } : {}),\n };\n });\n provider.chunkSequence = responses.map((r) => r.chunks);\n return provider;\n }\n\n /**\n * Convenience: build a `sequence()` from plain content strings, splitting\n * each one into fixed-size chunks for the streaming path. Default\n * `chunkSize` is 4 chars (≈1 token).\n */\n static chunked(contents: string[], chunkSize = 4): MockProvider {\n const responses = contents.map((content) => {\n const chunks: string[] = [];\n for (let i = 0; i < content.length; i += chunkSize) {\n chunks.push(content.slice(i, i + chunkSize));\n }\n return { content, chunks };\n });\n return MockProvider.sequence(responses);\n }\n\n static echo(): MockProvider {\n return new MockProvider((messages) => {\n const lastUser = [...messages].reverse().find((m) => m.role === 'user');\n return {\n content: lastUser ? inputText(lastUser.content) : '',\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n };\n });\n }\n\n static json(schema: unknown): MockProvider {\n return new MockProvider(() => ({\n content: JSON.stringify(generateFromSchema(schema)),\n usage: { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: 0,\n }));\n }\n\n static replay(source: string | ProviderResponse[]): MockProvider {\n const data =\n typeof source === 'string'\n ? (JSON.parse(readFileSync(source, 'utf-8')) as ProviderResponse[])\n : source;\n return new MockProvider((_messages, callIndex) => {\n if (callIndex >= data.length) {\n throw new Error(`MockProvider.replay: no recorded response for call index ${callIndex}`);\n }\n return data[callIndex];\n });\n }\n\n static fn(\n handler: (\n messages: ChatMessage[],\n callIndex: number,\n ) =>\n | {\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */\n timing?: CallTiming;\n }\n | Promise<{\n content: string;\n tool_calls?: ToolCallMessage[];\n providerMetadata?: Record<string, unknown>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n cost?: number;\n /** Per-call timing — see the `timing` note on {@link MockProvider.sequence}. */\n timing?: CallTiming;\n }>,\n ): MockProvider {\n return new MockProvider(async (messages, callIndex) => {\n const result = await handler(messages, callIndex);\n return {\n ...result,\n usage: result.usage ?? { prompt_tokens: 10, completion_tokens: 10, total_tokens: 20 },\n cost: result.cost ?? 0,\n };\n });\n }\n}\n","export class MockTool {\n private _calls: { input: unknown }[] = [];\n\n constructor(\n readonly name: string,\n private handler: (input: unknown) => unknown | Promise<unknown>,\n ) {}\n\n get calls() {\n return this._calls;\n }\n\n async execute(input: unknown): Promise<unknown> {\n this._calls.push({ input });\n return this.handler(input);\n }\n\n static create(name: string, handler: (input: unknown) => unknown | Promise<unknown>): MockTool {\n return new MockTool(name, handler);\n }\n}\n","import type {\n TranscriptionCapabilities,\n TranscriptionProvider,\n TranscriptionProviderRequest,\n TranscriptionProviderResult,\n} from '@axlsdk/axl';\n\nfunction clone<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction cloneRequest(\n request: TranscriptionProviderRequest,\n includeSignal: boolean,\n): TranscriptionProviderRequest {\n const audio =\n request.audio.type === 'bytes'\n ? { ...request.audio, data: request.audio.data.slice() }\n : { ...request.audio };\n return {\n model: request.model,\n audio,\n ...(request.language !== undefined ? { language: request.language } : {}),\n ...(request.timestamps !== undefined ? { timestamps: request.timestamps } : {}),\n ...(request.diarization !== undefined ? { diarization: request.diarization } : {}),\n ...(request.providerOptions ? { providerOptions: clone(request.providerOptions) } : {}),\n ...(includeSignal && request.signal ? { signal: request.signal } : {}),\n };\n}\n\n/** Dedicated transcription mock. It intentionally does not extend MockProvider,\n * so chat fixtures cannot accidentally become transcription adapters. */\nexport class MockTranscriptionProvider implements TranscriptionProvider {\n private _calls: TranscriptionProviderRequest[] = [];\n\n constructor(\n private readonly response: (\n request: TranscriptionProviderRequest,\n callIndex: number,\n ) => TranscriptionProviderResult | Promise<TranscriptionProviderResult>,\n private readonly capabilitiesFor: (\n model: string,\n ) => TranscriptionCapabilities | undefined = () => ({\n sources: ['bytes', 'base64', 'provider-file'],\n timestamps: ['segment', 'word'],\n diarization: true,\n }),\n ) {}\n\n get calls(): readonly TranscriptionProviderRequest[] {\n return this._calls.map(clone);\n }\n\n capabilities(model: string): TranscriptionCapabilities | undefined {\n return this.capabilitiesFor(model);\n }\n\n async transcribe(request: TranscriptionProviderRequest): Promise<TranscriptionProviderResult> {\n // AbortSignal cannot be structured-cloned. Keep it only on the callback\n // request so fixtures can observe cancellation; recorded calls omit it.\n const snapshot = cloneRequest(request, false);\n this._calls.push(snapshot);\n return clone(await this.response(cloneRequest(request, true), this._calls.length - 1));\n }\n\n static text(text: string): MockTranscriptionProvider {\n return new MockTranscriptionProvider(() => ({ transcript: { text } }));\n }\n}\n","import { MockProvider } from './mock-provider.js';\nimport { MockTool } from './mock-tool.js';\nimport { MockTranscriptionProvider } from './mock-transcription-provider.js';\nimport type {\n AxlEvent,\n AwaitHumanOptions,\n HumanDecision,\n ProviderResponse,\n AgentCallInfo,\n AxlConfig,\n AnyWorkflow,\n ToolCallOutcome,\n ObservationStatus,\n} from '@axlsdk/axl';\nimport {\n WorkflowContext,\n MemoryStore,\n ProviderRegistry,\n TranscriptionProviderRegistry,\n eventCostContribution,\n isUnpricedLeaf,\n} from '@axlsdk/axl';\nimport type { EventStreamOptions, WorkflowContextInit } from '@axlsdk/axl';\n\n// `AnyWorkflow` is a re-exported alias for `Workflow<any, any>` — see\n// the JSDoc on `AnyWorkflow` in `@axlsdk/axl/workflow.ts`. The bivariant\n// `any` is load-bearing for satisfying narrowly-typed handlers (e.g., a\n// `Workflow<TInput=MsgType>` whose handler takes `WorkflowContext<MsgType>`).\n\n/** One accepted provider-issued tool invocation, recorded from its terminal v2 event.\n *\n * Narrow `outcome.status` before reading status-specific fields. Correlation uses\n * `(executionId, askId, callId)`; `name` is the canonical tool name.\n */\nexport type RecordedToolCall = {\n name: string;\n executionId: string;\n askId: string;\n callId: string;\n args: unknown;\n requestedTool?: string;\n outcome: ToolCallOutcome;\n};\nexport type RecordedAgentCall = Partial<AgentCallInfo> &\n Pick<AgentCallInfo, 'agent' | 'prompt' | 'response'>;\nexport type RecordedStep = { step: number; type: string; data: unknown };\n\nfunction generateExecutionId(): string {\n return `test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction preserveDrainCause(error: Error, cause: unknown): Error {\n if (cause === undefined || cause === error || 'cause' in error) return error;\n Object.defineProperty(error, 'cause', {\n configurable: true,\n enumerable: false,\n value: cause,\n });\n return error;\n}\n\nexport type AxlTestRuntimeOptions = {\n record?: string;\n humanDecisions?: (options: AwaitHumanOptions) => HumanDecision;\n /** AxlConfig for the test runtime. Controls trace level/redact, context management, etc.\n * Defaults to `{}`. Set `{ trace: { level: 'full' } }` to verify verbose-mode snapshots\n * in tests, or `{ trace: { redact: true } }` to verify redaction policy in tests. */\n config?: AxlConfig;\n};\n\nexport class AxlTestRuntime {\n private workflows = new Map<string, AnyWorkflow>();\n private mockProviders = new Map<string, MockProvider>();\n private mockTranscriptionProviders = new Map<string, MockTranscriptionProvider>();\n private mockToolMap = new Map<string, MockTool>();\n private _toolCalls: RecordedToolCall[] = [];\n private _agentCalls: RecordedAgentCall[] = [];\n private _steps: RecordedStep[] = [];\n private _traceLog: AxlEvent[] = [];\n private _totalCost = 0;\n private _unpriced = false;\n private _stepCounter = 0;\n private recordPath?: string;\n private recorded: ProviderResponse[] = [];\n private _executionId: string = generateExecutionId();\n private _humanDecisionHandler?: (options: AwaitHumanOptions) => HumanDecision;\n private _config: AxlConfig;\n\n constructor(options?: AxlTestRuntimeOptions) {\n this.recordPath = options?.record;\n this._humanDecisionHandler = options?.humanDecisions;\n this._config = options?.config ?? {};\n }\n\n register(workflow: AnyWorkflow): void {\n this.workflows.set(workflow.name, workflow);\n }\n\n mockProvider(name: string, provider: MockProvider): void {\n this.mockProviders.set(name, provider);\n }\n\n mockTranscriptionProvider(name: string, provider: MockTranscriptionProvider): void {\n this.mockTranscriptionProviders.set(name, provider);\n }\n\n mockTool<TInput = unknown>(\n name: string,\n handler: (input: TInput) => unknown | Promise<unknown>,\n ): void {\n // The MockTool registry is type-erased (`(input: unknown) => unknown`)\n // because the runtime can't statically know each tool's input shape; tests\n // opt in to a typed input via the generic, and we cast on storage.\n this.mockToolMap.set(name, MockTool.create(name, handler as (input: unknown) => unknown));\n }\n\n async execute(\n workflowName: string,\n input: unknown,\n options?: {\n metadata?: Record<string, unknown>;\n events?: EventStreamOptions;\n branchDrainTimeoutMs?: number;\n },\n ): Promise<unknown> {\n const workflow = this.workflows.get(workflowName);\n if (!workflow) throw new Error(`Workflow \"${workflowName}\" not registered`);\n\n // Reset state between executions\n this._toolCalls = [];\n this._agentCalls = [];\n this._steps = [];\n this._traceLog = [];\n this._totalCost = 0;\n this._unpriced = false;\n this._stepCounter = 0;\n this.recorded = [];\n this._executionId = generateExecutionId();\n\n const validated = workflow.inputSchema.parse(input);\n\n // Build ProviderRegistry from registered mock providers\n const registry = new ProviderRegistry();\n registry.clearFactories(); // Remove built-in factories; only use explicit mocks\n for (const [name, provider] of this.mockProviders) {\n registry.registerInstance(name, provider);\n }\n const transcriptionRegistry = new TranscriptionProviderRegistry();\n for (const [name, provider] of this.mockTranscriptionProviders) {\n transcriptionRegistry.registerInstance(name, provider);\n }\n // Fallback resolution: 'default' key or single-provider\n const defaultProvider = this.mockProviders.get('default');\n if (defaultProvider) {\n registry.setFallback(defaultProvider);\n } else if (this.mockProviders.size === 1) {\n const onlyProvider = this.mockProviders.values().next().value!;\n registry.setFallback(onlyProvider);\n }\n\n // Build toolOverrides from mockToolMap\n const toolOverrides = new Map<string, (args: unknown) => Promise<unknown>>();\n for (const [name, mockTool] of this.mockToolMap) {\n toolOverrides.set(name, async (args: unknown) => {\n return mockTool.execute(args);\n });\n }\n\n // Previously `_recordStep('workflow_start', ...)` was called here with\n // raw `{ workflow, input }` data, AND `ctx._emitWorkflowStart` below\n // pushed through the onTrace handler (which also bumps `_stepCounter`\n // and appends to `_steps`). Result: two entries in `steps()` per\n // workflow_start, with the first bypassing `config.trace.redact`.\n // The `_recordStep` call is removed — onTrace is the single source\n // of truth for `_steps`, matching the behavior of every other event\n // type in the test runtime. Reviewer architecture §5.\n\n const init: WorkflowContextInit = {\n input: validated,\n executionId: this._executionId,\n metadata: options?.metadata,\n // Thread the constructor-provided config so tests can exercise\n // trace.level === 'full' and trace.redact behavior end-to-end.\n config: this._config,\n // Thread `events` config so tests can exercise the queue cap and\n // overflow policy on `ctx.events`. Mirrors the production runtime.\n eventStreamOptions: options?.events,\n branchDrainTimeoutMs: options?.branchDrainTimeoutMs,\n // Production runtime threads this (see `runtime.ts:594` in\n // `execute()` and `:765` in `stream()`). `emitEvent` auto-stamps\n // `event.workflow` from it, so consumers grouping by workflow\n // (Cost Dashboard `byWorkflow`, `trackExecution.metadata.workflows`,\n // any eval runner) see the same attribution in tests and prod.\n workflowName,\n providerRegistry: registry,\n transcriptionProviderRegistry: transcriptionRegistry,\n stateStore: new MemoryStore(),\n toolOverrides: toolOverrides.size > 0 ? toolOverrides : undefined,\n awaitHumanHandler: this._humanDecisionHandler\n ? (opts: AwaitHumanOptions) => this._humanDecisionHandler!(opts)\n : undefined,\n onTrace: (event: AxlEvent) => {\n this._traceLog.push(event);\n this._stepCounter++;\n this._steps.push({\n step: this._stepCounter,\n type: event.type,\n data: (event as { data?: unknown }).data,\n });\n\n if (event.type === 'tool_call_end') {\n const { args, requestedTool, outcome } = event.data;\n this._toolCalls.push({\n name: event.tool,\n executionId: event.executionId,\n askId: event.askId,\n callId: event.callId,\n args,\n ...(requestedTool !== undefined ? { requestedTool } : {}),\n outcome,\n });\n }\n\n // Single-source-of-truth cost accumulator. Skips ask_end\n // rollups (decision 10) and guards NaN/Infinity.\n this._totalCost += eventCostContribution(event);\n // Honest aggregate: one unpriced leaf makes `totalCost()` a lower bound.\n if (isUnpricedLeaf(event)) this._unpriced = true;\n\n // Track provider responses for recording\n if (event.type === 'agent_call_end' && event.data) {\n const { response } = event.data as { prompt?: string; response?: string };\n if (response !== undefined) {\n this.recorded.push({\n content: response,\n usage: event.tokens\n ? {\n prompt_tokens: event.tokens.input ?? 0,\n completion_tokens: event.tokens.output ?? 0,\n total_tokens:\n (event.tokens.input ?? 0) +\n (event.tokens.output ?? 0) +\n (event.tokens.reasoning ?? 0),\n ...(event.tokens.reasoning !== undefined\n ? { reasoning_tokens: event.tokens.reasoning }\n : {}),\n }\n : { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n ...(event.cost !== undefined ? { cost: event.cost } : {}),\n });\n }\n }\n },\n onAgentCallComplete: (call: AgentCallInfo) => {\n this._agentCalls.push(call);\n },\n };\n\n const ctx = new WorkflowContext(init);\n\n // Emit workflow_start through the same pipeline production uses,\n // AFTER ctx exists so `emitEvent`'s redact/step/timestamp handling\n // applies. Previously emitted via `_pushTrace` which bypassed\n // redaction — `config: { trace: { redact: true } }` on the test\n // runtime would scrub intermediate events but leak `input`/`result`\n // through the synthesized workflow_start/end.\n const startedAt = Date.now();\n ctx._emitWorkflowStart(validated);\n try {\n const result = await workflow.handler(ctx);\n const observation = await ctx._drainBranchWork();\n\n // Validate output schema if defined\n if (workflow.outputSchema) workflow.outputSchema.parse(result);\n\n ctx._emitWorkflowEnd({\n status: 'completed',\n duration: Date.now() - startedAt,\n result,\n observation,\n });\n // `_steps` is populated by onTrace for every event (including\n // workflow_end); no separate `_recordStep` call needed — that\n // would double-count.\n\n if (this.recordPath) await this._writeRecording();\n return result;\n } catch (err) {\n let workflowError = err;\n let observation: ObservationStatus = { complete: true };\n try {\n observation = await ctx._drainBranchWork();\n } catch (drainError) {\n workflowError =\n drainError instanceof Error ? preserveDrainCause(drainError, workflowError) : drainError;\n }\n // Parity with production: failed workflows get a terminal\n // `workflow_end(status: 'failed')` event so consumers counting\n // workflow_start ↔ workflow_end pairs never see an unclosed one.\n // `aborted` flag mirrors the runtime.ts AbortError detection.\n const aborted =\n typeof workflowError === 'object' &&\n workflowError !== null &&\n (workflowError as { name?: unknown }).name === 'AbortError';\n ctx._emitWorkflowEnd({\n status: 'failed',\n duration: Date.now() - startedAt,\n error: workflowError instanceof Error ? workflowError.message : String(workflowError),\n ...(aborted ? { aborted: true } : {}),\n observation,\n });\n // `_steps` populated by onTrace (see comment on the success path).\n throw workflowError;\n }\n }\n\n toolCalls(name?: string): RecordedToolCall[] {\n return name ? this._toolCalls.filter((c) => c.name === name) : this._toolCalls;\n }\n\n agentCalls(name?: string): RecordedAgentCall[] {\n return name ? this._agentCalls.filter((c) => c.agent === name) : this._agentCalls;\n }\n\n totalCost(): number {\n return this._totalCost;\n }\n /** True when any cost-bearing call was unpriced — `totalCost()` is then a\n * LOWER BOUND. Mirrors `ExecutionInfo.unpriced` / `BudgetResult.unpriced`. */\n unpriced(): boolean {\n return this._unpriced;\n }\n steps(): RecordedStep[] {\n return this._steps;\n }\n traceLog(): AxlEvent[] {\n return this._traceLog;\n }\n\n private async _writeRecording(): Promise<void> {\n if (!this.recordPath) return;\n const { writeFile, mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(this.recordPath), { recursive: true });\n await writeFile(this.recordPath, JSON.stringify(this.recorded, null, 2), 'utf8');\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axlsdk/testing",
3
- "version": "0.22.3",
3
+ "version": "0.23.1",
4
4
  "description": "Testing utilities for Axl agentic workflows",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -22,7 +22,7 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@axlsdk/axl": "0.22.3"
25
+ "@axlsdk/axl": "0.23.1"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "zod": "^4.0.0"