@vitest-evals/harness-pi-ai 0.9.0 → 0.11.0

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
@@ -43,23 +43,24 @@ describeEval("refund agent", { harness }, (it) => {
43
43
  ```
44
44
 
45
45
  `run` executes the Pi agent under test. Judges are created separately; keep
46
- judge prompts and model calls in the judge instead of putting a judge model
47
- call on the app harness.
46
+ judge prompts on the judge and model calls on a judge harness instead of
47
+ putting a judge model call on the app harness.
48
48
 
49
49
  ```ts
50
- const RefundRubricJudge = createJudge(
51
- "RefundRubricJudge",
52
- async (ctx: JudgeContext<string, RefundDecision>) => {
53
- const verdict = await queryRefundJudgeModel({
54
- prompt: formatJudgePrompt({
55
- input: ctx.input,
56
- output: ctx.output,
57
- }),
58
- });
50
+ import { getModel } from "@mariozechner/pi-ai";
51
+ import { piAiJudgeHarness } from "@vitest-evals/harness-pi-ai";
52
+ import { describeEval, FactualityJudge } from "vitest-evals";
59
53
 
60
- return parseJudgeVerdict(verdict);
61
- },
62
- );
54
+ const judgeHarness = piAiJudgeHarness({
55
+ model: getModel("anthropic", "claude-sonnet-4-5"),
56
+ temperature: 0,
57
+ });
58
+ const factualityJudge = FactualityJudge({ judgeHarness });
59
+
60
+ describeEval("refund agent", {
61
+ harness,
62
+ judges: [factualityJudge],
63
+ });
63
64
  ```
64
65
 
65
66
  If the agent already exposes its own tools, the adapter will infer them from
@@ -170,7 +171,7 @@ overwrite the native recording.
170
171
 
171
172
  Supported modes:
172
173
 
174
+ - `auto` (default): replay when present, otherwise call live and write a recording
175
+ - `record`: always call live and overwrite the recording
173
176
  - `off`: never read or write recordings
174
- - `auto`: replay when present, otherwise call live and write a recording
175
177
  - `strict`: require an existing recording and fail if it is missing
176
- - `record`: always call live and overwrite the recording
package/dist/index.d.mts CHANGED
@@ -1,11 +1,61 @@
1
+ import * as vitest_evals_judges from 'vitest-evals/judges';
1
2
  import { NormalizedMessage, JsonValue, HarnessMetadata, HarnessContext, HarnessRun, Harness } from 'vitest-evals/harness';
2
3
  import { ReplayMode, ToolRecording, ToolReplayConfig } from 'vitest-evals/replay';
4
+ import { Api, Model, SimpleStreamOptions } from '@mariozechner/pi-ai';
3
5
 
4
6
  type MaybePromise<T> = T | Promise<T>;
5
7
  type JsonOutput<TValue> = [TValue] extends [JsonValue | undefined] ? TValue : undefined;
6
8
  type ResultFieldOutput<TResult, TKey extends string> = TKey extends keyof TResult ? Record<string, never> extends Pick<TResult, TKey> ? JsonOutput<TResult[TKey]> | undefined : JsonOutput<TResult[TKey]> : undefined;
7
9
  type AgentSource<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = TAgent | ((args: PiAiCreateAgentArgs<TInput, TMetadata>) => MaybePromise<TAgent>);
8
10
  type AnyPiAiToolset<TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = Record<string, PiAiToolDefinition<any, any, TInput, TMetadata>>;
11
+ /**
12
+ * Configuration for adapting a Pi AI model into a judge harness.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { getModel } from "@mariozechner/pi-ai";
17
+ * import { piAiJudgeHarness } from "@vitest-evals/harness-pi-ai";
18
+ *
19
+ * const judgeHarness = piAiJudgeHarness({
20
+ * model: getModel("anthropic", "claude-sonnet-4-5"),
21
+ * temperature: 0,
22
+ * });
23
+ * ```
24
+ */
25
+ interface PiAiJudgeHarnessConfig<TApi extends Api = Api> {
26
+ /** Pi AI model used for judge prompts. */
27
+ model: Model<TApi>;
28
+ /** Optional display name for diagnostics. */
29
+ name?: string;
30
+ /** Optional judge-model temperature. */
31
+ temperature?: number;
32
+ /** Optional judge-model token cap. */
33
+ maxOutputTokens?: number;
34
+ /** Additional Pi AI stream options forwarded to `completeSimple(...)`. */
35
+ options?: Omit<SimpleStreamOptions, "signal" | "temperature" | "maxTokens">;
36
+ }
37
+ /**
38
+ * Adapts a Pi AI model into the provider-neutral judge harness API.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { getModel } from "@mariozechner/pi-ai";
43
+ * import { piAiJudgeHarness } from "@vitest-evals/harness-pi-ai";
44
+ * import { describeEval, FactualityJudge } from "vitest-evals";
45
+ *
46
+ * const judgeHarness = piAiJudgeHarness({
47
+ * model: getModel("anthropic", "claude-sonnet-4-5"),
48
+ * temperature: 0,
49
+ * });
50
+ *
51
+ * describeEval("qa agent", {
52
+ * harness: qaHarness,
53
+ * judgeHarness,
54
+ * judges: [FactualityJudge()],
55
+ * }, (it) => {});
56
+ * ```
57
+ */
58
+ declare function piAiJudgeHarness<TApi extends Api>(config: PiAiJudgeHarnessConfig<TApi>): vitest_evals_judges.JudgeHarness;
9
59
  type InferredPiAiToolset<TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = Record<string, PiAiToolDefinition<Record<string, JsonValue>, JsonValue, TInput, TMetadata>>;
10
60
  type PiAiResultOutput<TResult> = TResult extends HarnessRun<infer TOutput> ? TOutput : "output" extends keyof TResult ? ResultFieldOutput<TResult, "output"> : undefined;
11
61
  type PiAiAgentResult<TAgent, TInput, TMetadata extends HarnessMetadata, TTools extends PiAiToolset<TInput, TMetadata>> = TAgent extends {
@@ -118,4 +168,4 @@ declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessM
118
168
  declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata, TResult = unknown>(options: PiAiHarnessInferredToolsRunOptionsWithoutOutput<TAgent, TInput, TMetadata, TResult>): Harness<TInput, PiAiResultOutput<Awaited<TResult>>, TMetadata>;
119
169
  declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata>(options: PiAiHarnessInferredToolsAgentOptionsWithoutOutput<TAgent, TInput, TMetadata>): Harness<TInput, PiAiResultOutput<PiAiAgentResult<TAgent, TInput, TMetadata, InferredPiAiToolset<TInput, TMetadata>>>, TMetadata>;
120
170
 
121
- export { type PiAiCreateAgentArgs, type PiAiEventSink, type PiAiHarnessResultArgs, type PiAiHarnessRunArgs, type PiAiReplayMode, type PiAiRuntime, type PiAiToolContext, type PiAiToolDefinition, type PiAiToolRecording, type PiAiToolReplayConfig, type PiAiToolReplayPolicies, type PiAiToolReplayPolicy, type PiAiToolset, piAiHarness };
171
+ export { type PiAiCreateAgentArgs, type PiAiEventSink, type PiAiHarnessResultArgs, type PiAiHarnessRunArgs, type PiAiJudgeHarnessConfig, type PiAiReplayMode, type PiAiRuntime, type PiAiToolContext, type PiAiToolDefinition, type PiAiToolRecording, type PiAiToolReplayConfig, type PiAiToolReplayPolicies, type PiAiToolReplayPolicy, type PiAiToolset, piAiHarness, piAiJudgeHarness };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,61 @@
1
+ import * as vitest_evals_judges from 'vitest-evals/judges';
1
2
  import { NormalizedMessage, JsonValue, HarnessMetadata, HarnessContext, HarnessRun, Harness } from 'vitest-evals/harness';
2
3
  import { ReplayMode, ToolRecording, ToolReplayConfig } from 'vitest-evals/replay';
4
+ import { Api, Model, SimpleStreamOptions } from '@mariozechner/pi-ai';
3
5
 
4
6
  type MaybePromise<T> = T | Promise<T>;
5
7
  type JsonOutput<TValue> = [TValue] extends [JsonValue | undefined] ? TValue : undefined;
6
8
  type ResultFieldOutput<TResult, TKey extends string> = TKey extends keyof TResult ? Record<string, never> extends Pick<TResult, TKey> ? JsonOutput<TResult[TKey]> | undefined : JsonOutput<TResult[TKey]> : undefined;
7
9
  type AgentSource<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = TAgent | ((args: PiAiCreateAgentArgs<TInput, TMetadata>) => MaybePromise<TAgent>);
8
10
  type AnyPiAiToolset<TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = Record<string, PiAiToolDefinition<any, any, TInput, TMetadata>>;
11
+ /**
12
+ * Configuration for adapting a Pi AI model into a judge harness.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { getModel } from "@mariozechner/pi-ai";
17
+ * import { piAiJudgeHarness } from "@vitest-evals/harness-pi-ai";
18
+ *
19
+ * const judgeHarness = piAiJudgeHarness({
20
+ * model: getModel("anthropic", "claude-sonnet-4-5"),
21
+ * temperature: 0,
22
+ * });
23
+ * ```
24
+ */
25
+ interface PiAiJudgeHarnessConfig<TApi extends Api = Api> {
26
+ /** Pi AI model used for judge prompts. */
27
+ model: Model<TApi>;
28
+ /** Optional display name for diagnostics. */
29
+ name?: string;
30
+ /** Optional judge-model temperature. */
31
+ temperature?: number;
32
+ /** Optional judge-model token cap. */
33
+ maxOutputTokens?: number;
34
+ /** Additional Pi AI stream options forwarded to `completeSimple(...)`. */
35
+ options?: Omit<SimpleStreamOptions, "signal" | "temperature" | "maxTokens">;
36
+ }
37
+ /**
38
+ * Adapts a Pi AI model into the provider-neutral judge harness API.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { getModel } from "@mariozechner/pi-ai";
43
+ * import { piAiJudgeHarness } from "@vitest-evals/harness-pi-ai";
44
+ * import { describeEval, FactualityJudge } from "vitest-evals";
45
+ *
46
+ * const judgeHarness = piAiJudgeHarness({
47
+ * model: getModel("anthropic", "claude-sonnet-4-5"),
48
+ * temperature: 0,
49
+ * });
50
+ *
51
+ * describeEval("qa agent", {
52
+ * harness: qaHarness,
53
+ * judgeHarness,
54
+ * judges: [FactualityJudge()],
55
+ * }, (it) => {});
56
+ * ```
57
+ */
58
+ declare function piAiJudgeHarness<TApi extends Api>(config: PiAiJudgeHarnessConfig<TApi>): vitest_evals_judges.JudgeHarness;
9
59
  type InferredPiAiToolset<TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata> = Record<string, PiAiToolDefinition<Record<string, JsonValue>, JsonValue, TInput, TMetadata>>;
10
60
  type PiAiResultOutput<TResult> = TResult extends HarnessRun<infer TOutput> ? TOutput : "output" extends keyof TResult ? ResultFieldOutput<TResult, "output"> : undefined;
11
61
  type PiAiAgentResult<TAgent, TInput, TMetadata extends HarnessMetadata, TTools extends PiAiToolset<TInput, TMetadata>> = TAgent extends {
@@ -118,4 +168,4 @@ declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessM
118
168
  declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata, TResult = unknown>(options: PiAiHarnessInferredToolsRunOptionsWithoutOutput<TAgent, TInput, TMetadata, TResult>): Harness<TInput, PiAiResultOutput<Awaited<TResult>>, TMetadata>;
119
169
  declare function piAiHarness<TAgent, TInput = string, TMetadata extends HarnessMetadata = HarnessMetadata>(options: PiAiHarnessInferredToolsAgentOptionsWithoutOutput<TAgent, TInput, TMetadata>): Harness<TInput, PiAiResultOutput<PiAiAgentResult<TAgent, TInput, TMetadata, InferredPiAiToolset<TInput, TMetadata>>>, TMetadata>;
120
170
 
121
- export { type PiAiCreateAgentArgs, type PiAiEventSink, type PiAiHarnessResultArgs, type PiAiHarnessRunArgs, type PiAiReplayMode, type PiAiRuntime, type PiAiToolContext, type PiAiToolDefinition, type PiAiToolRecording, type PiAiToolReplayConfig, type PiAiToolReplayPolicies, type PiAiToolReplayPolicy, type PiAiToolset, piAiHarness };
171
+ export { type PiAiCreateAgentArgs, type PiAiEventSink, type PiAiHarnessResultArgs, type PiAiHarnessRunArgs, type PiAiJudgeHarnessConfig, type PiAiReplayMode, type PiAiRuntime, type PiAiToolContext, type PiAiToolDefinition, type PiAiToolRecording, type PiAiToolReplayConfig, type PiAiToolReplayPolicies, type PiAiToolReplayPolicy, type PiAiToolset, piAiHarness, piAiJudgeHarness };
package/dist/index.js CHANGED
@@ -20,54 +20,129 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- piAiHarness: () => piAiHarness
23
+ piAiHarness: () => piAiHarness,
24
+ piAiJudgeHarness: () => piAiJudgeHarness
24
25
  });
25
26
  module.exports = __toCommonJS(index_exports);
26
27
  var import_harness = require("vitest-evals/harness");
27
28
  var import_replay = require("vitest-evals/replay");
29
+ var import_judges = require("vitest-evals/judges");
30
+ var import_pi_ai = require("@mariozechner/pi-ai");
31
+ var nextTraceId = 0;
32
+ function piAiJudgeHarness(config) {
33
+ return (0, import_judges.createJudgeHarness)({
34
+ name: config.name ?? "pi-ai",
35
+ run: async ({ responseFormat, system, prompt }, { signal }) => {
36
+ const message = await (0, import_pi_ai.completeSimple)(
37
+ config.model,
38
+ {
39
+ systemPrompt: formatPiAiJudgeSystemPrompt(system, responseFormat),
40
+ messages: [
41
+ {
42
+ role: "user",
43
+ content: prompt,
44
+ timestamp: Date.now()
45
+ }
46
+ ]
47
+ },
48
+ {
49
+ ...config.options ?? {},
50
+ ...config.temperature !== void 0 ? { temperature: config.temperature } : {},
51
+ ...config.maxOutputTokens !== void 0 ? { maxTokens: config.maxOutputTokens } : {},
52
+ ...signal ? { signal } : {}
53
+ }
54
+ );
55
+ return resolvePiAiJudgeText(message);
56
+ }
57
+ });
58
+ }
59
+ function formatPiAiJudgeSystemPrompt(system, responseFormat) {
60
+ if (responseFormat?.type !== "json") {
61
+ return system;
62
+ }
63
+ const jsonInstruction = [
64
+ "Return only valid JSON. Do not include markdown fences or explanatory prose.",
65
+ responseFormat.schema ? `JSON Schema:
66
+ ${JSON.stringify(responseFormat.schema, null, 2)}` : void 0
67
+ ].filter(Boolean).join("\n\n");
68
+ return system ? `${system}
69
+
70
+ ${jsonInstruction}` : jsonInstruction;
71
+ }
72
+ function resolvePiAiJudgeText(message) {
73
+ if (message.stopReason === "error" || message.stopReason === "aborted") {
74
+ throw new Error(message.errorMessage ?? "Pi AI judge harness failed.");
75
+ }
76
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
77
+ }
28
78
  var ORIGINAL_NATIVE_EXECUTE = Symbol("vitest-evals.originalNativeExecute");
29
79
  function piAiHarness(options) {
30
80
  validateOptions(options);
81
+ const harnessName = options.name ?? "pi-ai";
31
82
  const harness = {
32
- name: options.name ?? "pi-ai",
83
+ name: harnessName,
33
84
  run: async (input, context) => {
34
- const agent = await resolveAgent(options, {
35
- input,
36
- context
37
- });
38
- const messages = [
39
- {
40
- role: "user",
41
- content: (0, import_harness.normalizeContent)(input)
85
+ const startedAt = /* @__PURE__ */ new Date();
86
+ try {
87
+ const agent = await resolveAgent(options, {
88
+ input,
89
+ context
90
+ });
91
+ const messages = [
92
+ {
93
+ role: "user",
94
+ content: (0, import_harness.normalizeContent)(input)
95
+ }
96
+ ];
97
+ const inferredTools = resolveInferredToolSurfaces(
98
+ agent
99
+ );
100
+ if (hasExplicitToolset(options)) {
101
+ return await executePiHarnessRun(
102
+ options,
103
+ agent,
104
+ input,
105
+ context,
106
+ messages,
107
+ options.tools,
108
+ inferredTools.nativeToolsets
109
+ );
42
110
  }
43
- ];
44
- const inferredTools = resolveInferredToolSurfaces(
45
- agent
46
- );
47
- if (hasExplicitToolset(options)) {
48
- return executePiHarnessRun(
111
+ return await executePiHarnessRun(
49
112
  options,
50
113
  agent,
51
114
  input,
52
115
  context,
53
116
  messages,
54
- options.tools,
117
+ inferredTools.runtimeTools,
55
118
  inferredTools.nativeToolsets
56
119
  );
120
+ } catch (error) {
121
+ if ((0, import_harness.getHarnessRunFromError)(error)) {
122
+ throw error;
123
+ }
124
+ throw (0, import_harness.attachHarnessRunToError)(
125
+ error,
126
+ createFailedPiAiRun(input, context, error, harnessName, startedAt)
127
+ );
57
128
  }
58
- return executePiHarnessRun(
59
- options,
60
- agent,
61
- input,
62
- context,
63
- messages,
64
- inferredTools.runtimeTools,
65
- inferredTools.nativeToolsets
66
- );
67
129
  }
68
130
  };
69
131
  return harness;
70
132
  }
133
+ function createFailedPiAiRun(input, context, error, harnessName, startedAt) {
134
+ const run = (0, import_harness.createFailedHarnessRun)(input, error, {
135
+ artifacts: context.artifacts
136
+ });
137
+ (0, import_harness.ensureRunTrace)(run, {
138
+ name: harnessName,
139
+ startedAt,
140
+ finishedAt: /* @__PURE__ */ new Date(),
141
+ operationName: "invoke_agent",
142
+ source: "harness-pi-ai"
143
+ });
144
+ return run;
145
+ }
71
146
  function validateOptions(options) {
72
147
  if (options.agent === void 0) {
73
148
  throw new Error(
@@ -76,6 +151,7 @@ function validateOptions(options) {
76
151
  }
77
152
  }
78
153
  async function executePiHarnessRun(options, agent, input, context, messages, runtimeTools, nativeToolsets) {
154
+ const trace = createTraceRecorder(options.name ?? "pi-ai");
79
155
  const executionState = createPiToolExecutionState();
80
156
  const runtime = createRuntime({
81
157
  input,
@@ -108,6 +184,7 @@ async function executePiHarnessRun(options, agent, input, context, messages, run
108
184
  if (Object.keys(context.artifacts).length > 0 && !result.artifacts) {
109
185
  result.artifacts = context.artifacts;
110
186
  }
187
+ attachPiAiTrace(result, trace, /* @__PURE__ */ new Date());
111
188
  return result;
112
189
  }
113
190
  const normalizeResult = result;
@@ -121,21 +198,42 @@ async function executePiHarnessRun(options, agent, input, context, messages, run
121
198
  const output = options.output ? await options.output(resultArgs) : resolveOutput(normalizeResult);
122
199
  const usage = resolveUsage(normalizeResult, runtime.toolCalls.length);
123
200
  const session = resolveSession(normalizeResult, messages, output, usage);
201
+ const errors = resolveErrors(normalizeResult);
202
+ const finishedAt = /* @__PURE__ */ new Date();
124
203
  return {
125
204
  session,
126
205
  output,
127
206
  usage,
128
207
  artifacts: Object.keys(context.artifacts).length > 0 ? context.artifacts : void 0,
129
- errors: resolveErrors(normalizeResult)
208
+ errors,
209
+ traces: [
210
+ finishPiAiTrace(trace, {
211
+ session,
212
+ usage,
213
+ errors,
214
+ finishedAt
215
+ })
216
+ ]
130
217
  };
131
218
  } catch (error) {
132
219
  const usage = resolveUsage(void 0, runtime.toolCalls.length);
220
+ const session = resolveSession(void 0, messages, void 0, usage);
221
+ const finishedAt = /* @__PURE__ */ new Date();
222
+ const serializedError = (0, import_harness.serializeError)(error);
133
223
  const run = {
134
- session: resolveSession(void 0, messages, void 0, usage),
224
+ session,
135
225
  output: void 0,
136
226
  usage,
137
227
  artifacts: Object.keys(context.artifacts).length > 0 ? context.artifacts : void 0,
138
- errors: [(0, import_harness.serializeError)(error)]
228
+ errors: [serializedError],
229
+ traces: [
230
+ finishPiAiTrace(trace, {
231
+ session,
232
+ usage,
233
+ errors: [serializedError],
234
+ finishedAt
235
+ })
236
+ ]
139
237
  };
140
238
  throw (0, import_harness.attachHarnessRunToError)(error, run);
141
239
  }
@@ -155,6 +253,83 @@ function isAgentFactory(agent) {
155
253
  function hasOutputSelector(options) {
156
254
  return Boolean(options.output);
157
255
  }
256
+ function createTraceRecorder(name) {
257
+ const id = `pi_ai_trace_${++nextTraceId}`;
258
+ return {
259
+ id,
260
+ rootSpanId: `${id}:run`,
261
+ name,
262
+ startedAt: /* @__PURE__ */ new Date()
263
+ };
264
+ }
265
+ function attachPiAiTrace(run, trace, finishedAt) {
266
+ const nativeTrace = finishPiAiTrace(trace, {
267
+ session: run.session,
268
+ usage: run.usage,
269
+ errors: run.errors ?? [],
270
+ finishedAt
271
+ });
272
+ run.traces = [...run.traces ?? [], nativeTrace];
273
+ }
274
+ function finishPiAiTrace(trace, options) {
275
+ const finishedAt = options.finishedAt;
276
+ const durationMs = finishedAt.getTime() - trace.startedAt.getTime();
277
+ const rootError = options.errors?.[0] ? (0, import_harness.normalizeSpanError)(options.errors[0]) : void 0;
278
+ const rootSpan = {
279
+ id: trace.rootSpanId,
280
+ traceId: trace.id,
281
+ name: trace.name,
282
+ kind: "run",
283
+ startedAt: trace.startedAt.toISOString(),
284
+ finishedAt: finishedAt.toISOString(),
285
+ durationMs,
286
+ status: rootError ? "error" : "ok",
287
+ ...rootError ? { error: rootError } : {},
288
+ attributes: (0, import_harness.normalizeSpanAttributes)({
289
+ "gen_ai.operation.name": "invoke_agent",
290
+ "gen_ai.workflow.name": trace.name,
291
+ ...(0, import_harness.createGenAiUsageAttributes)(options.usage)
292
+ })
293
+ };
294
+ const modelSpan = createUsageModelSpan(trace, options.usage);
295
+ const spans = [
296
+ rootSpan,
297
+ ...modelSpan ? [modelSpan] : [],
298
+ ...(0, import_harness.createToolCallSpans)((0, import_harness.toolCalls)(options.session), {
299
+ traceId: trace.id,
300
+ parentId: trace.rootSpanId,
301
+ spanIdPrefix: `${trace.id}:tool`
302
+ })
303
+ ];
304
+ return {
305
+ id: trace.id,
306
+ name: trace.name,
307
+ startedAt: trace.startedAt.toISOString(),
308
+ finishedAt: finishedAt.toISOString(),
309
+ durationMs,
310
+ metadata: {
311
+ source: "harness-pi-ai"
312
+ },
313
+ spans
314
+ };
315
+ }
316
+ function createUsageModelSpan(trace, usage) {
317
+ if (!usage?.provider && !usage?.model) {
318
+ return void 0;
319
+ }
320
+ return {
321
+ id: `${trace.id}:model:1`,
322
+ traceId: trace.id,
323
+ parentId: trace.rootSpanId,
324
+ name: usage.model ? `pi-ai ${usage.model}` : "pi-ai model",
325
+ kind: "model",
326
+ status: "ok",
327
+ attributes: (0, import_harness.normalizeSpanAttributes)({
328
+ "gen_ai.operation.name": "chat",
329
+ ...(0, import_harness.createGenAiUsageAttributes)(usage)
330
+ })
331
+ };
332
+ }
158
333
  function resolveInferredToolSurfaces(agent) {
159
334
  let runtimeTools;
160
335
  const nativeToolsets = [];
@@ -286,6 +461,7 @@ async function withInstrumentedAgentTools(agent, toolsets, args, callback) {
286
461
  });
287
462
  const finishedAt = /* @__PURE__ */ new Date();
288
463
  const call = {
464
+ id: toolCallId,
289
465
  name: tool.name,
290
466
  arguments: rawArgs,
291
467
  result: execution.normalizedResult,
@@ -310,6 +486,7 @@ async function withInstrumentedAgentTools(agent, toolsets, args, callback) {
310
486
  } catch (error) {
311
487
  const finishedAt = /* @__PURE__ */ new Date();
312
488
  const call = {
489
+ id: toolCallId,
313
490
  name: tool.name,
314
491
  arguments: rawArgs,
315
492
  error: serializeToolCallError(error),
@@ -802,6 +979,7 @@ async function executeToolWithReplay({
802
979
  }
803
980
  // Annotate the CommonJS export names for ESM import in node:
804
981
  0 && (module.exports = {
805
- piAiHarness
982
+ piAiHarness,
983
+ piAiJudgeHarness
806
984
  });
807
985
  //# sourceMappingURL=index.js.map