@apo-ai/sdk 0.3.0 → 0.5.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.
@@ -235,7 +235,8 @@ function levenshtein(a, b) {
235
235
  }
236
236
 
237
237
  // src/agent-task/checks/judge.ts
238
- var JUDGE_SYSTEM_PROMPT = 'You are an evaluation judge. Evaluate the given value(s) against the instruction. Respond with ONLY a JSON object: {"pass": true/false, "reasoning": "your reasoning"}';
238
+ var JUDGE_RESPONSE_CONTRACT = 'Respond with ONLY a JSON object: {"pass": true/false, "reasoning": "your reasoning"}';
239
+ var JUDGE_SYSTEM_PROMPT = `You are an evaluation judge. Evaluate the given value(s) against the instruction. ${JUDGE_RESPONSE_CONTRACT}`;
239
240
  function formatValue(value, depth = 0) {
240
241
  const indent = " ".repeat(depth);
241
242
  if (value === null || value === void 0) return String(value);
@@ -314,12 +315,11 @@ async function callJudge(args) {
314
315
  const apiKey = args.apiKey ?? process.env.OPENROUTER_API_KEY ?? process.env.OPENAI_API_KEY;
315
316
  const deliverableText = `Values to evaluate:
316
317
  ${formatJudgeValues(args.values)}`;
317
- const instructionText = `Instruction:
318
- ${args.instruction}`;
319
- const systemPromptText = `${JUDGE_SYSTEM_PROMPT}
318
+ const { briefingText, instructionText } = assembleBriefing(args);
319
+ const systemPromptText = `${briefingText}
320
320
 
321
321
  ${deliverableText}`;
322
- const cacheKey = `${args.model}\0${deliverableText}`;
322
+ const cacheKey = `${args.model}\0${briefingText}\0${deliverableText}`;
323
323
  return runWithSharedPrefix(cacheKey, async () => {
324
324
  const startedAt = Date.now();
325
325
  const response = await fetch(`${baseURL}/chat/completions`, {
@@ -334,7 +334,7 @@ ${deliverableText}`;
334
334
  {
335
335
  role: "system",
336
336
  content: [
337
- { type: "text", text: JUDGE_SYSTEM_PROMPT },
337
+ { type: "text", text: briefingText },
338
338
  {
339
339
  type: "text",
340
340
  text: deliverableText,
@@ -384,6 +384,33 @@ ${deliverableText}`;
384
384
  };
385
385
  });
386
386
  }
387
+ function assembleBriefing(args) {
388
+ if (!args.prompt) {
389
+ return {
390
+ briefingText: JUDGE_SYSTEM_PROMPT,
391
+ instructionText: `Instruction:
392
+ ${args.instruction}`
393
+ };
394
+ }
395
+ const ctx = {
396
+ taskId: args.context?.taskId ?? "",
397
+ checkName: args.context?.checkName ?? "",
398
+ ...args.context?.taskDescription !== void 0 ? { taskDescription: args.context.taskDescription } : {},
399
+ ...args.context?.deliverableNames !== void 0 && args.context.deliverableNames.length > 0 ? { deliverableNames: args.context.deliverableNames } : {},
400
+ instruction: args.instruction
401
+ };
402
+ const built = args.prompt(ctx);
403
+ const system = built.system?.trim();
404
+ const user = built.user?.trim();
405
+ return {
406
+ // The SDK appends its own response contract to any custom briefing.
407
+ briefingText: system ? `${system}
408
+
409
+ ${JUDGE_RESPONSE_CONTRACT}` : JUDGE_SYSTEM_PROMPT,
410
+ instructionText: user ?? `Instruction:
411
+ ${args.instruction}`
412
+ };
413
+ }
387
414
 
388
415
  // src/agent-task/checks/t.ts
389
416
  function resolveJudgeConfig(judgeConfig, override) {
@@ -392,7 +419,8 @@ function resolveJudgeConfig(judgeConfig, override) {
392
419
  return {
393
420
  model,
394
421
  baseURL: override?.baseURL ?? judgeConfig?.baseURL,
395
- apiKey: override?.apiKey ?? judgeConfig?.apiKey
422
+ apiKey: override?.apiKey ?? judgeConfig?.apiKey,
423
+ prompt: override?.prompt ?? judgeConfig?.prompt
396
424
  };
397
425
  }
398
426
  var TEST_METHOD_NAMES = [
@@ -421,6 +449,67 @@ function matchName(name, m) {
421
449
  if (m instanceof RegExp) return m.test(name);
422
450
  return m(name);
423
451
  }
452
+ function createJudgeMethod(rec, judgeConfig, judgeScope) {
453
+ return async (values, instruction, opts) => {
454
+ const label = opts?.label ?? "judge";
455
+ const location = rec.captureLocation();
456
+ const valueArray = Array.isArray(values) ? values : [values];
457
+ const effective = resolveJudgeConfig(judgeConfig, opts?.judge);
458
+ if (!effective) {
459
+ rec.record(
460
+ label,
461
+ false,
462
+ "No judge model configured. Set one of:\n\u2022 OPENROUTER_MODEL + OPENROUTER_API_KEY (OpenRouter \u2014 works with 200+ models, one account)\n\u2022 OPENAI_MODEL + OPENAI_API_KEY (OpenAI direct)\nOr pass { judge } to runTask() programmatically.",
463
+ { evaluator_type: "llm", location }
464
+ );
465
+ return;
466
+ }
467
+ const context2 = judgeScopeToContext(judgeScope);
468
+ try {
469
+ const { pass, reasoning, judge } = await callJudge({
470
+ values: valueArray,
471
+ instruction,
472
+ model: effective.model,
473
+ baseURL: effective.baseURL,
474
+ apiKey: effective.apiKey,
475
+ prompt: effective.prompt,
476
+ ...context2 ? { context: context2 } : {}
477
+ });
478
+ rec.record(label, pass, reasoning, {
479
+ evaluator_type: "llm",
480
+ judge,
481
+ expected: instruction,
482
+ // Store the raw evaluated value (not a truncated string) so the
483
+ // dashboard can render it with the right viewer — Markdown for
484
+ // prose, a JSON tree for structured objects.
485
+ received: valueArray.length === 1 ? valueArray[0] : valueArray,
486
+ location
487
+ });
488
+ } catch (error) {
489
+ rec.record(
490
+ label,
491
+ false,
492
+ `judge failed: ${error instanceof Error ? error.message : String(error)}`,
493
+ {
494
+ evaluator_type: "llm",
495
+ expected: instruction,
496
+ received: valueArray.length === 1 ? valueArray[0] : valueArray,
497
+ location
498
+ }
499
+ );
500
+ }
501
+ };
502
+ }
503
+ function judgeScopeToContext(scope) {
504
+ if (!scope) return void 0;
505
+ const deliverableNames = scope.readDeliverableNames?.();
506
+ return {
507
+ taskId: scope.taskId,
508
+ checkName: scope.checkName,
509
+ ...scope.taskDescription !== void 0 ? { taskDescription: scope.taskDescription } : {},
510
+ ...deliverableNames && deliverableNames.length > 0 ? { deliverableNames } : {}
511
+ };
512
+ }
424
513
  var CAPABILITY_LABELS = {
425
514
  messages: "message",
426
515
  tools: "tool",
@@ -429,7 +518,7 @@ var CAPABILITY_LABELS = {
429
518
  skills: "skill",
430
519
  subagents: "subagent"
431
520
  };
432
- function createTraceTestContext(view, rec, judgeConfig) {
521
+ function createTraceTestContext(view, rec, judgeConfig, judgeScope) {
433
522
  const unsupported = (id, capability) => {
434
523
  const label = CAPABILITY_LABELS[capability];
435
524
  rec.record(id, false, `${label} evidence is unavailable in this trace projection`, {
@@ -613,49 +702,9 @@ function createTraceTestContext(view, rec, judgeConfig) {
613
702
  { expected: matcher.label, received: describeValue(value) }
614
703
  );
615
704
  },
616
- async judge(values, instruction, opts) {
617
- const label = opts?.label ?? "judge";
618
- const location = rec.captureLocation();
619
- const valueArray = Array.isArray(values) ? values : [values];
620
- const effective = resolveJudgeConfig(judgeConfig, opts?.judge);
621
- if (!effective) {
622
- rec.record(
623
- label,
624
- false,
625
- "No judge model configured. Set one of:\n\u2022 OPENROUTER_MODEL + OPENROUTER_API_KEY (OpenRouter \u2014 works with 200+ models, one account)\n\u2022 OPENAI_MODEL + OPENAI_API_KEY (OpenAI direct)\nOr pass { judge } to runTask() programmatically.",
626
- { evaluator_type: "llm", location }
627
- );
628
- return;
629
- }
630
- try {
631
- const { pass, reasoning, judge } = await callJudge({
632
- values: valueArray,
633
- instruction,
634
- model: effective.model,
635
- baseURL: effective.baseURL,
636
- apiKey: effective.apiKey
637
- });
638
- rec.record(label, pass, reasoning, {
639
- evaluator_type: "llm",
640
- judge,
641
- expected: instruction,
642
- received: valueArray.length === 1 ? valueArray[0] : valueArray,
643
- location
644
- });
645
- } catch (error) {
646
- rec.record(
647
- label,
648
- false,
649
- `judge failed: ${error instanceof Error ? error.message : String(error)}`,
650
- {
651
- evaluator_type: "llm",
652
- expected: instruction,
653
- received: valueArray.length === 1 ? valueArray[0] : valueArray,
654
- location
655
- }
656
- );
657
- }
658
- }
705
+ // judge does not consult trace capabilities; the scope carries the
706
+ // task/check frame for prompt builders (#161).
707
+ judge: createJudgeMethod(rec, judgeConfig, judgeScope)
659
708
  };
660
709
  }
661
710
  function matchesTraceToolCall(call, name, opts) {
@@ -787,14 +836,21 @@ function describe(id, nameOrFn, fn) {
787
836
  async function runTraceChecks(args) {
788
837
  const view = new TraceView(args.snapshot);
789
838
  const locate = args.moduleUrl && args.displayFile ? (stack) => parseCheckLocation(stack, args.moduleUrl, args.displayFile) : void 0;
839
+ const taskMeta = readTaskMeta(args.task);
790
840
  const results = await Promise.all(
791
841
  registry.map(async (check) => {
792
842
  const rec = new Recorder(locate);
793
- const t = createTraceTestContext(view, rec, args.judgeConfig);
843
+ const reads = trackDeliverableReads(args.deliverables);
844
+ const t = createTraceTestContext(view, rec, args.judgeConfig, {
845
+ taskId: taskMeta.id ?? "",
846
+ ...taskMeta.description !== void 0 ? { taskDescription: taskMeta.description } : {},
847
+ checkName: check.id,
848
+ readDeliverableNames: reads.names
849
+ });
794
850
  let thrownLocation;
795
851
  try {
796
852
  await check.fn(t, {
797
- deliverables: args.deliverables,
853
+ deliverables: reads.proxied,
798
854
  files: args.files,
799
855
  task: args.task
800
856
  });
@@ -866,6 +922,26 @@ function proxyBrokenDeliverables(deliverables, brokenDeliverables) {
866
922
  }
867
923
  });
868
924
  }
925
+ function readTaskMeta(task2) {
926
+ if (!task2 || typeof task2 !== "object") return {};
927
+ const record = task2;
928
+ return {
929
+ ...typeof record.id === "string" ? { id: record.id } : {},
930
+ ...typeof record.description === "string" ? { description: record.description } : {}
931
+ };
932
+ }
933
+ function trackDeliverableReads(deliverables) {
934
+ const read = /* @__PURE__ */ new Set();
935
+ const proxied = new Proxy(deliverables, {
936
+ get(target, property, receiver) {
937
+ if (typeof property === "string" && Object.prototype.hasOwnProperty.call(target, property)) {
938
+ read.add(property);
939
+ }
940
+ return Reflect.get(target, property, receiver);
941
+ }
942
+ });
943
+ return { proxied, names: () => [...read] };
944
+ }
869
945
 
870
946
  // src/agent-task/task/defineTask.ts
871
947
  var TASK_ADAPTER_SYMBOL = /* @__PURE__ */ Symbol.for("agent-task.adapter-definition");
@@ -1553,6 +1629,8 @@ function observationTypeFor(t) {
1553
1629
  return "EMBEDDING";
1554
1630
  case "GUARDRAIL":
1555
1631
  return "GUARDRAIL";
1632
+ case "SKILL":
1633
+ return "SKILL";
1556
1634
  default:
1557
1635
  return "SPAN";
1558
1636
  }
@@ -2076,6 +2154,7 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
2076
2154
  summarize: (result2) => summarizeEvaluationResults(result2)
2077
2155
  },
2078
2156
  async () => {
2157
+ const judgeConfig = resolveJudgeConfig(options?.judge, task2.judge);
2079
2158
  if (!inlineChecks) {
2080
2159
  return loadAndRunFlowChecks(
2081
2160
  checksPath,
@@ -2084,7 +2163,7 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
2084
2163
  deliverables: collected,
2085
2164
  files,
2086
2165
  task: task2,
2087
- judgeConfig: options?.judge
2166
+ ...judgeConfig ? { judgeConfig } : {}
2088
2167
  },
2089
2168
  validationResults.brokenDeliverables
2090
2169
  );
@@ -2097,7 +2176,7 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
2097
2176
  ),
2098
2177
  files,
2099
2178
  task: task2,
2100
- judgeConfig: options?.judge,
2179
+ ...judgeConfig ? { judgeConfig } : {},
2101
2180
  moduleUrl,
2102
2181
  displayFile: evalFileName
2103
2182
  });
@@ -2277,12 +2356,13 @@ async function evaluate(loaded, options, phase1) {
2277
2356
  phase1.collected,
2278
2357
  validationResults.brokenDeliverables
2279
2358
  );
2359
+ const judgeConfig = resolveJudgeConfig(options?.judge, task2.judge);
2280
2360
  const checksResults = await (inlineChecks ? runTraceChecks({
2281
2361
  snapshot: phase1.snapshot,
2282
2362
  deliverables,
2283
2363
  files,
2284
2364
  task: task2,
2285
- judgeConfig: options?.judge,
2365
+ ...judgeConfig ? { judgeConfig } : {},
2286
2366
  moduleUrl,
2287
2367
  displayFile: evalFileName
2288
2368
  }) : loadAndRunFlowChecks(
@@ -2292,7 +2372,7 @@ async function evaluate(loaded, options, phase1) {
2292
2372
  deliverables,
2293
2373
  files,
2294
2374
  task: task2,
2295
- judgeConfig: options?.judge
2375
+ ...judgeConfig ? { judgeConfig } : {}
2296
2376
  },
2297
2377
  validationResults.brokenDeliverables
2298
2378
  ));
@@ -2449,9 +2529,10 @@ async function rejudgeTaskRun(runId, endpoints, options = {}) {
2449
2529
  const proxied = proxyBrokenDeliverables(deliverables, validation.brokenDeliverables);
2450
2530
  const passCounts = /* @__PURE__ */ new Map();
2451
2531
  let primary = [];
2532
+ const judgeConfig = resolveJudgeConfig(options.judge, loaded.task.judge);
2452
2533
  for (let sample = 0; sample < samples; sample++) {
2453
2534
  if (samples > 1) progress(`judging sample ${sample + 1}/${samples}`);
2454
- const results = await runChecksOnce(loaded, proxied, snapshot, options.judge);
2535
+ const results = await runChecksOnce(loaded, proxied, snapshot, judgeConfig);
2455
2536
  if (sample === 0) primary = results;
2456
2537
  for (const result of results) {
2457
2538
  passCounts.set(result.id, (passCounts.get(result.id) ?? 0) + (result.pass ? 1 : 0));
@@ -2468,9 +2549,9 @@ async function rejudgeTaskRun(runId, endpoints, options = {}) {
2468
2549
  taskId: detail.task_id,
2469
2550
  definitionRevisionId: revisionId,
2470
2551
  definitionRevisionIsPinned: revisionIsPinned,
2471
- judge: options.judge ? {
2472
- model: options.judge.model,
2473
- baseURL: options.judge.baseURL ?? process.env.OPENROUTER_BASE_URL
2552
+ judge: judgeConfig ? {
2553
+ model: judgeConfig.model,
2554
+ baseURL: judgeConfig.baseURL ?? process.env.OPENROUTER_BASE_URL
2474
2555
  } : null,
2475
2556
  samples,
2476
2557
  checks: aggregate.checks,