@artemiskit/core 0.5.0 → 0.5.2

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 CHANGED
@@ -19824,7 +19824,7 @@ var TestCaseSchema = objectType({
19824
19824
  tags: arrayType(stringType()).optional().default([]),
19825
19825
  metadata: recordType(unknownType()).optional().default({}),
19826
19826
  timeout: numberType().optional(),
19827
- retries: numberType().optional().default(0),
19827
+ retries: numberType().int().min(0).max(99).optional().default(0),
19828
19828
  provider: ProviderSchema.optional(),
19829
19829
  model: stringType().optional(),
19830
19830
  variables: VariablesSchema.optional(),
@@ -20524,16 +20524,48 @@ function mergeRedactionConfig(scenarioConfig, caseConfig, cliConfig) {
20524
20524
  }
20525
20525
  async function executeCase(testCase, context) {
20526
20526
  const { timeout, retries = 0 } = context;
20527
+ if (!Number.isSafeInteger(retries) || retries < 0 || retries > 99) {
20528
+ throw new RangeError("retries must be a whole number between 0 and 99");
20529
+ }
20527
20530
  const caseStartTime = Date.now();
20531
+ const requestedModel = testCase.model || context.requestedModel || context.scenario.model;
20532
+ const retryChainId = `${context.runId ?? "untracked"}:${testCase.id}`;
20533
+ const repetitionIndex = context.repetition?.index ?? 1;
20534
+ const attemptEvidence = [];
20528
20535
  let lastError = null;
20529
20536
  for (let attempt = 0;attempt <= retries; attempt++) {
20537
+ const attemptStartTime = Date.now();
20530
20538
  try {
20531
20539
  const result = await executeCaseAttempt(testCase, context, timeout);
20532
- return { ...result, attempts: attempt + 1 };
20540
+ return withAttemptEvidence(result, attemptEvidence, {
20541
+ retryChainId,
20542
+ repetitionIndex,
20543
+ attemptNumber: attempt + 1,
20544
+ includedInOutcome: true,
20545
+ latencyMs: result.latencyMs
20546
+ });
20533
20547
  } catch (error) {
20534
20548
  lastError = error;
20535
- if (error instanceof ToolLoopError)
20536
- return { ...error.caseResult, attempts: attempt + 1 };
20549
+ if (error instanceof ToolLoopError) {
20550
+ return withAttemptEvidence(error.caseResult, attemptEvidence, {
20551
+ retryChainId,
20552
+ repetitionIndex,
20553
+ attemptNumber: attempt + 1,
20554
+ includedInOutcome: true,
20555
+ latencyMs: error.caseResult.latencyMs,
20556
+ errorCode: "tool_error"
20557
+ });
20558
+ }
20559
+ attemptEvidence.push({
20560
+ attempt_id: `${retryChainId}:${attempt + 1}`,
20561
+ retry_chain_id: retryChainId,
20562
+ repetition_index: repetitionIndex,
20563
+ attempt_number: attempt + 1,
20564
+ status: "error",
20565
+ included_in_outcome: false,
20566
+ latency_ms: Date.now() - attemptStartTime,
20567
+ error_code: error instanceof TimeoutError ? "timeout" : "target_error"
20568
+ });
20537
20569
  if (attempt < retries) {
20538
20570
  await sleep(2 ** attempt * 1000);
20539
20571
  }
@@ -20546,6 +20578,10 @@ async function executeCase(testCase, context) {
20546
20578
  ok: false,
20547
20579
  status: "error",
20548
20580
  attempts: retries + 1,
20581
+ attempt_evidence: attemptEvidence.map((entry, index) => ({
20582
+ ...entry,
20583
+ included_in_outcome: index === attemptEvidence.length - 1
20584
+ })),
20549
20585
  score: 0,
20550
20586
  matcherType: testCase.expected.type,
20551
20587
  reason: `Failed after ${retries + 1} attempts: ${lastError?.message}`,
@@ -20555,11 +20591,34 @@ async function executeCase(testCase, context) {
20555
20591
  response: "",
20556
20592
  expected: testCase.expected,
20557
20593
  tags: testCase.tags,
20558
- error: lastError?.message
20594
+ error: lastError?.message,
20595
+ target: targetEvidence(context.client.provider, requestedModel)
20596
+ };
20597
+ }
20598
+ function withAttemptEvidence(result, priorAttempts, input) {
20599
+ return {
20600
+ ...result,
20601
+ attempts: input.attemptNumber,
20602
+ attempt_evidence: [
20603
+ ...priorAttempts,
20604
+ {
20605
+ attempt_id: `${input.retryChainId}:${input.attemptNumber}`,
20606
+ retry_chain_id: input.retryChainId,
20607
+ repetition_index: input.repetitionIndex,
20608
+ attempt_number: input.attemptNumber,
20609
+ status: getTerminalStatus(result),
20610
+ included_in_outcome: input.includedInOutcome,
20611
+ latency_ms: input.latencyMs,
20612
+ ...input.errorCode ? { error_code: input.errorCode } : {}
20613
+ }
20614
+ ]
20559
20615
  };
20560
20616
  }
20617
+ function getTerminalStatus(result) {
20618
+ return result.status ?? (result.ok ? "passed" : result.error ? "error" : "failed");
20619
+ }
20561
20620
  async function executeCaseAttempt(testCase, context, timeout) {
20562
- const { client, scenario, redaction: cliRedaction, toolExecutor } = context;
20621
+ const { client, scenario, requestedModel, redaction: cliRedaction, toolExecutor } = context;
20563
20622
  const variables = mergeVariables(scenario.variables, testCase.variables);
20564
20623
  let prompt = substituteVariables(testCase.prompt, variables);
20565
20624
  if (scenario.setup?.systemPrompt && typeof prompt === "string") {
@@ -20577,7 +20636,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20577
20636
  const loopPrompt = typeof prompt === "string" ? [{ role: "user", content: prompt }] : prompt;
20578
20637
  const generate = () => client.generate({
20579
20638
  prompt: loopPrompt,
20580
- model: testCase.model || scenario.model,
20639
+ model: testCase.model || requestedModel || scenario.model,
20581
20640
  temperature: scenario.temperature,
20582
20641
  maxTokens: scenario.maxTokens,
20583
20642
  seed: scenario.seed,
@@ -20585,6 +20644,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20585
20644
  });
20586
20645
  const generatePromise = generate();
20587
20646
  let result = timeout ? await Promise.race([generatePromise, createTimeout(timeout)]) : await generatePromise;
20647
+ const observedModels = [result.model];
20588
20648
  const generationMetrics = {
20589
20649
  latencyMs: result.latencyMs,
20590
20650
  tokens: { ...result.tokens }
@@ -20595,7 +20655,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20595
20655
  status: "error",
20596
20656
  steps: 0,
20597
20657
  terminationReason: "tool_error"
20598
- }, generationMetrics, "TOOL_EXECUTOR_REQUIRED");
20658
+ }, generationMetrics, "TOOL_EXECUTOR_REQUIRED", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20599
20659
  }
20600
20660
  const executor = toolExecutor ?? new FixtureToolExecutor({
20601
20661
  tools,
@@ -20614,7 +20674,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20614
20674
  status: "error",
20615
20675
  steps: step,
20616
20676
  terminationReason: "duplicate_call"
20617
- }, generationMetrics, "TOOL_DUPLICATE_CALL");
20677
+ }, generationMetrics, "TOOL_DUPLICATE_CALL", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20618
20678
  }
20619
20679
  seenCalls.add(fingerprint);
20620
20680
  const toolStart = Date.now();
@@ -20641,7 +20701,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20641
20701
  status: "error",
20642
20702
  steps: step + 1,
20643
20703
  terminationReason: execution.error?.code === "TOOL_UNKNOWN" ? "unknown_tool" : execution.error?.code?.startsWith("TOOL_ARGUMENTS") ? "invalid_arguments" : "tool_error"
20644
- }, generationMetrics, execution.error?.code ?? "TOOL_EXECUTION_FAILED");
20704
+ }, generationMetrics, execution.error?.code ?? "TOOL_EXECUTION_FAILED", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20645
20705
  }
