@vitest-evals/harness-pi-ai 0.9.0-beta.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/dist/index.js ADDED
@@ -0,0 +1,670 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ piAiHarness: () => piAiHarness
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_harness = require("vitest-evals/harness");
27
+ var import_replay = require("vitest-evals/replay");
28
+ var ORIGINAL_NATIVE_EXECUTE = Symbol("vitest-evals.originalNativeExecute");
29
+ function piAiHarness(options) {
30
+ return {
31
+ name: options.name ?? "pi-ai",
32
+ run: async (input, context) => {
33
+ const agent = await resolveAgent(options);
34
+ const messages = [
35
+ {
36
+ role: "user",
37
+ content: (0, import_harness.normalizeContent)(input)
38
+ }
39
+ ];
40
+ const inferredTools = resolveInferredToolSurfaces(
41
+ agent
42
+ );
43
+ if (hasExplicitToolset(options)) {
44
+ return executePiHarnessRun(
45
+ options,
46
+ agent,
47
+ input,
48
+ context,
49
+ messages,
50
+ options.tools,
51
+ inferredTools.nativeToolsets
52
+ );
53
+ }
54
+ return executePiHarnessRun(
55
+ options,
56
+ agent,
57
+ input,
58
+ context,
59
+ messages,
60
+ inferredTools.runtimeTools,
61
+ inferredTools.nativeToolsets
62
+ );
63
+ }
64
+ };
65
+ }
66
+ async function executePiHarnessRun(options, agent, input, context, messages, runtimeTools, nativeToolsets) {
67
+ const runtime = createRuntime({
68
+ input,
69
+ context,
70
+ tools: runtimeTools,
71
+ messages
72
+ });
73
+ try {
74
+ const result = await withInstrumentedAgentTools(
75
+ agent,
76
+ nativeToolsets,
77
+ {
78
+ input,
79
+ context,
80
+ messages,
81
+ toolCalls: runtime.toolCalls
82
+ },
83
+ () => runAgent(options, {
84
+ agent,
85
+ input,
86
+ context,
87
+ runtime
88
+ })
89
+ );
90
+ if ((0, import_harness.isHarnessRun)(result) && !hasResultOverrides(options)) {
91
+ if (Object.keys(context.artifacts).length > 0 && !result.artifacts) {
92
+ result.artifacts = context.artifacts;
93
+ }
94
+ return result;
95
+ }
96
+ const normalizeResult = result;
97
+ const resultArgs = {
98
+ agent,
99
+ input,
100
+ context,
101
+ runtime,
102
+ result: normalizeResult
103
+ };
104
+ const output = options.normalize?.output ? await options.normalize.output(resultArgs) : resolveOutput(normalizeResult);
105
+ const usage = options.normalize?.usage ? await options.normalize.usage(resultArgs) : resolveUsage(normalizeResult, runtime.toolCalls.length);
106
+ const session = options.normalize?.session ? await options.normalize.session(resultArgs) : resolveSession(normalizeResult, messages, output, usage);
107
+ return {
108
+ session,
109
+ output,
110
+ usage,
111
+ timings: options.normalize?.timings ? await options.normalize.timings(resultArgs) : void 0,
112
+ artifacts: Object.keys(context.artifacts).length > 0 ? context.artifacts : void 0,
113
+ errors: options.normalize?.errors ? await options.normalize.errors(resultArgs) : resolveErrors(normalizeResult)
114
+ };
115
+ } catch (error) {
116
+ const usage = resolveUsage(void 0, runtime.toolCalls.length);
117
+ const run = {
118
+ session: resolveSession(void 0, messages, void 0, usage),
119
+ output: void 0,
120
+ usage,
121
+ artifacts: Object.keys(context.artifacts).length > 0 ? context.artifacts : void 0,
122
+ errors: [(0, import_harness.serializeError)(error)]
123
+ };
124
+ throw (0, import_harness.attachHarnessRunToError)(error, run);
125
+ }
126
+ }
127
+ async function resolveAgent(options) {
128
+ if (options.agent !== void 0) {
129
+ return options.agent;
130
+ }
131
+ if (options.createAgent) {
132
+ return options.createAgent();
133
+ }
134
+ throw new Error(
135
+ "piAiHarness requires either an agent instance or a createAgent() function."
136
+ );
137
+ }
138
+ function hasResultOverrides(options) {
139
+ return Boolean(
140
+ options.normalize?.output ?? options.normalize?.session ?? options.normalize?.usage ?? options.normalize?.timings ?? options.normalize?.errors
141
+ );
142
+ }
143
+ function resolveInferredToolSurfaces(agent) {
144
+ let runtimeTools;
145
+ const nativeToolsets = [];
146
+ const seenToolsets = /* @__PURE__ */ new Set();
147
+ for (const candidate of getAgentToolCandidates(agent)) {
148
+ const nextRuntimeTools = getRuntimeToolset(candidate);
149
+ if (runtimeTools === void 0 && nextRuntimeTools !== void 0) {
150
+ runtimeTools = nextRuntimeTools;
151
+ }
152
+ const nativeTools = getNativeToolArray(candidate);
153
+ if (nativeTools && !seenToolsets.has(nativeTools)) {
154
+ seenToolsets.add(nativeTools);
155
+ nativeToolsets.push(nativeTools);
156
+ }
157
+ }
158
+ return {
159
+ ...runtimeTools ? { runtimeTools } : {},
160
+ ...nativeToolsets.length > 0 ? { nativeToolsets } : {}
161
+ };
162
+ }
163
+ function getAgentToolCandidates(agent) {
164
+ const roots = getAgentRoots(agent);
165
+ const candidates = [];
166
+ const seen = /* @__PURE__ */ new Set();
167
+ for (const root of roots) {
168
+ addUniqueObject(candidates, seen, root);
169
+ addUniqueObject(candidates, seen, getObjectProperty(root, "state"));
170
+ addUniqueObject(candidates, seen, getObjectProperty(root, "initialState"));
171
+ }
172
+ return candidates;
173
+ }
174
+ function getAgentRoots(agent) {
175
+ return [asObject(agent)].concat(asObject(getObjectProperty(agent, "agent"))).filter((value) => value !== void 0);
176
+ }
177
+ function addUniqueObject(candidates, seen, value) {
178
+ if (!value || typeof value !== "object" || seen.has(value)) {
179
+ return;
180
+ }
181
+ seen.add(value);
182
+ candidates.push(value);
183
+ }
184
+ function asObject(value) {
185
+ return value && typeof value === "object" ? value : void 0;
186
+ }
187
+ function getObjectProperty(value, key) {
188
+ return value && typeof value === "object" ? value[key] : void 0;
189
+ }
190
+ function getRuntimeToolset(value) {
191
+ const candidate = getObjectProperty(value, "tools") ?? getObjectProperty(value, "toolset");
192
+ return isPiAiToolset(candidate) ? candidate : void 0;
193
+ }
194
+ function getNativeToolArray(value) {
195
+ const candidate = getObjectProperty(value, "tools");
196
+ if (isAgentToolArray(candidate)) {
197
+ return candidate;
198
+ }
199
+ return void 0;
200
+ }
201
+ async function runAgent(options, args) {
202
+ if (options.run) {
203
+ return options.run(args);
204
+ }
205
+ if (hasPiAiRunMethod(args.agent)) {
206
+ return args.agent.run(args.input, args.runtime);
207
+ }
208
+ throw new Error(
209
+ "piAiHarness requires a run() function unless the provided agent exposes run(input, runtime)."
210
+ );
211
+ }
212
+ function hasExplicitToolset(options) {
213
+ return options.tools !== void 0;
214
+ }
215
+ function hasPiAiRunMethod(agent) {
216
+ if (!agent || typeof agent !== "object") {
217
+ return false;
218
+ }
219
+ return "run" in agent && typeof agent.run === "function";
220
+ }
221
+ function isPiAiToolset(value) {
222
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
223
+ return false;
224
+ }
225
+ const tools = Object.values(value);
226
+ return tools.length > 0 && tools.every(
227
+ (tool) => Boolean(
228
+ tool && typeof tool === "object" && "execute" in tool && typeof tool.execute === "function"
229
+ )
230
+ );
231
+ }
232
+ function isAgentToolArray(value) {
233
+ return Array.isArray(value) && value.every(
234
+ (tool) => Boolean(
235
+ tool && typeof tool === "object" && "name" in tool && typeof tool.name === "string" && "execute" in tool && typeof tool.execute === "function"
236
+ )
237
+ );
238
+ }
239
+ async function withInstrumentedAgentTools(agent, toolsets, args, callback) {
240
+ if (!toolsets || toolsets.length === 0) {
241
+ return callback();
242
+ }
243
+ const originalExecutions = /* @__PURE__ */ new Map();
244
+ const originalResets = /* @__PURE__ */ new Map();
245
+ const patchTool = (tool) => {
246
+ if (originalExecutions.has(tool)) {
247
+ return;
248
+ }
249
+ const originalExecute = getNativeToolExecuteOrigin(tool.execute);
250
+ originalExecutions.set(tool, originalExecute);
251
+ const instrumentedExecute = async (toolCallId, rawArgs) => {
252
+ const startedAt = /* @__PURE__ */ new Date();
253
+ const toolContext = {
254
+ input: args.input,
255
+ metadata: args.context.metadata,
256
+ signal: args.context.signal,
257
+ setArtifact: args.context.setArtifact
258
+ };
259
+ try {
260
+ const execution = await executeNativeToolWithReplay({
261
+ toolName: tool.name,
262
+ toolCallId,
263
+ execute: originalExecute,
264
+ replay: tool.replay,
265
+ args: rawArgs,
266
+ context: toolContext
267
+ });
268
+ const finishedAt = /* @__PURE__ */ new Date();
269
+ const call = {
270
+ name: tool.name,
271
+ arguments: rawArgs,
272
+ result: execution.normalizedResult,
273
+ startedAt: startedAt.toISOString(),
274
+ finishedAt: finishedAt.toISOString(),
275
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
276
+ metadata: (0, import_replay.normalizeReplayMetadata)(execution.replay)
277
+ };
278
+ args.toolCalls.push(call);
279
+ args.messages.push({
280
+ role: "assistant",
281
+ toolCalls: [call]
282
+ });
283
+ args.messages.push({
284
+ role: "tool",
285
+ content: execution.normalizedResult,
286
+ metadata: {
287
+ name: tool.name
288
+ }
289
+ });
290
+ return execution.result;
291
+ } catch (error) {
292
+ const finishedAt = /* @__PURE__ */ new Date();
293
+ const call = {
294
+ name: tool.name,
295
+ arguments: rawArgs,
296
+ error: serializeToolCallError(error),
297
+ startedAt: startedAt.toISOString(),
298
+ finishedAt: finishedAt.toISOString(),
299
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
300
+ metadata: (0, import_replay.normalizeReplayMetadata)((0, import_replay.getReplayMetadataFromError)(error))
301
+ };
302
+ args.toolCalls.push(call);
303
+ args.messages.push({
304
+ role: "assistant",
305
+ toolCalls: [call]
306
+ });
307
+ throw error;
308
+ }
309
+ };
310
+ instrumentedExecute[ORIGINAL_NATIVE_EXECUTE] = originalExecute;
311
+ tool.execute = instrumentedExecute;
312
+ };
313
+ const patchToolsets = (nextToolsets) => {
314
+ for (const toolset of nextToolsets) {
315
+ for (const tool of toolset) {
316
+ patchTool(tool);
317
+ }
318
+ }
319
+ };
320
+ patchToolsets(toolsets);
321
+ for (const target of getAgentResetTargets(agent)) {
322
+ const originalReset = target.reset;
323
+ originalResets.set(target, originalReset);
324
+ target.reset = function patchedReset(...resetArgs) {
325
+ const resetResult = originalReset.apply(this, resetArgs);
326
+ if (isPromiseLike(resetResult)) {
327
+ return resetResult.finally(() => {
328
+ patchToolsets(
329
+ resolveInferredNativeToolsets(agent)
330
+ );
331
+ });
332
+ }
333
+ patchToolsets(resolveInferredNativeToolsets(agent));
334
+ return resetResult;
335
+ };
336
+ }
337
+ try {
338
+ return await callback();
339
+ } finally {
340
+ for (const [target, originalReset] of originalResets) {
341
+ target.reset = originalReset;
342
+ }
343
+ for (const [tool, originalExecute] of originalExecutions) {
344
+ tool.execute = originalExecute;
345
+ }
346
+ }
347
+ }
348
+ function getAgentResetTargets(agent) {
349
+ return getAgentRoots(agent).filter(isResettableAgent);
350
+ }
351
+ function isResettableAgent(value) {
352
+ return "reset" in value && typeof value.reset === "function";
353
+ }
354
+ function resolveInferredNativeToolsets(agent) {
355
+ const toolsets = [];
356
+ const seenToolsets = /* @__PURE__ */ new Set();
357
+ for (const candidate of getAgentToolCandidates(agent)) {
358
+ const nativeTools = getNativeToolArray(candidate);
359
+ if (nativeTools && !seenToolsets.has(nativeTools)) {
360
+ seenToolsets.add(nativeTools);
361
+ toolsets.push(nativeTools);
362
+ }
363
+ }
364
+ return toolsets;
365
+ }
366
+ function isPromiseLike(value) {
367
+ return Boolean(
368
+ value && typeof value.then === "function"
369
+ );
370
+ }
371
+ function serializeToolCallError(error) {
372
+ const serialized = (0, import_harness.serializeError)(error);
373
+ const { message, type, ...details } = serialized;
374
+ return {
375
+ ...details,
376
+ message: typeof message === "string" ? message : String(message),
377
+ ...typeof type === "string" ? { type } : {}
378
+ };
379
+ }
380
+ function getNativeToolExecuteOrigin(execute) {
381
+ const nativeExecute = execute;
382
+ return nativeExecute[ORIGINAL_NATIVE_EXECUTE] ?? nativeExecute;
383
+ }
384
+ async function executeNativeToolWithReplay({
385
+ toolName,
386
+ toolCallId,
387
+ execute,
388
+ replay,
389
+ args,
390
+ context
391
+ }) {
392
+ let didExecute = false;
393
+ let liveResult;
394
+ const execution = await (0, import_replay.executeWithReplay)({
395
+ toolName,
396
+ args,
397
+ context,
398
+ execute: async (toolArgs) => {
399
+ didExecute = true;
400
+ liveResult = await execute(toolCallId, toolArgs);
401
+ return createNativeToolReplayEnvelope(liveResult);
402
+ },
403
+ replay
404
+ });
405
+ if (didExecute) {
406
+ return {
407
+ result: liveResult,
408
+ normalizedResult: normalizeReplayToolResult(liveResult),
409
+ replay: execution.replay
410
+ };
411
+ }
412
+ return {
413
+ ...resolveNativeToolReplayResult(execution.result),
414
+ replay: execution.replay
415
+ };
416
+ }
417
+ function createRuntime({
418
+ input,
419
+ context,
420
+ tools,
421
+ messages
422
+ }) {
423
+ const toolCalls = [];
424
+ const eventSink = {
425
+ message: (message) => {
426
+ messages.push(message);
427
+ },
428
+ system: (content, metadata) => {
429
+ messages.push({
430
+ role: "system",
431
+ content,
432
+ metadata
433
+ });
434
+ },
435
+ user: (content, metadata) => {
436
+ messages.push({
437
+ role: "user",
438
+ content,
439
+ metadata
440
+ });
441
+ },
442
+ assistant: (content, metadata) => {
443
+ messages.push({
444
+ role: "assistant",
445
+ content,
446
+ metadata
447
+ });
448
+ },
449
+ tool: (name, content, metadata) => {
450
+ messages.push({
451
+ role: "tool",
452
+ content,
453
+ metadata: {
454
+ name,
455
+ ...metadata ?? {}
456
+ }
457
+ });
458
+ }
459
+ };
460
+ const runtimeTools = Object.fromEntries(
461
+ Object.entries(tools ?? {}).map(([toolName, tool]) => [
462
+ toolName,
463
+ async (args) => {
464
+ const startedAt = /* @__PURE__ */ new Date();
465
+ const toolContext = {
466
+ input,
467
+ metadata: context.metadata,
468
+ signal: context.signal,
469
+ setArtifact: context.setArtifact
470
+ };
471
+ try {
472
+ const execution = await executeToolWithReplay({
473
+ toolName,
474
+ tool,
475
+ args,
476
+ context: toolContext
477
+ });
478
+ const finishedAt = /* @__PURE__ */ new Date();
479
+ const call = {
480
+ name: toolName,
481
+ arguments: args,
482
+ result: execution.result,
483
+ startedAt: startedAt.toISOString(),
484
+ finishedAt: finishedAt.toISOString(),
485
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
486
+ metadata: (0, import_replay.normalizeReplayMetadata)(execution.replay)
487
+ };
488
+ toolCalls.push(call);
489
+ messages.push({
490
+ role: "assistant",
491
+ toolCalls: [call]
492
+ });
493
+ messages.push({
494
+ role: "tool",
495
+ content: execution.result,
496
+ metadata: {
497
+ name: toolName
498
+ }
499
+ });
500
+ return execution.result;
501
+ } catch (error) {
502
+ const finishedAt = /* @__PURE__ */ new Date();
503
+ const call = {
504
+ name: toolName,
505
+ arguments: args,
506
+ error: serializeToolCallError(error),
507
+ startedAt: startedAt.toISOString(),
508
+ finishedAt: finishedAt.toISOString(),
509
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
510
+ metadata: (0, import_replay.normalizeReplayMetadata)(
511
+ (0, import_replay.getReplayMetadataFromError)(error)
512
+ )
513
+ };
514
+ toolCalls.push(call);
515
+ messages.push({
516
+ role: "assistant",
517
+ toolCalls: [call]
518
+ });
519
+ throw error;
520
+ }
521
+ }
522
+ ])
523
+ );
524
+ return {
525
+ tools: runtimeTools,
526
+ events: eventSink,
527
+ signal: context.signal,
528
+ toolCalls
529
+ };
530
+ }
531
+ function resolveOutput(result) {
532
+ if (!result || typeof result !== "object") {
533
+ return (0, import_harness.toJsonValue)(result);
534
+ }
535
+ const candidates = [
536
+ "output",
537
+ "decision",
538
+ "result",
539
+ "final"
540
+ ];
541
+ for (const key of candidates) {
542
+ const value = result[key];
543
+ const normalized = (0, import_harness.toJsonValue)(value);
544
+ if (normalized !== void 0) {
545
+ return normalized;
546
+ }
547
+ }
548
+ return void 0;
549
+ }
550
+ function normalizeToolResult(result) {
551
+ const details = result && typeof result === "object" ? (0, import_harness.toJsonValue)(result.details) : void 0;
552
+ if (details !== void 0) {
553
+ return details;
554
+ }
555
+ return (0, import_harness.toJsonValue)(result) ?? (result === void 0 ? void 0 : String(result));
556
+ }
557
+ function normalizeReplayToolResult(result) {
558
+ return normalizeToolResult(result) ?? null;
559
+ }
560
+ function createNativeToolReplayEnvelope(result) {
561
+ const normalizedResult = normalizeReplayToolResult(result);
562
+ return {
563
+ __vitestEvals: {
564
+ kind: "pi-ai-native-tool-result",
565
+ version: 2
566
+ },
567
+ agentResult: (0, import_harness.toJsonValue)(result) ?? normalizedResult,
568
+ normalizedResult
569
+ };
570
+ }
571
+ function resolveNativeToolReplayResult(result) {
572
+ if (isNativeToolReplayEnvelope(result)) {
573
+ return {
574
+ result: result.agentResult,
575
+ normalizedResult: result.normalizedResult
576
+ };
577
+ }
578
+ if (isLegacyNativeToolReplayEnvelope(result)) {
579
+ return {
580
+ result: result.agentResult ?? result.normalizedResult,
581
+ normalizedResult: result.normalizedResult
582
+ };
583
+ }
584
+ return {
585
+ result,
586
+ normalizedResult: normalizeReplayToolResult(result)
587
+ };
588
+ }
589
+ function isNativeToolReplayEnvelope(value) {
590
+ return Boolean(
591
+ value && typeof value === "object" && "__vitestEvals" in value && isNativeToolReplayMarker(
592
+ value.__vitestEvals
593
+ ) && "agentResult" in value && "normalizedResult" in value
594
+ );
595
+ }
596
+ function isNativeToolReplayMarker(value) {
597
+ return Boolean(
598
+ value && typeof value === "object" && "kind" in value && value.kind === "pi-ai-native-tool-result" && "version" in value && value.version === 2
599
+ );
600
+ }
601
+ function isLegacyNativeToolReplayEnvelope(value) {
602
+ return Boolean(
603
+ value && typeof value === "object" && "__vitestEvals" in value && isLegacyNativeToolReplayMarker(
604
+ value.__vitestEvals
605
+ ) && "normalizedResult" in value
606
+ );
607
+ }
608
+ function isLegacyNativeToolReplayMarker(value) {
609
+ return Boolean(
610
+ value && typeof value === "object" && "kind" in value && value.kind === "pi-ai-native-tool-result" && "version" in value && value.version === 1
611
+ );
612
+ }
613
+ function resolveUsage(result, toolCallCount) {
614
+ if (!result || typeof result !== "object") {
615
+ return toolCallCount > 0 ? { toolCalls: toolCallCount } : {};
616
+ }
617
+ const usageValue = result.usage ?? result.metrics;
618
+ const usage = usageValue && typeof usageValue === "object" ? { ...usageValue } : {};
619
+ if (usage.toolCalls === void 0 && toolCallCount > 0) {
620
+ usage.toolCalls = toolCallCount;
621
+ }
622
+ return usage;
623
+ }
624
+ function resolveSession(result, messages, output, usage) {
625
+ if ((0, import_harness.isNormalizedSession)(
626
+ result?.session
627
+ )) {
628
+ return result.session;
629
+ }
630
+ if ((0, import_harness.isNormalizedSession)(result?.trace)) {
631
+ return result.trace;
632
+ }
633
+ const sessionMessages = [...messages];
634
+ if (output !== void 0 && !sessionMessages.some(
635
+ (message) => message.role === "assistant" && message.content !== void 0
636
+ )) {
637
+ sessionMessages.push({
638
+ role: "assistant",
639
+ content: output
640
+ });
641
+ }
642
+ return {
643
+ messages: sessionMessages,
644
+ outputText: typeof output === "string" ? output : void 0,
645
+ provider: result?.provider ?? usage.provider,
646
+ model: result?.model ?? usage.model
647
+ };
648
+ }
649
+ function resolveErrors(result) {
650
+ return (0, import_harness.resolveHarnessRunErrors)(result);
651
+ }
652
+ async function executeToolWithReplay({
653
+ toolName,
654
+ tool,
655
+ args,
656
+ context
657
+ }) {
658
+ return (0, import_replay.executeWithReplay)({
659
+ toolName,
660
+ args,
661
+ context,
662
+ execute: tool.execute,
663
+ replay: tool.replay
664
+ });
665
+ }
666
+ // Annotate the CommonJS export names for ESM import in node:
667
+ 0 && (module.exports = {
668
+ piAiHarness
669
+ });
670
+ //# sourceMappingURL=index.js.map