20646
20706
  const content = JSON.stringify(execution.result ?? {});
20647
20707
  loopPrompt.push({
@@ -20653,7 +20713,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20653
20713
  }
20654
20714
  const remainingLoopTime = policy.timeoutMs - (Date.now() - loopStartedAt);
20655
20715
  if (remainingLoopTime <= 0) {
20656
- throw createToolLoopError(testCase, toolTrace, { status: "error", steps: step + 1, terminationReason: "timeout" }, generationMetrics, "TOOL_LOOP_TIMEOUT");
20716
+ throw createToolLoopError(testCase, toolTrace, { status: "error", steps: step + 1, terminationReason: "timeout" }, generationMetrics, "TOOL_LOOP_TIMEOUT", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20657
20717
  }
20658
20718
  const requestTimeout = timeout ? Math.min(timeout, remainingLoopTime) : remainingLoopTime;
20659
20719
  try {
@@ -20664,8 +20724,9 @@ async function executeCaseAttempt(testCase, context, timeout) {
20664
20724
  status: "error",
20665
20725
  steps: step + 1,
20666
20726
  terminationReason: timedOut ? "timeout" : "tool_error"
20667
- }, generationMetrics, timedOut ? "TOOL_LOOP_TIMEOUT" : "TOOL_GENERATION_FAILED");
20727
+ }, generationMetrics, timedOut ? "TOOL_LOOP_TIMEOUT" : "TOOL_GENERATION_FAILED", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20668
20728
  }
20729
+ observedModels.push(result.model);
20669
20730
  generationMetrics.latencyMs += result.latencyMs;
20670
20731
  generationMetrics.tokens.prompt += result.tokens.prompt;
20671
20732
  generationMetrics.tokens.completion += result.tokens.completion;
@@ -20676,7 +20737,7 @@ async function executeCaseAttempt(testCase, context, timeout) {
20676
20737
  status: "error",
20677
20738
  steps: policy.maxSteps,
20678
20739
  terminationReason: "max_steps"
20679
- }, generationMetrics, "TOOL_LOOP_MAX_STEPS");
20740
+ }, generationMetrics, "TOOL_LOOP_MAX_STEPS", targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels));
20680
20741
  }
20681
20742
  toolLoop = { status: "completed", steps: toolTrace.length, terminationReason: "completed" };
20682
20743
  }
@@ -20772,11 +20833,22 @@ async function executeCaseAttempt(testCase, context, timeout) {
20772
20833
  tags: testCase.tags,
20773
20834
  redaction: redactionInfo,
20774
20835
  evidence: finalEvidence,
20836
+ target: targetEvidence(client.provider, testCase.model || requestedModel || scenario.model, observedModels),
20775
20837
  toolTrace: toolTrace.length ? toolTrace : undefined,
20776
20838
  toolLoop
20777
20839
  };
20778
20840
  }
20779
- function createToolLoopError(testCase, toolTrace, toolLoop, generationMetrics, code) {
20841
+ function targetEvidence(provider, requestedModel, observedModels) {
20842
+ const observed = [
20843
+ ...new Set((observedModels ?? []).filter((model) => typeof model === "string" && model.length > 0))
20844
+ ].map((model) => sanitizeArtifactText(model, 200)).filter((model) => Boolean(model));
20845
+ return {
20846
+ provider: sanitizeArtifactText(provider, 100) ?? "unknown",
20847
+ ...requestedModel ? { requested_model: sanitizeArtifactText(requestedModel, 200) } : {},
20848
+ ...observed.length ? { observed_models: observed } : {}
20849
+ };
20850
+ }
20851
+ function createToolLoopError(testCase, toolTrace, toolLoop, generationMetrics, code, target) {
20780
20852
  return new ToolLoopError(code, {
20781
20853
  id: testCase.id,
20782
20854
  name: testCase.name,
@@ -20792,6 +20864,7 @@ function createToolLoopError(testCase, toolTrace, toolLoop, generationMetrics, c
20792
20864
  expected: testCase.expected,
20793
20865
  tags: testCase.tags,
20794
20866
  error: code,
20867
+ target,
20795
20868
  toolTrace,
20796
20869
  toolLoop
20797
20870
  });
@@ -20912,374 +20985,61 @@ function nanoid(size = 21) {
20912
20985
  return id;
20913
20986
  }
20914
20987
 
20915
- // src/cost/pricing.ts
20916
- var MODEL_PRICING = {
20917
- "gpt-5": {
20918
- promptPer1K: 0.00125,
20919
- completionPer1K: 0.01,
20920
- lastUpdated: "2026-01",
20921
- notes: "400K context window"
20922
- },
20923
- "gpt-5.1": {
20924
- promptPer1K: 0.00125,
20925
- completionPer1K: 0.01,
20926
- lastUpdated: "2026-01"
20927
- },
20928
- "gpt-5.2": {
20929
- promptPer1K: 0.00175,
20930
- completionPer1K: 0.014,
20931
- lastUpdated: "2026-01"
20932
- },
20933
- "gpt-5-mini": {
20934
- promptPer1K: 0.00025,
20935
- completionPer1K: 0.002,
20936
- lastUpdated: "2026-01"
20937
- },
20938
- "gpt-5-nano": {
20939
- promptPer1K: 0.00005,
20940
- completionPer1K: 0.0004,
20941
- lastUpdated: "2026-01"
20942
- },
20943
- "gpt-4.1": {
20944
- promptPer1K: 0.002,
20945
- completionPer1K: 0.008,
20946
- lastUpdated: "2026-01",
20947
- notes: "1M context window"
20948
- },
20949
- "gpt-4.1-mini": {
20950
- promptPer1K: 0.0004,
20951
- completionPer1K: 0.0016,
20952
- lastUpdated: "2026-01"
20953
- },
20954
- "gpt-4.1-nano": {
20955
- promptPer1K: 0.0001,
20956
- completionPer1K: 0.0004,
20957
- lastUpdated: "2026-01"
20958
- },
20959
- "gpt-4o": {
20960
- promptPer1K: 0.0025,
20961
- completionPer1K: 0.01,
20962
- lastUpdated: "2026-01",
20963
- notes: "128K context window"
20964
- },
20965
- "gpt-4o-mini": {
20966
- promptPer1K: 0.00015,
20967
- completionPer1K: 0.0006,
20968
- lastUpdated: "2026-01",
20969
- notes: "128K context window"
20970
- },
20971
- o1: {
20972
- promptPer1K: 0.015,
20973
- completionPer1K: 0.06,
20974
- lastUpdated: "2026-01",
20975
- notes: "Reasoning model - internal thinking tokens billed as output"
20976
- },
20977
- o3: {
20978
- promptPer1K: 0.002,
20979
- completionPer1K: 0.008,
20980
- lastUpdated: "2026-01"
20981
- },
20982
- "o3-mini": {
20983
- promptPer1K: 0.0011,
20984
- completionPer1K: 0.0044,
20985
- lastUpdated: "2026-01"
20986
- },
20987
- "o4-mini": {
20988
- promptPer1K: 0.0011,
20989
- completionPer1K: 0.0044,
20990
- lastUpdated: "2026-01"
20991
- },
20992
- "gpt-4-turbo": {
20993
- promptPer1K: 0.01,
20994
- completionPer1K: 0.03,
20995
- lastUpdated: "2026-01"
20996
- },
20997
- "gpt-4": {
20998
- promptPer1K: 0.03,
20999
- completionPer1K: 0.06,
21000
- lastUpdated: "2026-01"
21001
- },
21002
- "gpt-3.5-turbo": {
21003
- promptPer1K: 0.0005,
21004
- completionPer1K: 0.0015,
21005
- lastUpdated: "2026-01"
21006
- },
21007
- "claude-opus-4.5": {
21008
- promptPer1K: 0.005,
21009
- completionPer1K: 0.025,
21010
- lastUpdated: "2026-01",
21011
- notes: "Most capable Claude model"
21012
- },
21013
- "claude-sonnet-4.5": {
21014
- promptPer1K: 0.003,
21015
- completionPer1K: 0.015,
21016
- lastUpdated: "2026-01",
21017
- notes: "Balanced performance and cost"
21018
- },
21019
- "claude-haiku-4.5": {
21020
- promptPer1K: 0.001,
21021
- completionPer1K: 0.005,
21022
- lastUpdated: "2026-01",
21023
- notes: "Fastest Claude model"
21024
- },
21025
- "claude-opus-4": {
21026
- promptPer1K: 0.015,
21027
- completionPer1K: 0.075,
21028
- lastUpdated: "2026-01"
21029
- },
21030
- "claude-opus-4.1": {
21031
- promptPer1K: 0.015,
21032
- completionPer1K: 0.075,
21033
- lastUpdated: "2026-01"
21034
- },
21035
- "claude-sonnet-4": {
21036
- promptPer1K: 0.003,
21037
- completionPer1K: 0.015,
21038
- lastUpdated: "2026-01"
21039
- },
21040
- "claude-sonnet-3.7": {
21041
- promptPer1K: 0.003,
21042
- completionPer1K: 0.015,
21043
- lastUpdated: "2026-01"
21044
- },
21045
- "claude-3-7-sonnet": {
21046
- promptPer1K: 0.003,
21047
- completionPer1K: 0.015,
21048
- lastUpdated: "2026-01"
21049
- },
21050
- "claude-3-5-sonnet-20241022": {
21051
- promptPer1K: 0.003,
21052
- completionPer1K: 0.015,
21053
- lastUpdated: "2026-01"
21054
- },
21055
- "claude-3-5-haiku-20241022": {
21056
- promptPer1K: 0.0008,
21057
- completionPer1K: 0.004,
21058
- lastUpdated: "2026-01"
21059
- },
21060
- "claude-haiku-3.5": {
21061
- promptPer1K: 0.0008,
21062
- completionPer1K: 0.004,
21063
- lastUpdated: "2026-01"
21064
- },
21065
- "claude-3-opus": {
21066
- promptPer1K: 0.015,
21067
- completionPer1K: 0.075,
21068
- lastUpdated: "2026-01"
21069
- },
21070
- "claude-3-sonnet": {
21071
- promptPer1K: 0.003,
21072
- completionPer1K: 0.015,
21073
- lastUpdated: "2026-01"
21074
- },
21075
- "claude-3-haiku": {
21076
- promptPer1K: 0.00025,
21077
- completionPer1K: 0.00125,
21078
- lastUpdated: "2026-01"
21079
- },
21080
- "claude-3.5-sonnet": {
21081
- promptPer1K: 0.003,
21082
- completionPer1K: 0.015,
21083
- lastUpdated: "2026-01"
21084
- },
21085
- "claude-3.5-haiku": {
21086
- promptPer1K: 0.0008,
21087
- completionPer1K: 0.004,
21088
- lastUpdated: "2026-01"
21089
- }
21090
- };
21091
- var DEFAULT_PRICING = {
21092
- promptPer1K: 0.003,
21093
- completionPer1K: 0.015,
21094
- lastUpdated: "2026-01",
21095
- notes: "Default pricing - verify with provider"
21096
- };
21097
- function getModelPricing(model) {
21098
- if (MODEL_PRICING[model]) {
21099
- return MODEL_PRICING[model];
20988
+ // src/provenance/environment.ts
20989
+ function getEnvironmentInfo() {
20990
+ return {
20991
+ node_version: process.version,
20992
+ platform: process.platform,
20993
+ arch: process.arch
20994
+ };
20995
+ }
20996
+
20997
+ // src/provenance/git.ts
20998
+ import { execSync } from "child_process";
20999
+ function getGitInfo() {
21000
+ try {
21001
+ const commit = execGit("rev-parse HEAD");
21002
+ const branch = execGit("rev-parse --abbrev-ref HEAD");
21003
+ const dirty = execGit("status --porcelain").length > 0;
21004
+ const remote = execGit("remote get-url origin", true);
21005
+ return {
21006
+ commit,
21007
+ branch,
21008
+ dirty,
21009
+ remote: remote || undefined
21010
+ };
21011
+ } catch {
21012
+ return {
21013
+ commit: "unknown",
21014
+ branch: "unknown",
21015
+ dirty: false
21016
+ };
21100
21017
  }
21101
- const lowerModel = model.toLowerCase();
21102
- for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
21103
- if (key.toLowerCase() === lowerModel) {
21104
- return pricing;
21018
+ }
21019
+ function execGit(command, allowFailure = false) {
21020
+ try {
21021
+ return execSync(`git ${command}`, {
21022
+ encoding: "utf-8",
21023
+ stdio: ["pipe", "pipe", "pipe"]
21024
+ }).trim();
21025
+ } catch {
21026
+ if (allowFailure) {
21027
+ return "";
21105
21028
  }
21029
+ throw new Error(`Git command failed: ${command}`);
21106
21030
  }
21107
- if (lowerModel.includes("gpt-5.2")) {
21108
- return MODEL_PRICING["gpt-5.2"];
21109
- }
21110
- if (lowerModel.includes("gpt-5.1")) {
21111
- return MODEL_PRICING["gpt-5.1"];
21112
- }
21113
- if (lowerModel.includes("gpt-5-mini")) {
21114
- return MODEL_PRICING["gpt-5-mini"];
21115
- }
21116
- if (lowerModel.includes("gpt-5-nano")) {
21117
- return MODEL_PRICING["gpt-5-nano"];
21118
- }
21119
- if (lowerModel.includes("gpt-5")) {
21120
- return MODEL_PRICING["gpt-5"];
21121
- }
21122
- if (lowerModel.includes("gpt-4.1-mini")) {
21123
- return MODEL_PRICING["gpt-4.1-mini"];
21124
- }
21125
- if (lowerModel.includes("gpt-4.1-nano")) {
21126
- return MODEL_PRICING["gpt-4.1-nano"];
21127
- }
21128
- if (lowerModel.includes("gpt-4.1")) {
21129
- return MODEL_PRICING["gpt-4.1"];
21130
- }
21131
- if (lowerModel.includes("gpt-4o-mini")) {
21132
- return MODEL_PRICING["gpt-4o-mini"];
21133
- }
21134
- if (lowerModel.includes("gpt-4o")) {
21135
- return MODEL_PRICING["gpt-4o"];
21136
- }
21137
- if (lowerModel.includes("o4-mini")) {
21138
- return MODEL_PRICING["o4-mini"];
21139
- }
21140
- if (lowerModel.includes("o3-mini")) {
21141
- return MODEL_PRICING["o3-mini"];
21142
- }
21143
- if (lowerModel.includes("o3")) {
21144
- return MODEL_PRICING.o3;
21145
- }
21146
- if (lowerModel.includes("o1")) {
21147
- return MODEL_PRICING.o1;
21148
- }
21149
- if (lowerModel.includes("gpt-4-turbo")) {
21150
- return MODEL_PRICING["gpt-4-turbo"];
21151
- }
21152
- if (lowerModel.includes("gpt-4")) {
21153
- return MODEL_PRICING["gpt-4"];
21154
- }
21155
- if (lowerModel.includes("gpt-3.5")) {
21156
- return MODEL_PRICING["gpt-3.5-turbo"];
21157
- }
21158
- if (lowerModel.includes("opus-4.5") || lowerModel.includes("opus-4-5")) {
21159
- return MODEL_PRICING["claude-opus-4.5"];
21160
- }
21161
- if (lowerModel.includes("sonnet-4.5") || lowerModel.includes("sonnet-4-5")) {
21162
- return MODEL_PRICING["claude-sonnet-4.5"];
21163
- }
21164
- if (lowerModel.includes("haiku-4.5") || lowerModel.includes("haiku-4-5")) {
21165
- return MODEL_PRICING["claude-haiku-4.5"];
21166
- }
21167
- if (lowerModel.includes("opus-4.1") || lowerModel.includes("opus-4-1")) {
21168
- return MODEL_PRICING["claude-opus-4.1"];
21169
- }
21170
- if (lowerModel.includes("opus-4")) {
21171
- return MODEL_PRICING["claude-opus-4"];
21172
- }
21173
- if (lowerModel.includes("sonnet-4")) {
21174
- return MODEL_PRICING["claude-sonnet-4"];
21175
- }
21176
- if (lowerModel.includes("sonnet-3.7") || lowerModel.includes("sonnet-3-7")) {
21177
- return MODEL_PRICING["claude-sonnet-3.7"];
21178
- }
21179
- if (lowerModel.includes("claude-3-5-sonnet") || lowerModel.includes("claude-3.5-sonnet")) {
21180
- return MODEL_PRICING["claude-3.5-sonnet"];
21181
- }
21182
- if (lowerModel.includes("claude-3-5-haiku") || lowerModel.includes("claude-3.5-haiku")) {
21183
- return MODEL_PRICING["claude-3.5-haiku"];
21184
- }
21185
- if (lowerModel.includes("claude-3-opus")) {
21186
- return MODEL_PRICING["claude-3-opus"];
21187
- }
21188
- if (lowerModel.includes("claude-3-sonnet")) {
21189
- return MODEL_PRICING["claude-3-sonnet"];
21190
- }
21191
- if (lowerModel.includes("claude-3-haiku")) {
21192
- return MODEL_PRICING["claude-3-haiku"];
21193
- }
21194
- if (lowerModel.includes("claude")) {
21195
- return MODEL_PRICING["claude-sonnet-4.5"];
21196
- }
21197
- return DEFAULT_PRICING;
21198
- }
21199
- function estimateCost(promptTokens, completionTokens, model) {
21200
- const pricing = getModelPricing(model);
21201
- const promptCostUsd = promptTokens / 1000 * pricing.promptPer1K;
21202
- const completionCostUsd = completionTokens / 1000 * pricing.completionPer1K;
21203
- const totalUsd = promptCostUsd + completionCostUsd;
21204
- return {
21205
- totalUsd,
21206
- promptCostUsd,
21207
- completionCostUsd,
21208
- model,
21209
- pricing
21210
- };
21211
- }
21212
- function formatCost(costUsd) {
21213
- if (costUsd < 0.01) {
21214
- return `$${(costUsd * 100).toFixed(4)} cents`;
21215
- }
21216
- if (costUsd < 1) {
21217
- return `$${costUsd.toFixed(4)}`;
21218
- }
21219
- return `$${costUsd.toFixed(2)}`;
21220
- }
21221
- function listKnownModels() {
21222
- return Object.entries(MODEL_PRICING).map(([model, pricing]) => ({
21223
- model,
21224
- pricing
21225
- }));
21226
- }
21227
-
21228
- // src/provenance/environment.ts
21229
- function getEnvironmentInfo() {
21230
- return {
21231
- node_version: process.version,
21232
- platform: process.platform,
21233
- arch: process.arch
21234
- };
21235
- }
21236
-
21237
- // src/provenance/git.ts
21238
- import { execSync } from "child_process";
21239
- function getGitInfo() {
21240
- try {
21241
- const commit = execGit("rev-parse HEAD");
21242
- const branch = execGit("rev-parse --abbrev-ref HEAD");
21243
- const dirty = execGit("status --porcelain").length > 0;
21244
- const remote = execGit("remote get-url origin", true);
21245
- return {
21246
- commit,
21247
- branch,
21248
- dirty,
21249
- remote: remote || undefined
21250
- };
21251
- } catch {
21252
- return {
21253
- commit: "unknown",
21254
- branch: "unknown",
21255
- dirty: false
21256
- };
21257
- }
21258
- }
21259
- function execGit(command, allowFailure = false) {
21260
- try {
21261
- return execSync(`git ${command}`, {
21262
- encoding: "utf-8",
21263
- stdio: ["pipe", "pipe", "pipe"]
21264
- }).trim();
21265
- } catch {
21266
- if (allowFailure) {
21267
- return "";
21268
- }
21269
- throw new Error(`Git command failed: ${command}`);
21270
- }
21271
- }
21272
-
21273
- // src/artifacts/types.ts
21274
- var CASE_EVALUATION_STATUS_LABELS = {
21275
- passed: "Passed",
21276
- failed: "Failed criteria",
21277
- invalid: "Invalid measurement",
21278
- error: "Execution error"
21279
- };
21280
- function getCaseEvaluationStatus(caseResult) {
21281
- if (caseResult.status === "passed" || caseResult.status === "failed" || caseResult.status === "invalid" || caseResult.status === "error") {
21282
- return caseResult.status;
21031
+ }
21032
+
21033
+ // src/artifacts/types.ts
21034
+ var CASE_EVALUATION_STATUS_LABELS = {
21035
+ passed: "Passed",
21036
+ failed: "Failed criteria",
21037
+ invalid: "Invalid measurement",
21038
+ error: "Execution error"
21039
+ };
21040
+ function getCaseEvaluationStatus(caseResult) {
21041
+ if (caseResult.status === "passed" || caseResult.status === "failed" || caseResult.status === "invalid" || caseResult.status === "error") {
21042
+ return caseResult.status;
21283
21043
  }
21284
21044
  if (caseResult.ok)
21285
21045
  return "passed";
@@ -21295,6 +21055,15 @@ function assertRunManifestIntegrity(manifest) {
21295
21055
  if (manifest.workload_identity !== undefined) {
21296
21056
  assertWorkloadIdentity(manifest.workload_identity);
21297
21057
  }
21058
+ if (manifest.execution_provenance !== undefined) {
21059
+ assertExecutionProvenance(manifest.execution_provenance);
21060
+ }
21061
+ if (manifest.attempt_evidence !== undefined) {
21062
+ assertRunAttemptEvidence(manifest.attempt_evidence);
21063
+ }
21064
+ if (isRecord2(manifest.metrics) && manifest.metrics.cost_provenance !== undefined) {
21065
+ assertCostProvenance(manifest.metrics.cost_provenance);
21066
+ }
21298
21067
  for (const [index, caseResult] of manifest.cases.entries()) {
21299
21068
  if (!isRecord2(caseResult)) {
21300
21069
  throw new Error(`Invalid run manifest: case ${index} is not an object`);
@@ -21305,8 +21074,91 @@ function assertRunManifestIntegrity(manifest) {
21305
21074
  if (caseResult.evidence !== undefined) {
21306
21075
  assertCaseEvaluationEvidence(caseResult.evidence, index);
21307
21076
  }
21077
+ if (caseResult.target !== undefined) {
21078
+ assertCaseTargetEvidence(caseResult.target);
21079
+ }
21080
+ if (caseResult.attempt_evidence !== undefined) {
21081
+ assertCaseAttemptEvidence(caseResult.attempt_evidence, index);
21082
+ }
21083
+ }
21084
+ }
21085
+ function assertRunAttemptEvidence(evidence) {
21086
+ if (!isRecord2(evidence) || evidence.schema_version !== "1" || !isRecord2(evidence.repetition) || !isPositiveSafeInteger(evidence.repetition.index) || !isPositiveSafeInteger(evidence.repetition.total) || evidence.repetition.index > evidence.repetition.total || !isRecord2(evidence.retry_policy) || !isNonnegativeSafeInteger(evidence.retry_policy.default_max_retries) || evidence.retry_policy.backoff !== "exponential" || !isNonnegativeFiniteNumber(evidence.retry_policy.initial_delay_ms) || evidence.timeout !== undefined && (!isRecord2(evidence.timeout) || !isPositiveFiniteNumber(evidence.timeout.default_ms))) {
21087
+ throw new Error("Invalid run manifest: malformed attempt evidence");
21088
+ }
21089
+ }
21090
+ function assertCaseAttemptEvidence(evidence, index) {
21091
+ if (!Array.isArray(evidence) || evidence.length === 0 || evidence.length > 100) {
21092
+ throw new Error(`Invalid run manifest: case ${index} has malformed attempt evidence`);
21093
+ }
21094
+ for (const attempt of evidence) {
21095
+ if (!isRecord2(attempt) || !isBoundedNonemptyString(attempt.attempt_id, 200) || !isBoundedNonemptyString(attempt.retry_chain_id, 200) || !isPositiveSafeInteger(attempt.repetition_index) || !isPositiveSafeInteger(attempt.attempt_number) || !isCaseEvaluationStatus(attempt.status) || typeof attempt.included_in_outcome !== "boolean" || !isNonnegativeFiniteNumber(attempt.latency_ms) || attempt.error_code !== undefined && attempt.error_code !== "timeout" && attempt.error_code !== "target_error" && attempt.error_code !== "tool_error") {
21096
+ throw new Error(`Invalid run manifest: case ${index} has malformed attempt evidence`);
21097
+ }
21098
+ }
21099
+ }
21100
+ function assertCostProvenance(cost) {
21101
+ if (!isRecord2(cost) || cost.schema_version !== "1") {
21102
+ throw new Error("Invalid run manifest: malformed cost provenance");
21103
+ }
21104
+ if (cost.status === "unavailable") {
21105
+ if (cost.amount !== undefined || cost.currency !== undefined || cost.source !== undefined || cost.recorded_at !== undefined || cost.unavailable_reason !== "provider_billing_not_recorded" && cost.unavailable_reason !== "unsupported_provider" && cost.unavailable_reason !== "not_requested") {
21106
+ throw new Error("Invalid run manifest: malformed cost provenance");
21107
+ }
21108
+ return;
21109
+ }
21110
+ if (cost.status !== "known" && cost.status !== "user_supplied" || !isNonnegativeFiniteNumber(cost.amount) || !isBoundedNonemptyString(cost.currency, 3) || cost.status === "known" && cost.source !== "provider_billing" || cost.status === "user_supplied" && cost.source !== "operator_input" || !isIsoTimestamp(cost.recorded_at) || cost.unavailable_reason !== undefined) {
21111
+ throw new Error("Invalid run manifest: malformed cost provenance");
21112
+ }
21113
+ }
21114
+ function isCaseEvaluationStatus(value) {
21115
+ return value === "passed" || value === "failed" || value === "invalid" || value === "error";
21116
+ }
21117
+ function isBoundedNonemptyString(value, maxLength) {
21118
+ return typeof value === "string" && value.length > 0 && value.length <= maxLength;
21119
+ }
21120
+ function isNonnegativeFiniteNumber(value) {
21121
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
21122
+ }
21123
+ function isPositiveFiniteNumber(value) {
21124
+ return isNonnegativeFiniteNumber(value) && value > 0;
21125
+ }
21126
+ function isNonnegativeSafeInteger(value) {
21127
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
21128
+ }
21129
+ function isPositiveSafeInteger(value) {
21130
+ return isNonnegativeSafeInteger(value) && value > 0;
21131
+ }
21132
+ function isIsoTimestamp(value) {
21133
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
21134
+ }
21135
+ function assertCaseTargetEvidence(target) {
21136
+ if (!isRecord2(target) || typeof target.provider !== "string" || target.provider.length === 0 || target.provider.length > 100 || target.requested_model !== undefined && (typeof target.requested_model !== "string" || target.requested_model.length > 200) || !isBoundedStringList(target.observed_models)) {
21137
+ throw new Error("Invalid run manifest: malformed target evidence");
21138
+ }
21139
+ }
21140
+ function assertExecutionProvenance(provenance) {
21141
+ if (!isRecord2(provenance) || provenance.schema_version !== "1" || !isRecord2(provenance.target)) {
21142
+ throw new Error("Invalid run manifest: malformed execution provenance");
21143
+ }
21144
+ const target = provenance.target;
21145
+ if (typeof target.provider !== "string" || target.provider.length === 0 || target.provider.length > 100 || !isBoundedStringList(target.requested_models) || !isBoundedStringList(target.observed_models) || target.generation !== undefined && !isGenerationConfig(target.generation)) {
21146
+ throw new Error("Invalid run manifest: malformed execution provenance");
21147
+ }
21148
+ if (provenance.evaluator !== undefined) {
21149
+ if (!isRecord2(provenance.evaluator) || !isBoundedStringList(provenance.evaluator.models)) {
21150
+ throw new Error("Invalid run manifest: malformed execution provenance");
21151
+ }
21308
21152
  }
21309
21153
  }
21154
+ function isBoundedStringList(value) {
21155
+ return value === undefined || Array.isArray(value) && value.length <= 100 && value.every((item) => typeof item === "string" && item.length > 0 && item.length <= 200);
21156
+ }
21157
+ function isGenerationConfig(value) {
21158
+ if (!isRecord2(value))
21159
+ return false;
21160
+ return [value.temperature, value.max_tokens, value.seed].every((item) => item === undefined || typeof item === "number" && Number.isFinite(item));
21161
+ }
21310
21162
  function assertWorkloadIdentity(identity) {
21311
21163
  if (!isRecord2(identity) || identity.schema_version !== "1" || !isContentIdentity(identity.workload) || !isContentIdentity(identity.rubric)) {
21312
21164
  throw new Error("Invalid run manifest: malformed workload identity");
@@ -21357,6 +21209,10 @@ function createRunManifest(options) {
21357
21209
  config,
21358
21210
  resolvedConfig,
21359
21211
  workloadIdentity,
21212
+ executionProvenance,
21213
+ attemptEvidence,
21214
+ costProvenance,
21215
+ runId,
21360
21216
  cases,
21361
21217
  startTime,
21362
21218
  endTime,
@@ -21364,13 +21220,12 @@ function createRunManifest(options) {
21364
21220
  runReason,
21365
21221
  redaction
21366
21222
  } = options;
21367
- const modelForCost = resolvedConfig?.model || config.model;
21368
- const metrics = calculateMetrics(cases, modelForCost);
21223
+ const metrics = calculateMetrics(cases, costProvenance);
21369
21224
  const git = getGitInfo();
21370
21225
  const environment = getEnvironmentInfo();
21371
21226
  return {
21372
- version: "1.2",
21373
- run_id: nanoid(12),
21227
+ version: "1.4",
21228
+ run_id: runId ?? nanoid(12),
21374
21229
  project,
21375
21230
  start_time: startTime.toISOString(),
21376
21231
  end_time: endTime.toISOString(),
@@ -21378,6 +21233,8 @@ function createRunManifest(options) {
21378
21233
  config,
21379
21234
  resolved_config: resolvedConfig,
21380
21235
  workload_identity: workloadIdentity,
21236
+ execution_provenance: executionProvenance,
21237
+ attempt_evidence: attemptEvidence,
21381
21238
  metrics,
21382
21239
  git,
21383
21240
  provenance: {
@@ -21390,7 +21247,7 @@ function createRunManifest(options) {
21390
21247
  redaction
21391
21248
  };
21392
21249
  }
21393
- function calculateMetrics(cases, model) {
21250
+ function calculateMetrics(cases, costProvenance) {
21394
21251
  const passedCases = cases.filter((c) => getCaseEvaluationStatus(c) === "passed");
21395
21252
  const validCases = cases.filter((c) => {
21396
21253
  const status = getCaseEvaluationStatus(c);
@@ -21402,21 +21259,11 @@ function calculateMetrics(cases, model) {
21402
21259
  const p95Latency = latencies.length > 0 ? latencies[p95Index] : 0;
21403
21260
  const totalPromptTokens = cases.reduce((sum, c) => sum + c.tokens.prompt, 0);
21404
21261
  const totalCompletionTokens = cases.reduce((sum, c) => sum + c.tokens.completion, 0);
21405
- let cost;
21406
- if (model && !model.toLowerCase().includes("ling-") && (totalPromptTokens > 0 || totalCompletionTokens > 0)) {
21407
- const costEstimate = estimateCost(totalPromptTokens, totalCompletionTokens, model);
21408
- const pricing = getModelPricing(model);
21409
- cost = {
21410
- total_usd: costEstimate.totalUsd,
21411
- prompt_cost_usd: costEstimate.promptCostUsd,
21412
- completion_cost_usd: costEstimate.completionCostUsd,
21413
- model: costEstimate.model,
21414
- pricing: {
21415
- prompt_per_1k: pricing.promptPer1K,
21416
- completion_per_1k: pricing.completionPer1K
21417
- }
21418
- };
21419
- }
21262
+ const cost_provenance = costProvenance ?? {
21263
+ schema_version: "1",
21264
+ status: "unavailable",
21265
+ unavailable_reason: "provider_billing_not_recorded"
21266
+ };
21420
21267
  return {
21421
21268
  success_rate: validCases.length > 0 ? passedCases.length / validCases.length : 0,
21422
21269
  total_attempts: cases.reduce((sum, c) => sum + (c.attempts ?? 1), 0),
@@ -21431,7 +21278,7 @@ function calculateMetrics(cases, model) {
21431
21278
  total_tokens: totalPromptTokens + totalCompletionTokens,
21432
21279
  total_prompt_tokens: totalPromptTokens,
21433
21280
  total_completion_tokens: totalCompletionTokens,
21434
- cost
21281
+ cost_provenance
21435
21282
  };
21436
21283
  }
21437
21284
  function detectCIEnvironment() {
@@ -21530,6 +21377,39 @@ function canonicalize(value, patterns, key) {
21530
21377
  }
21531
21378
  return value;
21532
21379
  }
21380
+ // src/provenance/execution-provenance.ts
21381
+ function createExecutionProvenance(options) {
21382
+ const requestedModels = uniqueStrings([
21383
+ options.requestedModel,
21384
+ ...options.cases.map((caseResult) => caseResult.target?.requested_model)
21385
+ ]);
21386
+ const observedModels = uniqueStrings(options.cases.flatMap((caseResult) => caseResult.target?.observed_models ?? []));
21387
+ const evaluatorModels = uniqueStrings(options.cases.map((caseResult) => caseResult.evidence?.model));
21388
+ const generation = omitUndefined({
21389
+ temperature: options.temperature,
21390
+ max_tokens: options.maxTokens,
21391
+ seed: options.seed
21392
+ });
21393
+ return {
21394
+ schema_version: "1",
21395
+ target: {
21396
+ provider: boundedString(options.provider, 100) ?? "unknown",
21397
+ ...requestedModels.length ? { requested_models: requestedModels } : {},
21398
+ ...observedModels.length ? { observed_models: observedModels } : {},
21399
+ ...Object.keys(generation).length ? { generation } : {}
21400
+ },
21401
+ ...evaluatorModels.length ? { evaluator: { models: evaluatorModels } } : {}
21402
+ };
21403
+ }
21404
+ function uniqueStrings(values) {
21405
+ return [...new Set(values.map((value) => boundedString(value, 200)).filter(Boolean))];
21406
+ }
21407
+ function boundedString(value, maxLength) {
21408
+ return typeof value === "string" && value.length > 0 ? value.slice(0, maxLength) : undefined;
21409
+ }
21410
+ function omitUndefined(value) {
21411
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
21412
+ }
21533
21413
  // src/runner/runner.ts
21534
21414
  async function runScenario(options) {
21535
21415
  const {
@@ -21541,6 +21421,8 @@ async function runScenario(options) {
21541
21421
  concurrency = 1,
21542
21422
  timeout,
21543
21423
  retries,
21424
+ repetition = { index: 1, total: 1 },
21425
+ costProvenance,
21544
21426
  redaction,
21545
21427
  toolExecutor,
21546
21428
  onCaseComplete,
@@ -21556,6 +21438,7 @@ async function runScenario(options) {
21556
21438
  }
21557
21439
  onProgress?.(`Running ${cases.length} test cases...`);
21558
21440
  const startTime = new Date;
21441
+ const runId = nanoid(12);
21559
21442
  const results = [];
21560
21443
  if (concurrency === 1) {
21561
21444
  for (let i = 0;i < cases.length; i++) {
@@ -21563,8 +21446,11 @@ async function runScenario(options) {
21563
21446
  const result = await executeCase(testCase, {
21564
21447
  client,
21565
21448
  scenario,
21449
+ requestedModel: resolvedConfig?.model,
21566
21450
  timeout: testCase.timeout || timeout,
21567
21451
  retries: testCase.retries ?? retries,
21452
+ runId,
21453
+ repetition,
21568
21454
  redaction,
21569
21455
  toolExecutor
21570
21456
  });
@@ -21579,8 +21465,11 @@ async function runScenario(options) {
21579
21465
  const result = await executeCase(testCase, {
21580
21466
  client,
21581
21467
  scenario,
21468
+ requestedModel: resolvedConfig?.model,
21582
21469
  timeout: testCase.timeout || timeout,
21583
21470
  retries: testCase.retries ?? retries,
21471
+ runId,
21472
+ repetition,
21584
21473
  redaction,
21585
21474
  toolExecutor
21586
21475
  });
@@ -21623,9 +21512,29 @@ async function runScenario(options) {
21623
21512
  },
21624
21513
  resolvedConfig,
21625
21514
  workloadIdentity: createWorkloadIdentity(scenario),
21515
+ executionProvenance: createExecutionProvenance({
21516
+ provider: client.provider,
21517
+ requestedModel: resolvedConfig?.model || scenario.model,
21518
+ temperature: resolvedConfig?.temperature ?? scenario.temperature,
21519
+ maxTokens: resolvedConfig?.max_tokens ?? scenario.maxTokens,
21520
+ seed: scenario.seed,
21521
+ cases: results
21522
+ }),
21523
+ attemptEvidence: {
21524
+ schema_version: "1",
21525
+ repetition,
21526
+ retry_policy: {
21527
+ default_max_retries: retries ?? 0,
21528
+ backoff: "exponential",
21529
+ initial_delay_ms: 1000
21530
+ },
21531
+ ...timeout ? { timeout: { default_ms: timeout } } : {}
21532
+ },
21533
+ costProvenance,
21626
21534
  cases: results,
21627
21535
  startTime,
21628
21536
  endTime,
21537
+ runId,
21629
21538
  redaction: redactionInfo
21630
21539
  });
21631
21540
  const success = manifest.metrics.failed_cases === 0 && (manifest.metrics.invalid_evaluations ?? 0) === 0;
@@ -32007,6 +31916,318 @@ class Logger {
32007
31916
  }
32008
31917
  }
32009
31918
  var logger = new Logger("artemis");
31919
+ // src/cost/pricing.ts
31920
+ var MODEL_PRICING = {
31921
+ "gpt-5": {
31922
+ promptPer1K: 0.00125,
31923
+ completionPer1K: 0.01,
31924
+ lastUpdated: "2026-01",
31925
+ notes: "400K context window"
31926
+ },
31927
+ "gpt-5.1": {
31928
+ promptPer1K: 0.00125,
31929
+ completionPer1K: 0.01,
31930
+ lastUpdated: "2026-01"
31931
+ },
31932
+ "gpt-5.2": {
31933
+ promptPer1K: 0.00175,
31934
+ completionPer1K: 0.014,
31935
+ lastUpdated: "2026-01"
31936
+ },
31937
+ "gpt-5-mini": {
31938
+ promptPer1K: 0.00025,
31939
+ completionPer1K: 0.002,
31940
+ lastUpdated: "2026-01"
31941
+ },
31942
+ "gpt-5-nano": {
31943
+ promptPer1K: 0.00005,
31944
+ completionPer1K: 0.0004,
31945
+ lastUpdated: "2026-01"
31946
+ },
31947
+ "gpt-4.1": {
31948
+ promptPer1K: 0.002,
31949
+ completionPer1K: 0.008,
31950
+ lastUpdated: "2026-01",
31951
+ notes: "1M context window"
31952
+ },
31953
+ "gpt-4.1-mini": {
31954
+ promptPer1K: 0.0004,
31955
+ completionPer1K: 0.0016,
31956
+ lastUpdated: "2026-01"
31957
+ },
31958
+ "gpt-4.1-nano": {
31959
+ promptPer1K: 0.0001,
31960
+ completionPer1K: 0.0004,
31961
+ lastUpdated: "2026-01"
31962
+ },
31963
+ "gpt-4o": {
31964
+ promptPer1K: 0.0025,
31965
+ completionPer1K: 0.01,
31966
+ lastUpdated: "2026-01",
31967
+ notes: "128K context window"
31968
+ },
31969
+ "gpt-4o-mini": {
31970
+ promptPer1K: 0.00015,
31971
+ completionPer1K: 0.0006,
31972
+ lastUpdated: "2026-01",
31973
+ notes: "128K context window"
31974
+ },
31975
+ o1: {
31976
+ promptPer1K: 0.015,
31977
+ completionPer1K: 0.06,
31978
+ lastUpdated: "2026-01",
31979
+ notes: "Reasoning model - internal thinking tokens billed as output"
31980
+ },
31981
+ o3: {
31982
+ promptPer1K: 0.002,
31983
+ completionPer1K: 0.008,
31984
+ lastUpdated: "2026-01"
31985
+ },
31986
+ "o3-mini": {
31987
+ promptPer1K: 0.0011,
31988
+ completionPer1K: 0.0044,
31989
+ lastUpdated: "2026-01"
31990
+ },
31991
+ "o4-mini": {
31992
+ promptPer1K: 0.0011,
31993
+ completionPer1K: 0.0044,
31994
+ lastUpdated: "2026-01"
31995
+ },
31996
+ "gpt-4-turbo": {
31997
+ promptPer1K: 0.01,
31998
+ completionPer1K: 0.03,
31999
+ lastUpdated: "2026-01"
32000
+ },
32001
+ "gpt-4": {
32002
+ promptPer1K: 0.03,
32003
+ completionPer1K: 0.06,
32004
+ lastUpdated: "2026-01"
32005
+ },
32006
+ "gpt-3.5-turbo": {
32007
+ promptPer1K: 0.0005,
32008
+ completionPer1K: 0.0015,
32009
+ lastUpdated: "2026-01"
32010
+ },
32011
+ "claude-opus-4.5": {
32012
+ promptPer1K: 0.005,
32013
+ completionPer1K: 0.025,
32014
+ lastUpdated: "2026-01",
32015
+ notes: "Most capable Claude model"
32016
+ },
32017
+ "claude-sonnet-4.5": {
32018
+ promptPer1K: 0.003,
32019
+ completionPer1K: 0.015,
32020
+ lastUpdated: "2026-01",
32021
+ notes: "Balanced performance and cost"
32022
+ },
32023
+ "claude-haiku-4.5": {
32024
+ promptPer1K: 0.001,
32025
+ completionPer1K: 0.005,
32026
+ lastUpdated: "2026-01",
32027
+ notes: "Fastest Claude model"
32028
+ },
32029
+ "claude-opus-4": {
32030
+ promptPer1K: 0.015,
32031
+ completionPer1K: 0.075,
32032
+ lastUpdated: "2026-01"
32033
+ },
32034
+ "claude-opus-4.1": {
32035
+ promptPer1K: 0.015,
32036
+ completionPer1K: 0.075,
32037
+ lastUpdated: "2026-01"
32038
+ },
32039
+ "claude-sonnet-4": {
32040
+ promptPer1K: 0.003,
32041
+ completionPer1K: 0.015,
32042
+ lastUpdated: "2026-01"
32043
+ },
32044
+ "claude-sonnet-3.7": {
32045
+ promptPer1K: 0.003,
32046
+ completionPer1K: 0.015,
32047
+ lastUpdated: "2026-01"
32048
+ },
32049
+ "claude-3-7-sonnet": {
32050
+ promptPer1K: 0.003,
32051
+ completionPer1K: 0.015,
32052
+ lastUpdated: "2026-01"
32053
+ },
32054
+ "claude-3-5-sonnet-20241022": {
32055
+ promptPer1K: 0.003,
32056
+ completionPer1K: 0.015,
32057
+ lastUpdated: "2026-01"
32058
+ },
32059
+ "claude-3-5-haiku-20241022": {
32060
+ promptPer1K: 0.0008,
32061
+ completionPer1K: 0.004,
32062
+ lastUpdated: "2026-01"
32063
+ },
32064
+ "claude-haiku-3.5": {
32065
+ promptPer1K: 0.0008,
32066
+ completionPer1K: 0.004,
32067
+ lastUpdated: "2026-01"
32068
+ },
32069
+ "claude-3-opus": {
32070
+ promptPer1K: 0.015,
32071
+ completionPer1K: 0.075,
32072
+ lastUpdated: "2026-01"
32073
+ },
32074
+ "claude-3-sonnet": {
32075
+ promptPer1K: 0.003,
32076
+ completionPer1K: 0.015,
32077
+ lastUpdated: "2026-01"
32078
+ },
32079
+ "claude-3-haiku": {
32080
+ promptPer1K: 0.00025,
32081
+ completionPer1K: 0.00125,
32082
+ lastUpdated: "2026-01"
32083
+ },
32084
+ "claude-3.5-sonnet": {
32085
+ promptPer1K: 0.003,
32086
+ completionPer1K: 0.015,
32087
+ lastUpdated: "2026-01"
32088
+ },
32089
+ "claude-3.5-haiku": {
32090
+ promptPer1K: 0.0008,
32091
+ completionPer1K: 0.004,
32092
+ lastUpdated: "2026-01"
32093
+ }
32094
+ };
32095
+ var DEFAULT_PRICING = {
32096
+ promptPer1K: 0.003,
32097
+ completionPer1K: 0.015,
32098
+ lastUpdated: "2026-01",
32099
+ notes: "Default pricing - verify with provider"
32100
+ };
32101
+ function getModelPricing(model) {
32102
+ if (MODEL_PRICING[model]) {
32103
+ return MODEL_PRICING[model];
32104
+ }
32105
+ const lowerModel = model.toLowerCase();
32106
+ for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
32107
+ if (key.toLowerCase() === lowerModel) {
32108
+ return pricing;
32109
+ }
32110
+ }
32111
+ if (lowerModel.includes("gpt-5.2")) {
32112
+ return MODEL_PRICING["gpt-5.2"];
32113
+ }
32114
+ if (lowerModel.includes("gpt-5.1")) {
32115
+ return MODEL_PRICING["gpt-5.1"];
32116
+ }
32117
+ if (lowerModel.includes("gpt-5-mini")) {
32118
+ return MODEL_PRICING["gpt-5-mini"];
32119
+ }
32120
+ if (lowerModel.includes("gpt-5-nano")) {
32121
+ return MODEL_PRICING["gpt-5-nano"];
32122
+ }
32123
+ if (lowerModel.includes("gpt-5")) {
32124
+ return MODEL_PRICING["gpt-5"];
32125
+ }
32126
+ if (lowerModel.includes("gpt-4.1-mini")) {
32127
+ return MODEL_PRICING["gpt-4.1-mini"];
32128
+ }
32129
+ if (lowerModel.includes("gpt-4.1-nano")) {
32130
+ return MODEL_PRICING["gpt-4.1-nano"];
32131
+ }
32132
+ if (lowerModel.includes("gpt-4.1")) {
32133
+ return MODEL_PRICING["gpt-4.1"];
32134
+ }
32135
+ if (lowerModel.includes("gpt-4o-mini")) {
32136
+ return MODEL_PRICING["gpt-4o-mini"];
32137
+ }
32138
+ if (lowerModel.includes("gpt-4o")) {
32139
+ return MODEL_PRICING["gpt-4o"];
32140
+ }
32141
+ if (lowerModel.includes("o4-mini")) {
32142
+ return MODEL_PRICING["o4-mini"];
32143
+ }
32144
+ if (lowerModel.includes("o3-mini")) {
32145
+ return MODEL_PRICING["o3-mini"];
32146
+ }
32147
+ if (lowerModel.includes("o3")) {
32148
+ return MODEL_PRICING.o3;
32149
+ }
32150
+ if (lowerModel.includes("o1")) {
32151
+ return MODEL_PRICING.o1;
32152
+ }
32153
+ if (lowerModel.includes("gpt-4-turbo")) {
32154
+ return MODEL_PRICING["gpt-4-turbo"];
32155
+ }
32156
+ if (lowerModel.includes("gpt-4")) {
32157
+ return MODEL_PRICING["gpt-4"];
32158
+ }
32159
+ if (lowerModel.includes("gpt-3.5")) {
32160
+ return MODEL_PRICING["gpt-3.5-turbo"];
32161
+ }
32162
+ if (lowerModel.includes("opus-4.5") || lowerModel.includes("opus-4-5")) {
32163
+ return MODEL_PRICING["claude-opus-4.5"];
32164
+ }
32165
+ if (lowerModel.includes("sonnet-4.5") || lowerModel.includes("sonnet-4-5")) {
32166
+ return MODEL_PRICING["claude-sonnet-4.5"];
32167
+ }
32168
+ if (lowerModel.includes("haiku-4.5") || lowerModel.includes("haiku-4-5")) {
32169
+ return MODEL_PRICING["claude-haiku-4.5"];
32170
+ }
32171
+ if (lowerModel.includes("opus-4.1") || lowerModel.includes("opus-4-1")) {
32172
+ return MODEL_PRICING["claude-opus-4.1"];
32173
+ }
32174
+ if (lowerModel.includes("opus-4")) {
32175
+ return MODEL_PRICING["claude-opus-4"];
32176
+ }
32177
+ if (lowerModel.includes("sonnet-4")) {
32178
+ return MODEL_PRICING["claude-sonnet-4"];
32179
+ }
32180
+ if (lowerModel.includes("sonnet-3.7") || lowerModel.includes("sonnet-3-7")) {
32181
+ return MODEL_PRICING["claude-sonnet-3.7"];
32182
+ }
32183
+ if (lowerModel.includes("claude-3-5-sonnet") || lowerModel.includes("claude-3.5-sonnet")) {
32184
+ return MODEL_PRICING["claude-3.5-sonnet"];
32185
+ }
32186
+ if (lowerModel.includes("claude-3-5-haiku") || lowerModel.includes("claude-3.5-haiku")) {
32187
+ return MODEL_PRICING["claude-3.5-haiku"];
32188
+ }
32189
+ if (lowerModel.includes("claude-3-opus")) {
32190
+ return MODEL_PRICING["claude-3-opus"];
32191
+ }
32192
+ if (lowerModel.includes("claude-3-sonnet")) {
32193
+ return MODEL_PRICING["claude-3-sonnet"];
32194
+ }
32195
+ if (lowerModel.includes("claude-3-haiku")) {
32196
+ return MODEL_PRICING["claude-3-haiku"];
32197
+ }
32198
+ if (lowerModel.includes("claude")) {
32199
+ return MODEL_PRICING["claude-sonnet-4.5"];
32200
+ }
32201
+ return DEFAULT_PRICING;
32202
+ }
32203
+ function estimateCost(promptTokens, completionTokens, model) {
32204
+ const pricing = getModelPricing(model);
32205
+ const promptCostUsd = promptTokens / 1000 * pricing.promptPer1K;
32206
+ const completionCostUsd = completionTokens / 1000 * pricing.completionPer1K;
32207
+ const totalUsd = promptCostUsd + completionCostUsd;
32208
+ return {
32209
+ totalUsd,
32210
+ promptCostUsd,
32211
+ completionCostUsd,
32212
+ model,
32213
+ pricing
32214
+ };
32215
+ }
32216
+ function formatCost(costUsd) {
32217
+ if (costUsd < 0.01) {
32218
+ return `$${(costUsd * 100).toFixed(4)} cents`;
32219
+ }
32220
+ if (costUsd < 1) {
32221
+ return `$${costUsd.toFixed(4)}`;
32222
+ }
32223
+ return `$${costUsd.toFixed(2)}`;
32224
+ }
32225
+ function listKnownModels() {
32226
+ return Object.entries(MODEL_PRICING).map(([model, pricing]) => ({
32227
+ model,
32228
+ pricing
32229
+ }));
32230
+ }
32010
32231
  // src/agent-evaluation/types.ts
32011
32232
  function actionBudgetExceeded(task, trace) {
32012
32233
  return trace.actions.length > task.maxActions;
@@ -32705,6 +32926,7 @@ export {
32705
32926
  assertRunManifestIntegrity,
32706
32927
  createAdapter,
32707
32928
  createDefaultRedactor,
32929
+ createExecutionProvenance,
32708
32930
  createNoOpRedactor,
32709
32931
  createRedactionOptions,
32710
32932
  createRunManifest,