@apo-ai/sdk 0.16.0 → 0.17.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.
@@ -9,20 +9,70 @@ import {
9
9
  resolveApoAuthHeaders,
10
10
  withApoRun,
11
11
  withApoRunSync
12
- } from "../chunk-P24ECOI3.js";
12
+ } from "../chunk-CSOY6OSP.js";
13
13
  import {
14
14
  createApoTracer
15
15
  } from "../chunk-OZWOLCJ5.js";
16
16
  import "../chunk-HRYXLRXJ.js";
17
17
  import {
18
18
  createApoOpenAI
19
- } from "../chunk-NSMKSPBU.js";
19
+ } from "../chunk-Z7DVZ34X.js";
20
20
  import {
21
21
  createApoAnthropic
22
- } from "../chunk-4BQSCD6T.js";
22
+ } from "../chunk-6U6V7VKZ.js";
23
23
  import {
24
24
  servingHostFromBaseURL
25
- } from "../chunk-JN7RHBTS.js";
25
+ } from "../chunk-KWIYQSWK.js";
26
+
27
+ // src/agent-task/trace-projection/retrieval.ts
28
+ function parseRetrievedDocuments(output) {
29
+ const array = documentsArray(output);
30
+ if (array === void 0) return void 0;
31
+ const documents = [];
32
+ for (const item of array) {
33
+ const document = parseDocument(item);
34
+ if (document !== void 0) documents.push(document);
35
+ }
36
+ return documents;
37
+ }
38
+ function documentsFromObservation(obs) {
39
+ return parseRetrievedDocuments(obs.toolResult) ?? parseRetrievedDocuments(obs.output);
40
+ }
41
+ function retrievalQueryFrom(obs) {
42
+ return queryIn(obs.toolParameters) ?? queryIn(obs.input);
43
+ }
44
+ function queryIn(value) {
45
+ if (value != null && typeof value === "object") {
46
+ const query = value.query;
47
+ if (typeof query === "string") return query;
48
+ }
49
+ return void 0;
50
+ }
51
+ function documentsArray(output) {
52
+ if (Array.isArray(output)) return output;
53
+ if (output != null && typeof output === "object") {
54
+ const wrapped = output.documents;
55
+ if (Array.isArray(wrapped)) return wrapped;
56
+ }
57
+ return void 0;
58
+ }
59
+ function parseDocument(item) {
60
+ if (typeof item === "string") {
61
+ return item.length > 0 ? { content: item } : void 0;
62
+ }
63
+ if (item == null || typeof item !== "object") return void 0;
64
+ const record = item;
65
+ const rawContent = record.content ?? record.text;
66
+ const content = typeof rawContent === "string" ? rawContent : "";
67
+ const id = typeof record.id === "string" && record.id.length > 0 ? record.id : void 0;
68
+ if (content.length === 0 && id === void 0) return void 0;
69
+ const document = { content };
70
+ if (id !== void 0) document.id = id;
71
+ if (typeof record.score === "number" && Number.isFinite(record.score)) {
72
+ document.score = record.score;
73
+ }
74
+ return document;
75
+ }
26
76
 
27
77
  // src/agent-task/run/steer-scheduler.ts
28
78
  var TASK_STEER_SPAN_NAME = "task.steer";
@@ -277,6 +327,18 @@ var TraceView = class _TraceView {
277
327
  get toolNamesInOrder() {
278
328
  return this.toolCalls.map((c) => c.name);
279
329
  }
330
+ /** Retrievals derived from `RETRIEVER` observations, in invocation order. */
331
+ get retrievals() {
332
+ return this.sortedObservations.filter((o) => o.type === "RETRIEVER").map((o) => {
333
+ const retrieval = { spanId: o.spanId, status: o.status };
334
+ const query = retrievalQueryFrom(o);
335
+ if (query !== void 0) retrieval.query = query;
336
+ const documents = documentsFromObservation(o);
337
+ if (documents !== void 0) retrieval.documents = documents;
338
+ if (o.startedAt !== void 0) retrieval.startedAt = o.startedAt;
339
+ return retrieval;
340
+ });
341
+ }
280
342
  /** Skill loads derived from `SKILL` observations, in invocation order. */
281
343
  get skillLoads() {
282
344
  return this.sortedObservations.filter((o) => o.type === "SKILL").map((o) => ({ spanId: o.spanId, skill: o.name, startedAt: o.startedAt }));
@@ -2073,9 +2135,6 @@ function evaluateComputeExpression(expression) {
2073
2135
  const parser = new Parser(tokens);
2074
2136
  const value = parser.parseExpression(0);
2075
2137
  parser.expectEnd();
2076
- if (!Number.isFinite(value)) {
2077
- return { error: "result is not finite" };
2078
- }
2079
2138
  return { value, text: Number(value.toPrecision(12)).toString() };
2080
2139
  } catch (error) {
2081
2140
  return { error: error instanceof ComputeError ? error.message : String(error) };
@@ -2863,7 +2922,7 @@ function createAgentMethod(rec, judgeConfig, judgeScope, evidence, judgeTracer,
2863
2922
  rec.record(
2864
2923
  label,
2865
2924
  false,
2866
- "No judge model configured for t.agent. Set one of:\n\u2022 OPENROUTER_MODEL + OPENROUTER_API_KEY (OpenRouter \u2014 needs a tool-calling-capable model)\n\u2022 OPENAI_MODEL + OPENAI_API_KEY (OpenAI direct)\n\u2022 ANTHROPIC_MODEL + ANTHROPIC_API_KEY (Anthropic direct \u2014 needs a tool-calling-capable model; or ANTHROPIC_AUTH_TOKEN for plan credits)\nOr pass { judge } to runTask() programmatically.",
2925
+ "No judge model configured for t.agent. Set one of:\n\u2022 AGENT_TASK_OPENROUTER_MODEL or OPENROUTER_MODEL + OPENROUTER_API_KEY (OpenRouter, needs a tool-calling-capable model)\n\u2022 OPENAI_MODEL + OPENAI_API_KEY (OpenAI direct)\n\u2022 ANTHROPIC_MODEL + ANTHROPIC_API_KEY (Anthropic direct, needs a tool-calling-capable model, or ANTHROPIC_AUTH_TOKEN for plan credits)\nOr pass { judge } to runTask() programmatically.",
2867
2926
  { evaluator_type: "agent", location, outcome: "error" }
2868
2927
  );
2869
2928
  return;
@@ -2964,6 +3023,246 @@ function createAgentMethod(rec, judgeConfig, judgeScope, evidence, judgeTracer,
2964
3023
  };
2965
3024
  }
2966
3025
 
3026
+ // src/agent-task/checks/grounded.ts
3027
+ var GROUNDEDNESS_RUBRIC = [
3028
+ "Value 1 is an answer. Value 2 is the set of source documents the answer was supposed to rely on.",
3029
+ "Decide whether the answer is grounded in the sources. Check every factual claim in the answer. A claim is supported only when a source states it, or it follows directly from what a source states. Greetings, hedges, and statements that the answer cannot help are not factual claims.",
3030
+ "PASS when every factual claim is supported. FAIL when any factual claim is unsupported or contradicted by the sources, even if it is true in the real world. Groundedness measures the answer against these sources, not against your own knowledge. In your reasoning, name each unsupported claim."
3031
+ ].join("\n\n");
3032
+ function formatTraceSources(retrievals) {
3033
+ const seen = /* @__PURE__ */ new Set();
3034
+ const documents = [];
3035
+ for (const retrieval of retrievals) {
3036
+ for (const document of retrieval.documents ?? []) {
3037
+ const key = document.id ?? document.content;
3038
+ if (seen.has(key)) continue;
3039
+ seen.add(key);
3040
+ documents.push(document);
3041
+ }
3042
+ }
3043
+ return formatSources(documents);
3044
+ }
3045
+ function formatAuthorSources(sources) {
3046
+ return formatSources(sources.map((content) => ({ content })));
3047
+ }
3048
+ function formatSources(documents) {
3049
+ if (documents.length === 0) return "(no documents were retrieved)";
3050
+ return documents.map((document, i) => {
3051
+ const heading = document.id === void 0 ? `[${i + 1}]` : `[${i + 1}] ${document.id}`;
3052
+ return `${heading}
3053
+ ${document.content}`;
3054
+ }).join("\n\n");
3055
+ }
3056
+
3057
+ // src/agent-task/checks/embedding.ts
3058
+ function isEmbeddingHttpConfig(provider) {
3059
+ return typeof provider.model === "string";
3060
+ }
3061
+ function resolveEmbeddingConfig(provider, override) {
3062
+ const base = resolveEmbeddingProvider(provider);
3063
+ if (!override) return base;
3064
+ if (base?.custom && !override.model) return base;
3065
+ const baseConfig = base?.config;
3066
+ if (!override.model && !baseConfig) return void 0;
3067
+ return {
3068
+ config: {
3069
+ model: override.model ?? baseConfig.model,
3070
+ baseURL: override.baseURL ?? baseConfig?.baseURL,
3071
+ apiKey: override.apiKey ?? baseConfig?.apiKey,
3072
+ dimensions: override.dimensions ?? baseConfig?.dimensions
3073
+ }
3074
+ };
3075
+ }
3076
+ function resolveEmbeddingProvider(provider) {
3077
+ if (!provider) return void 0;
3078
+ if ("config" in provider || "custom" in provider) return provider;
3079
+ return isEmbeddingHttpConfig(provider) ? { config: provider } : { custom: provider };
3080
+ }
3081
+ var EmbeddingUnavailableError = class extends Error {
3082
+ constructor(message) {
3083
+ super(message);
3084
+ this.name = "EmbeddingUnavailableError";
3085
+ }
3086
+ };
3087
+ function createEmbeddingClient() {
3088
+ const resolved = /* @__PURE__ */ new Map();
3089
+ const inflight = /* @__PURE__ */ new Map();
3090
+ return {
3091
+ async embed(texts, provider) {
3092
+ const inputs = texts.map((text) => text.replace(/\r?\n/g, " "));
3093
+ const keyed = inputs.map((text) => ({ text, key: cacheKeyFor(text, provider) }));
3094
+ const seen = /* @__PURE__ */ new Set();
3095
+ const needFetch = keyed.filter(({ key }) => {
3096
+ if (resolved.has(key) || inflight.has(key) || seen.has(key)) return false;
3097
+ seen.add(key);
3098
+ return true;
3099
+ });
3100
+ let fetchedMeta;
3101
+ if (needFetch.length > 0) {
3102
+ const fetchPromise = (async () => fetchEmbeddings(needFetch.map((n) => n.text), provider))();
3103
+ for (let i = 0; i < needFetch.length; i++) {
3104
+ const promise = fetchPromise.then((fetched) => {
3105
+ fetchedMeta = {
3106
+ dimensions: fetched.dimensions,
3107
+ ...fetched.servedModel !== void 0 ? { servedModel: fetched.servedModel } : {},
3108
+ ...fetched.tokens !== void 0 ? { tokens: fetched.tokens } : {},
3109
+ ...fetched.latencyMs !== void 0 ? { latencyMs: fetched.latencyMs } : {}
3110
+ };
3111
+ return fetched.vectors[i];
3112
+ });
3113
+ inflight.set(needFetch[i].key, promise);
3114
+ }
3115
+ void Promise.allSettled(needFetch.map(({ key }) => inflight.get(key))).then(() => {
3116
+ for (const { key } of needFetch) inflight.delete(key);
3117
+ });
3118
+ }
3119
+ const vectors = await Promise.all(
3120
+ keyed.map(async ({ key }) => {
3121
+ const inFlight = inflight.get(key);
3122
+ if (inFlight !== void 0) {
3123
+ const vector = await inFlight;
3124
+ resolved.set(key, vector);
3125
+ return vector;
3126
+ }
3127
+ const cached = resolved.get(key);
3128
+ if (cached !== void 0) return cached;
3129
+ throw new EmbeddingUnavailableError("embedding vector vanished: concurrent cache race");
3130
+ })
3131
+ );
3132
+ const dims = vectors[0].length;
3133
+ if (vectors.some((v) => v.length !== dims)) {
3134
+ throw new EmbeddingUnavailableError("provider returned vectors of mixed dimensionality");
3135
+ }
3136
+ return {
3137
+ vectors,
3138
+ dimensions: dims,
3139
+ ...fetchedMeta?.servedModel !== void 0 ? { servedModel: fetchedMeta.servedModel } : {},
3140
+ ...fetchedMeta?.tokens !== void 0 ? { tokens: fetchedMeta.tokens } : {},
3141
+ ...fetchedMeta?.latencyMs !== void 0 ? { latencyMs: fetchedMeta.latencyMs } : {}
3142
+ };
3143
+ }
3144
+ };
3145
+ }
3146
+ function cacheKeyFor(text, provider) {
3147
+ if (provider.custom) return `custom|${text}`;
3148
+ const c = provider.config;
3149
+ return `${c.model}|${c.baseURL ?? ""}|${c.dimensions ?? "auto"}|${text}`;
3150
+ }
3151
+ async function fetchEmbeddings(inputs, provider) {
3152
+ const startedAt = Date.now();
3153
+ if (provider.custom) {
3154
+ const vectors = await provider.custom.embed(inputs);
3155
+ if (!Array.isArray(vectors) || vectors.length !== inputs.length) {
3156
+ throw new EmbeddingUnavailableError(
3157
+ `custom embed function returned ${vectors?.length ?? 0} vectors for ${inputs.length} inputs`
3158
+ );
3159
+ }
3160
+ return { vectors, dimensions: vectors[0]?.length ?? 0, latencyMs: Date.now() - startedAt };
3161
+ }
3162
+ const config = provider.config;
3163
+ const call = (omitDimensions) => requestEmbeddings(config, inputs, omitDimensions);
3164
+ const first = await callWithRetry(() => call(false));
3165
+ const result = first.kind === "rejected-dimensions" ? await callWithRetry(() => call(true)) : first;
3166
+ if (result.kind !== "ok") throw new Error(result.message);
3167
+ return {
3168
+ vectors: result.vectors,
3169
+ ...result.servedModel ? { servedModel: result.servedModel } : {},
3170
+ dimensions: result.vectors[0]?.length ?? 0,
3171
+ ...result.tokens ? { tokens: result.tokens } : {},
3172
+ latencyMs: Date.now() - startedAt
3173
+ };
3174
+ }
3175
+ async function callWithRetry(call) {
3176
+ const first = await call();
3177
+ if (first.kind !== "error" || first.status !== 429) return first;
3178
+ const retryAfter = Number(first.message.match(/retry-after (\d+)/)?.[1] ?? 1);
3179
+ await new Promise((resolve5) => setTimeout(resolve5, Math.min(retryAfter, 10) * 1e3));
3180
+ return call();
3181
+ }
3182
+ async function requestEmbeddings(config, inputs, omitDimensions) {
3183
+ if (!config.baseURL) {
3184
+ return {
3185
+ kind: "error",
3186
+ message: "no embeddings endpoint configured: set APO_EMBEDDING_BASE_URL (or pass baseURL), e.g. https://openrouter.ai/api/v1 or a local server like http://localhost:11434/v1"
3187
+ };
3188
+ }
3189
+ const url = `${config.baseURL.replace(/\/$/, "")}/embeddings`;
3190
+ let response;
3191
+ try {
3192
+ response = await fetch(url, {
3193
+ method: "POST",
3194
+ headers: {
3195
+ "content-type": "application/json",
3196
+ ...config.apiKey ? { authorization: `Bearer ${config.apiKey}` } : {}
3197
+ },
3198
+ body: JSON.stringify({
3199
+ model: config.model,
3200
+ input: inputs,
3201
+ ...omitDimensions ? {} : config.dimensions !== void 0 ? { dimensions: config.dimensions } : {}
3202
+ })
3203
+ });
3204
+ } catch (error) {
3205
+ return { kind: "error", message: `embeddings endpoint unreachable: ${error instanceof Error ? error.message : String(error)}` };
3206
+ }
3207
+ if (response.status === 400) {
3208
+ const body2 = await response.text();
3209
+ if (/dimensions/i.test(body2)) return { kind: "rejected-dimensions", message: body2 };
3210
+ return { kind: "error", status: 400, message: `embeddings request rejected (400): ${body2}` };
3211
+ }
3212
+ if (response.status === 429) {
3213
+ const retryAfter = response.headers.get("retry-after");
3214
+ return { kind: "error", status: 429, message: `embeddings rate-limited (429), retry-after ${retryAfter ?? 1}` };
3215
+ }
3216
+ if (!response.ok) {
3217
+ const body2 = await response.text();
3218
+ return { kind: "error", status: response.status, message: `embeddings request failed (${response.status}): ${body2.slice(0, 500)}` };
3219
+ }
3220
+ const body = await response.json();
3221
+ const data = body.data;
3222
+ if (!Array.isArray(data) || data.length !== inputs.length) {
3223
+ return { kind: "error", message: `embeddings response malformed: expected ${inputs.length} vectors, got ${data?.length ?? "none"}` };
3224
+ }
3225
+ const vectors = Array.from({ length: data.length });
3226
+ data.forEach((item, position) => {
3227
+ const vector = item.embedding;
3228
+ if (!Array.isArray(vector) || vector.some((v) => typeof v !== "number")) {
3229
+ throw new EmbeddingUnavailableError("embeddings response malformed: a returned embedding is not a number array");
3230
+ }
3231
+ const slot = typeof item.index === "number" && item.index >= 0 && item.index < data.length ? item.index : position;
3232
+ vectors[slot] = vector;
3233
+ });
3234
+ if (vectors.some((v) => v === void 0)) {
3235
+ return { kind: "error", message: "embeddings response malformed: index field named a slot outside the response" };
3236
+ }
3237
+ return {
3238
+ kind: "ok",
3239
+ vectors,
3240
+ ...typeof body.model === "string" ? { servedModel: body.model } : {},
3241
+ ...typeof body.usage?.prompt_tokens === "number" ? { tokens: { input: body.usage.prompt_tokens } } : {}
3242
+ };
3243
+ }
3244
+ function cosineSimilarity(a, b) {
3245
+ if (a.length !== b.length) return Number.NaN;
3246
+ let dot = 0;
3247
+ let normA = 0;
3248
+ let normB = 0;
3249
+ for (let i = 0; i < a.length; i++) {
3250
+ dot += a[i] * b[i];
3251
+ normA += a[i] * a[i];
3252
+ normB += b[i] * b[i];
3253
+ }
3254
+ if (normA === 0 || normB === 0) return 0;
3255
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
3256
+ }
3257
+ function vectorMagnitude(v) {
3258
+ let sum = 0;
3259
+ for (const x of v) sum += x * x;
3260
+ return Math.sqrt(sum);
3261
+ }
3262
+ function embeddingSettingsSignature(model, dimensions) {
3263
+ return `${model}:${dimensions}d:cosine:apo-v1`;
3264
+ }
3265
+
2967
3266
  // src/agent-task/checks/t.ts
2968
3267
  function resolveJudgeConfig(judgeConfig, override) {
2969
3268
  const model = override?.model ?? judgeConfig?.model;
@@ -2985,6 +3284,8 @@ var TEST_METHOD_NAMES = [
2985
3284
  "usedNoTools",
2986
3285
  "maxToolCalls",
2987
3286
  "noFailedActions",
3287
+ "retrieved",
3288
+ "maxRetrievals",
2988
3289
  "loadedSkill",
2989
3290
  "calledSubagent",
2990
3291
  "messageIncludes",
@@ -2995,9 +3296,12 @@ var TEST_METHOD_NAMES = [
2995
3296
  "steerDelivered",
2996
3297
  "afterSteer",
2997
3298
  "assert",
3299
+ "not",
2998
3300
  "check",
2999
3301
  "judge",
3000
- "agent"
3302
+ "semantic",
3303
+ "agent",
3304
+ "grounded"
3001
3305
  ];
3002
3306
  function describeName(m) {
3003
3307
  if (typeof m === "string") return `"${m}"`;
@@ -3078,7 +3382,7 @@ function createJudgeMethod(rec, judgeConfig, judgeScope, judgeTracer) {
3078
3382
  rec.record(
3079
3383
  label,
3080
3384
  false,
3081
- "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)\n\u2022 ANTHROPIC_MODEL + ANTHROPIC_API_KEY (Anthropic direct \u2014 or ANTHROPIC_AUTH_TOKEN for plan credits)\nOr pass { judge } to runTask() programmatically.",
3385
+ "No judge model configured. Set one of:\n\u2022 AGENT_TASK_OPENROUTER_MODEL or OPENROUTER_MODEL + OPENROUTER_API_KEY (OpenRouter, 200+ models, one account)\n\u2022 OPENAI_MODEL + OPENAI_API_KEY (OpenAI direct)\n\u2022 ANTHROPIC_MODEL + ANTHROPIC_API_KEY (Anthropic direct, or ANTHROPIC_AUTH_TOKEN for plan credits)\nOr pass { judge } to runTask() programmatically.",
3082
3386
  // No judge means no verdict — the same bucket as an unreachable judge.
3083
3387
  { evaluator_type: "llm", location, outcome: "error" }
3084
3388
  );
@@ -3187,7 +3491,10 @@ function createJudgeMethod(rec, judgeConfig, judgeScope, judgeTracer) {
3187
3491
  text: res?.reasoning?.slice(0, 2e3) ?? "",
3188
3492
  verdict: {
3189
3493
  reasoning: res?.reasoning?.slice(0, 2e3) ?? null,
3190
- pass: res?.pass ?? null
3494
+ pass: res?.pass ?? null,
3495
+ // A judge that gave no verdict still reports pass: false;
3496
+ // the outcome tells a trace reader it is not a "no".
3497
+ ...res?.unavailable ? { outcome: "error" } : {}
3191
3498
  }
3192
3499
  };
3193
3500
  },
@@ -3258,6 +3565,143 @@ function judgeScopeToContext(scope) {
3258
3565
  ...deliverableNames && deliverableNames.length > 0 ? { deliverableNames } : {}
3259
3566
  };
3260
3567
  }
3568
+ function createSemanticMethod(rec, embedding, judgeScope) {
3569
+ const client = embedding?.client ?? createEmbeddingClient();
3570
+ const previewOf = (text) => text.length > 120 ? `${text.slice(0, 117)}...` : text;
3571
+ return async (value, reference, opts) => {
3572
+ const label = opts?.label ?? "semantic";
3573
+ const location = rec.captureLocation();
3574
+ const threshold = opts.threshold;
3575
+ if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
3576
+ rec.record(
3577
+ label,
3578
+ false,
3579
+ `t.semantic: threshold must be a finite number in [0, 1], got ${threshold}`,
3580
+ { evaluator_type: "embedding", location }
3581
+ );
3582
+ return;
3583
+ }
3584
+ const resolved = resolveEmbeddingConfig(embedding?.provider, opts.embedding);
3585
+ if (!resolved) {
3586
+ rec.record(
3587
+ label,
3588
+ false,
3589
+ "No embedding model configured. Set one of:\n\u2022 APO_EMBEDDING_MODEL + APO_EMBEDDING_API_KEY (+ APO_EMBEDDING_BASE_URL for a local server)\n\u2022 OPENROUTER_API_KEY (defaults to openai/text-embedding-3-small via OpenRouter)\n\u2022 OPENAI_API_KEY (defaults to text-embedding-3-small)\nOr pass { embedding } to runTask() programmatically.",
3590
+ { evaluator_type: "embedding", location, outcome: "error" }
3591
+ );
3592
+ return;
3593
+ }
3594
+ const valueText = typeof value === "string" ? value : describeValue(value);
3595
+ const modelName = resolved.config?.model ?? "custom";
3596
+ const tracer = embedding?.tracer;
3597
+ const grade = (result) => {
3598
+ const [valueVector, referenceVector] = result.vectors;
3599
+ const similarity2 = cosineSimilarity(valueVector, referenceVector);
3600
+ if (!Number.isFinite(similarity2)) {
3601
+ throw new Error("cosine similarity undefined: provider returned non-finite vector components");
3602
+ }
3603
+ const rounded = Math.round(similarity2 * 1e4) / 1e4;
3604
+ return {
3605
+ similarity: rounded,
3606
+ pass: rounded >= threshold,
3607
+ signature: embeddingSettingsSignature(modelName, result.dimensions),
3608
+ dimensions: result.dimensions,
3609
+ ...result.servedModel ? { servedModel: result.servedModel } : {},
3610
+ ...result.tokens ? { tokens: result.tokens } : {},
3611
+ ...result.latencyMs !== void 0 ? { latencyMs: result.latencyMs } : {},
3612
+ zeroMagnitude: vectorMagnitude(valueVector) === 0 || vectorMagnitude(referenceVector) === 0
3613
+ };
3614
+ };
3615
+ let spanId;
3616
+ const call = () => client.embed([valueText, reference], resolved);
3617
+ try {
3618
+ const verdict = await (tracer ? tracer.step(
3619
+ {
3620
+ step_name: `embedding:${judgeScope?.checkName ?? label}`,
3621
+ observation_type: "EMBEDDING",
3622
+ model: modelName,
3623
+ ...servingHostFromBaseURL(resolved.config?.baseURL),
3624
+ input: { model: modelName, threshold },
3625
+ summarize: (r) => {
3626
+ const res = r;
3627
+ const text = `cosine ${res.similarity} vs threshold ${threshold}` + (res.pass ? ": pass" : ": fail");
3628
+ return {
3629
+ text,
3630
+ verdict: {
3631
+ pass: res.pass,
3632
+ similarity: res.similarity,
3633
+ threshold,
3634
+ settings: res.signature
3635
+ }
3636
+ };
3637
+ },
3638
+ usage: (r) => {
3639
+ const tokens = r.tokens;
3640
+ return tokens?.input !== void 0 ? { prompt_tokens: tokens.input } : void 0;
3641
+ }
3642
+ },
3643
+ async (id) => {
3644
+ spanId = id;
3645
+ return grade(await call());
3646
+ }
3647
+ ) : grade(await call()));
3648
+ const delta = verdict.similarity - threshold;
3649
+ let reasoning = verdict.pass ? `semantic similarity ${verdict.similarity.toFixed(4)} \u2265 ${threshold.toFixed(4)} [${verdict.signature}]` : `semantic similarity ${verdict.similarity.toFixed(4)} < ${threshold.toFixed(4)} (delta ${delta.toFixed(4)}) [${verdict.signature}]`;
3650
+ if (Math.abs(delta) < 0.02) {
3651
+ reasoning += ". Verdict is marginal: within 0.02 of the threshold, it will flip on model drift";
3652
+ }
3653
+ if (verdict.zeroMagnitude) {
3654
+ reasoning += " (zero-magnitude vector: the provider returned an empty direction)";
3655
+ }
3656
+ const metadata = {
3657
+ model: modelName,
3658
+ ...verdict.servedModel ? { served_model: verdict.servedModel } : {},
3659
+ dimensions: verdict.dimensions,
3660
+ similarity: verdict.similarity,
3661
+ threshold,
3662
+ metric: "cosine",
3663
+ settings_hash: verdict.signature,
3664
+ ...verdict.tokens ? { tokens: verdict.tokens } : {},
3665
+ ...verdict.latencyMs !== void 0 ? { latency_ms: verdict.latencyMs } : {},
3666
+ ...isTraceableSpanId(spanId) ? { span_id: spanId } : {},
3667
+ value_preview: previewOf(valueText),
3668
+ reference_preview: previewOf(reference)
3669
+ };
3670
+ rec.record(label, verdict.pass, reasoning, {
3671
+ evaluator_type: "embedding",
3672
+ expected: `cosine \u2265 ${threshold.toFixed(4)} (${modelName}:${verdict.dimensions}d)`,
3673
+ received: verdict.similarity,
3674
+ location,
3675
+ embedding: metadata
3676
+ });
3677
+ if (tracer && isTraceableSpanId(spanId)) {
3678
+ try {
3679
+ await tracer.score({
3680
+ name: "semantic_similarity",
3681
+ value: verdict.similarity,
3682
+ dataType: "NUMERIC",
3683
+ source: "EVAL",
3684
+ observationId: spanId
3685
+ }).catch(() => void 0);
3686
+ } catch {
3687
+ }
3688
+ }
3689
+ } catch (error) {
3690
+ rec.record(
3691
+ label,
3692
+ false,
3693
+ `embedding check failed: ${error instanceof Error ? error.message : String(error)}`,
3694
+ {
3695
+ evaluator_type: "embedding",
3696
+ expected: `cosine \u2265 ${threshold.toFixed(4)}`,
3697
+ received: valueText,
3698
+ location,
3699
+ outcome: "error"
3700
+ }
3701
+ );
3702
+ }
3703
+ };
3704
+ }
3261
3705
  var CAPABILITY_LABELS = {
3262
3706
  messages: "message",
3263
3707
  tools: "tool",
@@ -3265,9 +3709,10 @@ var CAPABILITY_LABELS = {
3265
3709
  timing: "timing",
3266
3710
  skills: "skill",
3267
3711
  subagents: "subagent",
3268
- usage: "token usage"
3712
+ usage: "token usage",
3713
+ retrieval: "retrieval"
3269
3714
  };
3270
- function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidence, judgeTracer, judgeTools) {
3715
+ function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidence, judgeTracer, judgeTools, embedding) {
3271
3716
  const unsupported = (id, capability) => {
3272
3717
  const label = CAPABILITY_LABELS[capability];
3273
3718
  rec.record(id, false, `${label} evidence is unavailable in this trace projection`, {
@@ -3277,7 +3722,24 @@ function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidenc
3277
3722
  });
3278
3723
  };
3279
3724
  const isAvailable = (c) => view.requireCapability(c) === "available";
3280
- return {
3725
+ const traceSourcesFor = (label) => {
3726
+ if (!isAvailable("retrieval")) {
3727
+ unsupported(label, "retrieval");
3728
+ return void 0;
3729
+ }
3730
+ const opaque = view.retrievals.filter((r) => r.documents === void 0).length;
3731
+ if (opaque > 0) {
3732
+ rec.record(label, false, `${opaque} retrieval(s) returned results in an unrecognized shape, so the sources are incomplete`, {
3733
+ evaluator_type: "llm",
3734
+ outcome: "unsupported",
3735
+ expected: "every retrieval's documents known",
3736
+ received: `${opaque} unrecognized retrieval result(s)`
3737
+ });
3738
+ return void 0;
3739
+ }
3740
+ return formatTraceSources(view.retrievals);
3741
+ };
3742
+ const context2 = {
3281
3743
  calledTool(name, opts) {
3282
3744
  if (!isAvailable("tools")) {
3283
3745
  unsupported(`calledTool(${describeName(name)})`, "tools");
@@ -3375,6 +3837,51 @@ function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidenc
3375
3837
  received: `${failed}`
3376
3838
  });
3377
3839
  },
3840
+ retrieved(document, opts) {
3841
+ requireTopK(opts?.topK);
3842
+ const id = `retrieved(${describeDocument(document)}${describeRetrievedOptions(opts)})`;
3843
+ if (!isAvailable("retrieval")) {
3844
+ unsupported(id, "retrieval");
3845
+ return;
3846
+ }
3847
+ const expected = `${describeDocument(document)} retrieved${describeRetrievedOptions(opts)}`;
3848
+ const scoped = view.retrievals.filter(
3849
+ (r) => opts?.query === void 0 || (r.query?.includes(opts.query) ?? false)
3850
+ );
3851
+ const hit = findRetrievedDocument(scoped, document, opts?.topK);
3852
+ if (hit !== void 0) {
3853
+ rec.record(id, true, "", { expected, received: hit });
3854
+ return;
3855
+ }
3856
+ const opaque = scoped.filter((r) => r.documents === void 0).length;
3857
+ if (opaque > 0) {
3858
+ rec.record(
3859
+ id,
3860
+ false,
3861
+ `${opaque} retrieval(s) in scope returned results in an unrecognized shape`,
3862
+ { outcome: "unsupported", expected, received: `${opaque} unrecognized retrieval result(s)` }
3863
+ );
3864
+ return;
3865
+ }
3866
+ rec.record(
3867
+ id,
3868
+ false,
3869
+ scoped.length === 0 ? `no retrieval ran with a query containing "${opts?.query ?? ""}"` : `no retrieval returned ${describeDocument(document)}${describeRetrievedOptions(opts)}`,
3870
+ { expected, received: summarizeRetrievals(scoped) }
3871
+ );
3872
+ },
3873
+ maxRetrievals(n) {
3874
+ requireBudget("maxRetrievals", n);
3875
+ if (view.requireCapability("retrieval") === "unavailable") {
3876
+ unsupported(`maxRetrievals(${n})`, "retrieval");
3877
+ return;
3878
+ }
3879
+ const count = view.retrievals.length;
3880
+ rec.record(`maxRetrievals(${n})`, count <= n, count <= n ? "" : `expected \u2264 ${n} retrievals, got ${count}`, {
3881
+ expected: `\u2264 ${n} retrievals`,
3882
+ received: `${count}`
3883
+ });
3884
+ },
3378
3885
  loadedSkill(skill) {
3379
3886
  if (!isAvailable("skills")) {
3380
3887
  unsupported(`loadedSkill("${skill}")`, "skills");
@@ -3462,7 +3969,8 @@ function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidenc
3462
3969
  judgeScope,
3463
3970
  agentEvidence,
3464
3971
  judgeTracer,
3465
- judgeTools
3972
+ judgeTools,
3973
+ embedding
3466
3974
  )
3467
3975
  ),
3468
3976
  check(value, matcher, label) {
@@ -3477,7 +3985,38 @@ function createTraceTestContext(view, rec, judgeConfig, judgeScope, agentEvidenc
3477
3985
  // judge does not consult trace capabilities; the scope carries the
3478
3986
  // task/check frame for prompt builders (#161).
3479
3987
  judge: trackPending(rec, createJudgeMethod(rec, judgeConfig, judgeScope, judgeTracer)),
3480
- agent: trackPending(rec, createAgentMethod(rec, judgeConfig, judgeScope, agentEvidence ?? { deliverables: {}, view }, judgeTracer, judgeTools))
3988
+ agent: trackPending(rec, createAgentMethod(rec, judgeConfig, judgeScope, agentEvidence ?? { deliverables: {}, view }, judgeTracer, judgeTools)),
3989
+ // Like t.judge: value-plane check, no capability gating — but traced, so
3990
+ // the embeddings call shows up as an EMBEDDING observation.
3991
+ semantic: trackPending(rec, createSemanticMethod(rec, embedding, judgeScope)),
3992
+ async grounded(value, opts) {
3993
+ const label = opts?.label ?? "grounded";
3994
+ const sources = opts?.sources ?? "trace";
3995
+ const sourceList = sources === "trace" ? traceSourcesFor(label) : formatAuthorSources(sources);
3996
+ if (sourceList === void 0) return;
3997
+ await context2.judge([value, sourceList], GROUNDEDNESS_RUBRIC, {
3998
+ label,
3999
+ judge: opts?.judge,
4000
+ expect: opts?.expect
4001
+ });
4002
+ },
4003
+ get not() {
4004
+ return createNotContext(context2, rec);
4005
+ }
4006
+ };
4007
+ return context2;
4008
+ }
4009
+ function createNotContext(t, rec) {
4010
+ return {
4011
+ calledTool: (name, opts) => rec.negated(() => t.calledTool(name, opts)),
4012
+ toolOrder: (names) => rec.negated(() => t.toolOrder(names)),
4013
+ loadedSkill: (skill) => rec.negated(() => t.loadedSkill(skill)),
4014
+ calledSubagent: (agent) => rec.negated(() => t.calledSubagent(agent)),
4015
+ messageIncludes: (token) => rec.negated(() => t.messageIncludes(token)),
4016
+ retrieved: (document, opts) => rec.negated(() => t.retrieved(document, opts)),
4017
+ check(value, matcher, label) {
4018
+ rec.negated(() => t.check(value, matcher, label));
4019
+ }
3481
4020
  };
3482
4021
  }
3483
4022
  function requireBudget(method, n) {
@@ -3582,14 +4121,76 @@ function matchesTraceToolCall(call, name, opts) {
3582
4121
  }
3583
4122
  return true;
3584
4123
  }
4124
+ function findRetrievedDocument(retrievals, document, topK) {
4125
+ for (const retrieval of retrievals) {
4126
+ const documents = retrieval.documents ?? [];
4127
+ const limit = topK === void 0 ? documents.length : Math.min(topK, documents.length);
4128
+ for (let i = 0; i < limit; i++) {
4129
+ if (matchesDocument(documents[i], document)) {
4130
+ return `rank ${i + 1} for query "${retrieval.query ?? "(none recorded)"}"`;
4131
+ }
4132
+ }
4133
+ }
4134
+ return void 0;
4135
+ }
4136
+ function matchesDocument(doc, matcher) {
4137
+ if (typeof matcher === "string") {
4138
+ return doc.id === matcher || (doc.id?.startsWith(`${matcher}#`) ?? false);
4139
+ }
4140
+ if ("id" in matcher) return doc.id === matcher.id;
4141
+ if ("idPrefix" in matcher) return doc.id?.startsWith(matcher.idPrefix) ?? false;
4142
+ return doc.content.includes(matcher.contentIncludes);
4143
+ }
4144
+ function describeDocument(matcher) {
4145
+ if (typeof matcher === "string") return `"${matcher}"`;
4146
+ if ("id" in matcher) return `{ id: "${matcher.id}" }`;
4147
+ if ("idPrefix" in matcher) return `{ idPrefix: "${matcher.idPrefix}" }`;
4148
+ return `{ contentIncludes: "${matcher.contentIncludes}" }`;
4149
+ }
4150
+ function describeRetrievedOptions(opts) {
4151
+ const parts = [];
4152
+ if (opts?.topK !== void 0) parts.push(`in top ${opts.topK}`);
4153
+ if (opts?.query !== void 0) parts.push(`for query containing "${opts.query}"`);
4154
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
4155
+ }
4156
+ function summarizeRetrievals(retrievals) {
4157
+ if (retrievals.length === 0) return "(no retrievals)";
4158
+ return retrievals.map((r) => {
4159
+ const documents = (r.documents ?? []).map((d) => d.id ?? `"${d.content.slice(0, 40)}"`);
4160
+ const shown = documents.slice(0, 10).join(", ") + (documents.length > 10 ? ", ..." : "");
4161
+ return `"${r.query ?? "(none recorded)"}": [${shown}]`;
4162
+ }).join("; ");
4163
+ }
4164
+ function requireTopK(topK) {
4165
+ if (topK !== void 0 && (!Number.isInteger(topK) || topK < 1)) {
4166
+ throw new TypeError(`retrieved: topK must be a positive integer, got ${topK}`);
4167
+ }
4168
+ }
3585
4169
 
3586
4170
  // src/agent-task/checks/recorder.ts
3587
4171
  var Recorder = class {
3588
4172
  constructor(locate) {
3589
4173
  this.records = [];
3590
4174
  this.pending = [];
4175
+ this.negating = false;
3591
4176
  this.locate = locate;
3592
4177
  }
4178
+ /**
4179
+ * Run `fn` with every assertion it records negated (`t.not.*`): a pass
4180
+ * becomes a fail and a fail becomes a pass. Outcomes that are not verdicts
4181
+ * (`unsupported`, `error`) keep their outcome, so missing evidence can never
4182
+ * become a pass by negation. Only synchronous assertions are negatable,
4183
+ * which keeps the scope from leaking into a later record.
4184
+ */
4185
+ negated(fn) {
4186
+ const previous = this.negating;
4187
+ this.negating = true;
4188
+ try {
4189
+ fn();
4190
+ } finally {
4191
+ this.negating = previous;
4192
+ }
4193
+ }
3593
4194
  /**
3594
4195
  * Capture the call site location synchronously. Use this when a record will
3595
4196
  * happen AFTER an `await` (e.g. `t.judge`): once an async function resumes,
@@ -3625,7 +4226,7 @@ var Recorder = class {
3625
4226
  */
3626
4227
  record(id, pass, reasoning, extra) {
3627
4228
  const location = extra?.location ?? (this.locate ? this.locate(new Error().stack ?? "") : void 0);
3628
- this.records.push({
4229
+ const result = {
3629
4230
  id,
3630
4231
  pass,
3631
4232
  reasoning,
@@ -3634,13 +4235,29 @@ var Recorder = class {
3634
4235
  received: extra?.received,
3635
4236
  evaluator_type: extra?.evaluator_type,
3636
4237
  judge: extra?.judge,
4238
+ embedding: extra?.embedding,
3637
4239
  outcome: extra?.outcome
3638
- });
4240
+ };
4241
+ this.records.push(this.negating ? negate(result) : result);
3639
4242
  }
3640
4243
  get all() {
3641
4244
  return this.records;
3642
4245
  }
3643
4246
  };
4247
+ function negate(result) {
4248
+ const negated = {
4249
+ ...result,
4250
+ id: `not.${result.id}`,
4251
+ expected: result.expected === void 0 ? void 0 : `not ${result.expected}`
4252
+ };
4253
+ if (result.outcome === "unsupported" || result.outcome === "error") return negated;
4254
+ return {
4255
+ ...negated,
4256
+ pass: !result.pass,
4257
+ reasoning: result.pass ? `expected ${result.id} not to hold, but it did` : "",
4258
+ outcome: result.outcome === void 0 ? void 0 : result.pass ? "fail" : "pass"
4259
+ };
4260
+ }
3644
4261
 
3645
4262
  // src/agent-task/checks/compact.ts
3646
4263
  import { createHash as createHash4 } from "crypto";
@@ -3875,12 +4492,18 @@ async function runTraceChecks(args) {
3875
4492
  const view = new TraceView(args.snapshot);
3876
4493
  const locate = args.moduleUrl && args.displayFile ? (stack) => parseCheckLocation(stack, args.moduleUrl, args.displayFile) : void 0;
3877
4494
  const taskMeta = readTaskMeta(args.task);
4495
+ const embedding = args.embedding ? { ...args.embedding, ...args.embedding.tracer ? {} : optionalTracer(args.judgeTracer) } : { client: createEmbeddingClient(), ...optionalTracer(args.judgeTracer) };
3878
4496
  const results = await Promise.all(
3879
- registry.map((check) => runOneCheck(check, args, view, taskMeta, locate))
4497
+ registry.map((check) => runOneCheck(check, args, embedding, view, taskMeta, locate))
3880
4498
  );
3881
4499
  return results;
3882
4500
  }
3883
- async function runOneCheck(check, args, view, taskMeta, locate) {
4501
+ function optionalTracer(tracer) {
4502
+ if (!tracer) return {};
4503
+ const candidate = tracer;
4504
+ return typeof candidate.step === "function" && typeof candidate.score === "function" ? { tracer: candidate } : {};
4505
+ }
4506
+ async function runOneCheck(check, args, embedding, view, taskMeta, locate) {
3884
4507
  const runCheck = async () => {
3885
4508
  const rec = new Recorder(locate);
3886
4509
  const reads = trackDeliverableReads(args.deliverables);
@@ -3904,7 +4527,8 @@ async function runOneCheck(check, args, view, taskMeta, locate) {
3904
4527
  ...args.historyUnavailableReason ? { historyUnavailable: args.historyUnavailableReason } : {}
3905
4528
  },
3906
4529
  args.judgeTracer,
3907
- args.judgeTools
4530
+ args.judgeTools,
4531
+ embedding
3908
4532
  );
3909
4533
  let thrownLocation;
3910
4534
  try {
@@ -3924,9 +4548,11 @@ async function runOneCheck(check, args, view, taskMeta, locate) {
3924
4548
  const pass = failed.length === 0;
3925
4549
  const outcome = rollUpCheckOutcome(failed);
3926
4550
  const judged = rec.all.filter(isJudgedAssertion);
3927
- const reasoning = failed.length > 0 ? failed.map((r) => r.reasoning || r.id).join("; ") : judged.length > 0 ? judged.map((r) => r.reasoning).filter(Boolean).join("; ") || "passed" : rec.all.length > 0 ? "passed" : "no assertions recorded";
4551
+ const semantic = rec.all.filter((r) => r.evaluator_type === "embedding");
4552
+ const reasoning = failed.length > 0 ? failed.map((r) => r.reasoning || r.id).join("; ") : judged.length > 0 || semantic.length > 0 ? [...judged, ...semantic].map((r) => r.reasoning).filter(Boolean).join("; ") || "passed" : rec.all.length > 0 ? "passed" : "no assertions recorded";
3928
4553
  const location = failed.find((r) => r.location)?.location;
3929
4554
  const judge = rec.all.find((r) => r.judge)?.judge;
4555
+ const embeddingMeta = rec.all.find((r) => r.embedding)?.embedding;
3930
4556
  return {
3931
4557
  id: check.id,
3932
4558
  pass,
@@ -3934,6 +4560,7 @@ async function runOneCheck(check, args, view, taskMeta, locate) {
3934
4560
  evaluator_type: deriveEvaluatorType(rec.all),
3935
4561
  ...outcome ? { outcome } : {},
3936
4562
  ...judge ? { judge } : {},
4563
+ ...embeddingMeta ? { embedding: embeddingMeta } : {},
3937
4564
  ...location ? { location } : {},
3938
4565
  ...args.displayFile ? { source_file: args.displayFile } : {},
3939
4566
  ...rec.all.length > 0 ? { assertions: rec.all.map((a) => ({ ...a })) } : {},
@@ -3963,35 +4590,48 @@ function isJudgedAssertion(assertion) {
3963
4590
  function deriveEvaluatorType(assertions) {
3964
4591
  let sawLlm = false;
3965
4592
  let sawAgent = false;
4593
+ let sawEmbedding = false;
3966
4594
  let sawDeterministic = false;
3967
4595
  for (const assertion of assertions) {
3968
4596
  if (isJudgedAssertion(assertion)) {
3969
4597
  if (assertion.evaluator_type === "agent") sawAgent = true;
3970
4598
  else sawLlm = true;
4599
+ } else if (assertion.evaluator_type === "embedding") {
4600
+ sawEmbedding = true;
3971
4601
  } else {
3972
4602
  sawDeterministic = true;
3973
4603
  }
3974
4604
  }
3975
- if (!sawLlm && !sawAgent) return "code";
3976
- if (sawDeterministic) return "mixed";
3977
- if (sawAgent && !sawLlm) return "agent";
3978
- return "llm";
4605
+ const evaluatorKinds = [sawLlm, sawAgent, sawEmbedding].filter(Boolean).length;
4606
+ if (evaluatorKinds === 0 && !sawDeterministic) return "code";
4607
+ if (evaluatorKinds === 1 && !sawDeterministic) {
4608
+ if (sawAgent) return "agent";
4609
+ if (sawEmbedding) return "embedding";
4610
+ return "llm";
4611
+ }
4612
+ if (evaluatorKinds > 0) return "mixed";
4613
+ return "code";
3979
4614
  }
3980
4615
  function summarizeCheckForSpan(result) {
3981
4616
  const check = result;
4617
+ const reasoning = check.reasoning.slice(0, 2e3);
3982
4618
  return {
3983
- pass: check.pass,
3984
- reasoning: check.reasoning.slice(0, 2e3),
3985
- evaluator_type: check.evaluator_type,
3986
- ...check.judge ? { judge_model: check.judge.model } : {},
3987
- ...check.assertions ? {
3988
- assertions: check.assertions.map((a) => ({
3989
- id: a.id,
3990
- pass: a.pass,
3991
- ...a.expected !== void 0 ? { expected: a.expected } : {},
3992
- ...a.received !== void 0 ? { received: truncateReceivedValue(a.received) } : {}
3993
- }))
3994
- } : {}
4619
+ text: reasoning,
4620
+ verdict: {
4621
+ pass: check.pass,
4622
+ ...check.outcome ? { outcome: check.outcome } : {},
4623
+ reasoning,
4624
+ evaluator_type: check.evaluator_type,
4625
+ ...check.judge ? { judge_model: check.judge.model } : {},
4626
+ ...check.assertions ? {
4627
+ assertions: check.assertions.map((a) => ({
4628
+ id: a.id,
4629
+ pass: a.pass,
4630
+ ...a.expected !== void 0 ? { expected: a.expected } : {},
4631
+ ...a.received !== void 0 ? { received: truncateReceivedValue(a.received) } : {}
4632
+ }))
4633
+ } : {}
4634
+ }
3995
4635
  };
3996
4636
  }
3997
4637
  function rollUpCheckOutcome(failed) {
@@ -4016,6 +4656,7 @@ async function loadAndRunFlowChecks(checksPath, args, brokenDeliverables = {}) {
4016
4656
  ...args.judgeTools ? { judgeTools: args.judgeTools } : {},
4017
4657
  ...args.historyPlane ? { historyPlane: args.historyPlane } : {},
4018
4658
  ...args.judgeTracer ? { judgeTracer: args.judgeTracer } : {},
4659
+ ...args.embedding ? { embedding: args.embedding } : {},
4019
4660
  moduleUrl,
4020
4661
  displayFile: basename(checksPath)
4021
4662
  });
@@ -4816,6 +5457,7 @@ function deriveCapabilities(observations, hierarchyAmbiguous) {
4816
5457
  const hasMessages = observations.some(
4817
5458
  (o) => o.type === "GENERATION" && o.messages && o.messages.length > 0
4818
5459
  );
5460
+ const retrievals = observations.filter((o) => o.type === "RETRIEVER");
4819
5461
  return {
4820
5462
  messages: hasMessages ? "available" : "unavailable",
4821
5463
  tools: hasType("TOOL") ? "available" : "unavailable",
@@ -4828,7 +5470,8 @@ function deriveCapabilities(observations, hierarchyAmbiguous) {
4828
5470
  // Usage is attributed to turns through parentSpanId; with an ambiguous
4829
5471
  // step id the chain is broken, so a per-turn or per-run sum could
4830
5472
  // silently drop turns.
4831
- usage: !observations.some((o) => o.usage !== void 0) ? "unavailable" : hierarchyAmbiguous ? "partial" : "available"
5473
+ usage: !observations.some((o) => o.usage !== void 0) ? "unavailable" : hierarchyAmbiguous ? "partial" : "available",
5474
+ retrieval: retrievals.length === 0 ? "unavailable" : retrievals.some((o) => documentsFromObservation(o) !== void 0) ? "available" : "partial"
4832
5475
  };
4833
5476
  }
4834
5477
  function usageFrom(params) {
@@ -4844,6 +5487,7 @@ function createProjectionTee(real) {
4844
5487
  const observations = [];
4845
5488
  const pending = /* @__PURE__ */ new Map();
4846
5489
  const realIdByTeeId = /* @__PURE__ */ new Map();
5490
+ const exportedSpanIds = /* @__PURE__ */ new Set();
4847
5491
  const teeIdByRealStepId = /* @__PURE__ */ new Map();
4848
5492
  const ambiguousRealStepIds = /* @__PURE__ */ new Set();
4849
5493
  const activeStep = new AsyncLocalStorage();
@@ -4881,6 +5525,7 @@ function createProjectionTee(real) {
4881
5525
  } else if (params?.output !== void 0) {
4882
5526
  obs.output = params.output;
4883
5527
  }
5528
+ if (p.type === "RETRIEVER" && p.input !== void 0) obs.input = p.input;
4884
5529
  if (p.type === "GENERATION" && params?.output != null) {
4885
5530
  const text = extractText(params.output);
4886
5531
  if (text !== "") {
@@ -4920,6 +5565,7 @@ function createProjectionTee(real) {
4920
5565
  ambiguousRealStepIds.add(realSpanId);
4921
5566
  } else {
4922
5567
  teeIdByRealStepId.set(realSpanId, obsSpanId);
5568
+ exportedSpanIds.add(realSpanId);
4923
5569
  }
4924
5570
  }
4925
5571
  return activeStep.run(obsSpanId, () => fn(realSpanId));
@@ -4979,6 +5625,9 @@ function createProjectionTee(real) {
4979
5625
  realIdByTeeId.delete(spanId);
4980
5626
  try {
4981
5627
  real.endSpan(realId, endParams);
5628
+ if (realId != null && realId !== spanId) {
5629
+ exportedSpanIds.add(realId);
5630
+ }
4982
5631
  } catch {
4983
5632
  }
4984
5633
  },
@@ -5008,7 +5657,32 @@ function createProjectionTee(real) {
5008
5657
  }
5009
5658
  },
5010
5659
  async traceRetriever(query, fn) {
5011
- return real.traceRetriever(query, fn);
5660
+ const obsSpanId = recordStart({
5661
+ step_name: "retrieve",
5662
+ observation_type: "RETRIEVER",
5663
+ input: { query }
5664
+ });
5665
+ const start = monotonicNowMs();
5666
+ try {
5667
+ const result = await real.traceRetriever(query, fn);
5668
+ const documents = parseRetrievedDocuments(result);
5669
+ complete(obsSpanId, {
5670
+ latency_ms: round3(monotonicNowMs() - start),
5671
+ // Recognized results are stored normalized so every reader sees one
5672
+ // typed shape. Anything else passes through raw, and the retrieval
5673
+ // capability derives "partial" from it.
5674
+ output: documents !== void 0 ? { documents } : result
5675
+ });
5676
+ return result;
5677
+ } catch (error) {
5678
+ const message = error instanceof Error ? error.message : String(error);
5679
+ complete(obsSpanId, {
5680
+ latency_ms: round3(monotonicNowMs() - start),
5681
+ status_message: message,
5682
+ level: "ERROR"
5683
+ });
5684
+ throw error;
5685
+ }
5012
5686
  },
5013
5687
  async traceChain(name, fn) {
5014
5688
  return real.traceChain(name, fn);
@@ -5045,14 +5719,22 @@ function createProjectionTee(real) {
5045
5719
  observations: [...observations]
5046
5720
  };
5047
5721
  },
5048
- injectObservations(injected) {
5722
+ injectObservations(injected, opts) {
5049
5723
  observations.push(...injected);
5724
+ if (opts?.remote !== false) {
5725
+ for (const observation of injected) {
5726
+ exportedSpanIds.add(observation.spanId);
5727
+ }
5728
+ }
5050
5729
  observations.sort((a, b) => {
5051
5730
  const at = a.startedAt ?? "";
5052
5731
  const bt = b.startedAt ?? "";
5053
5732
  if (at !== bt) return at < bt ? -1 : 1;
5054
5733
  return a.spanId < b.spanId ? -1 : 1;
5055
5734
  });
5735
+ },
5736
+ getExportedSpanIds() {
5737
+ return new Set(exportedSpanIds);
5056
5738
  }
5057
5739
  };
5058
5740
  }
@@ -6064,6 +6746,11 @@ function resolveJudgeToolsForTask(runLevel, taskLevel, taskDir) {
6064
6746
  if (!layered?.mcp) return layered;
6065
6747
  return { mcp: resolveMcpServerPaths(layered.mcp, taskDir) };
6066
6748
  }
6749
+ function buildEmbeddingRuntime(runLevel, taskLevel) {
6750
+ const provider = resolveEmbeddingConfig(runLevel, taskLevel);
6751
+ if (!provider) return void 0;
6752
+ return { provider, client: createEmbeddingClient() };
6753
+ }
6067
6754
  async function runTask(taskDir, options) {
6068
6755
  const loaded = options?.loaded ?? await loadTask(taskDir);
6069
6756
  const trace2 = options?.tracing;
@@ -6081,19 +6768,19 @@ async function runTask(taskDir, options) {
6081
6768
  const phase1 = await captureExecution(loaded, options, traceContext);
6082
6769
  const canonical = await readCanonicalSnapshot(
6083
6770
  trace2,
6084
- phase1.replayObservationFloor
6771
+ phase1.exportedSpanIds
6085
6772
  );
6086
6773
  if (canonical) phase1.snapshot = canonical;
6087
6774
  return evaluate(loaded, options, phase1, traceContext);
6088
6775
  }
6089
6776
  );
6090
6777
  }
6091
- async function readCanonicalSnapshot(trace2, minObservations) {
6778
+ async function readCanonicalSnapshot(trace2, exportedSpanIds, opts = {}) {
6092
6779
  if (!trace2.taskRunId) return null;
6093
6780
  const endpoint = process.env.AGENT_TASK_TRACE_ENDPOINT;
6094
6781
  const authToken = process.env.APO_AUTH_TOKEN;
6095
6782
  if (!endpoint || !authToken) return null;
6096
- const deadlineMs = 3e4;
6783
+ const deadlineMs = opts.deadlineMs ?? 3e4;
6097
6784
  const start = Date.now();
6098
6785
  try {
6099
6786
  let snapshot = await readTaskRunProjection({
@@ -6101,25 +6788,17 @@ async function readCanonicalSnapshot(trace2, minObservations) {
6101
6788
  authToken,
6102
6789
  taskRunId: trace2.taskRunId,
6103
6790
  deadlineMs,
6104
- // Fresh-run readback: the run row was pre-created by the caller/executor
6105
- // flow, and the subprocess-exported trace may still be in flight when
6106
- // the checks phase starts — a 409 "no trace yet" must wait for the
6107
- // claim, not fall back to a local snapshot that cannot see the
6108
- // subprocess's spans.
6109
- // Fresh-run readback: the run row was pre-created by the caller/executor
6110
- // flow, and the subprocess-exported trace may still be in flight when
6111
- // the checks phase starts — a 409 "no trace yet" must wait for the
6112
- // claim, not fall back to a local snapshot that cannot see the
6113
- // subprocess's spans.
6791
+ // Fresh-run readback: the trace claim may still be landing, so the
6792
+ // 409 must be waited out rather than fallen back from.
6114
6793
  retryNoTrace: true
6115
6794
  });
6116
- while (minObservations !== void 0 && snapshot.observations.length < minObservations) {
6795
+ while (!containsAllSpanIds(snapshot, exportedSpanIds)) {
6117
6796
  const remaining = deadlineMs - (Date.now() - start);
6118
6797
  if (remaining <= 0) {
6119
6798
  console.error(
6120
- `[AgentTask] Projection still missing replayed observations after ${deadlineMs}ms (${snapshot.observations.length}/${minObservations}); continuing with the current snapshot`
6799
+ `[AgentTask] Projection missing ${countMissingSpanIds(snapshot, exportedSpanIds)}/${exportedSpanIds.size} spans the run exported after ${deadlineMs}ms; using the local snapshot`
6121
6800
  );
6122
- break;
6801
+ return null;
6123
6802
  }
6124
6803
  await new Promise((resolve5) => setTimeout(resolve5, 500));
6125
6804
  snapshot = await readTaskRunProjection({
@@ -6138,6 +6817,27 @@ async function readCanonicalSnapshot(trace2, minObservations) {
6138
6817
  return null;
6139
6818
  }
6140
6819
  }
6820
+ function containsAllSpanIds(snapshot, required) {
6821
+ if (required.size === 0) return true;
6822
+ const present = new Set(
6823
+ snapshot.observations.map((observation) => observation.spanId)
6824
+ );
6825
+ for (const id of required) {
6826
+ if (!present.has(id)) return false;
6827
+ }
6828
+ return true;
6829
+ }
6830
+ function countMissingSpanIds(snapshot, required) {
6831
+ if (required.size === 0) return 0;
6832
+ const present = new Set(
6833
+ snapshot.observations.map((observation) => observation.spanId)
6834
+ );
6835
+ let missing = 0;
6836
+ for (const id of required) {
6837
+ if (!present.has(id)) missing++;
6838
+ }
6839
+ return missing;
6840
+ }
6141
6841
  async function executeAdapterRun(loaded, options, rawTrace, continueWith) {
6142
6842
  const {
6143
6843
  task: task2,
@@ -6272,7 +6972,7 @@ async function executeAdapterRun(loaded, options, rawTrace, continueWith) {
6272
6972
  endpoint: process.env.AGENT_TASK_TRACE_ENDPOINT
6273
6973
  })
6274
6974
  );
6275
- tee.injectObservations(replay.observations);
6975
+ tee.injectObservations(replay.observations, { remote: replay.exported });
6276
6976
  }
6277
6977
  return await continueWith({
6278
6978
  collected,
@@ -6280,7 +6980,8 @@ async function executeAdapterRun(loaded, options, rawTrace, continueWith) {
6280
6980
  runConfiguration,
6281
6981
  replay,
6282
6982
  trace: trace2,
6283
- getSnapshot: () => tee.getSnapshot()
6983
+ getSnapshot: () => tee.getSnapshot(),
6984
+ getExportedSpanIds: () => tee.getExportedSpanIds()
6284
6985
  });
6285
6986
  } catch (error) {
6286
6987
  throw withRunConfiguration(error, runConfiguration);
@@ -6333,6 +7034,7 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
6333
7034
  async () => {
6334
7035
  const judgeConfig = resolveJudgeConfig(options?.judge, task2.judge);
6335
7036
  const judgeTools = resolveJudgeToolsForTask(options?.judgeTools, task2.judgeTools, absoluteDir);
7037
+ const embeddingRuntime = buildEmbeddingRuntime(options?.embedding, task2.embedding);
6336
7038
  const historyPlane = await freezeHistoryPlaneFromEnv(task2.id);
6337
7039
  if (!inlineChecks) {
6338
7040
  return loadAndRunFlowChecks(
@@ -6345,7 +7047,8 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
6345
7047
  ...judgeConfig ? { judgeConfig } : {},
6346
7048
  ...judgeTools ? { judgeTools } : {},
6347
7049
  judgeTracer: trace2,
6348
- ...historyPlane ? { historyPlane } : {}
7050
+ ...historyPlane ? { historyPlane } : {},
7051
+ ...embeddingRuntime ? { embedding: embeddingRuntime } : {}
6349
7052
  },
6350
7053
  validationResults.brokenDeliverables
6351
7054
  );
@@ -6362,6 +7065,7 @@ async function executeLoadedTask(loaded, options, rawTrace, traceRunId) {
6362
7065
  ...judgeTools ? { judgeTools } : {},
6363
7066
  judgeTracer: trace2,
6364
7067
  ...historyPlane ? { historyPlane } : {},
7068
+ ...embeddingRuntime ? { embedding: embeddingRuntime } : {},
6365
7069
  moduleUrl,
6366
7070
  displayFile: evalFileName
6367
7071
  });
@@ -6385,11 +7089,9 @@ async function captureExecution(loaded, options, rawTrace) {
6385
7089
  collected: outcome.collected,
6386
7090
  transcriptTurns: outcome.transcriptTurns,
6387
7091
  runConfiguration: outcome.runConfiguration,
6388
- // Freeze the snapshot before the root span ends — Phase 2 reads it.
7092
+ // Freeze the snapshot before the root span ends: Phase 2 reads it.
6389
7093
  snapshot: outcome.getSnapshot(),
6390
- // The floor for the canonical read-back: everything the local snapshot
6391
- // holds (runner spans + replayed observations) should project remotely.
6392
- ...outcome.replay ? { replayObservationFloor: outcome.getSnapshot().observations.length } : {}
7094
+ exportedSpanIds: outcome.getExportedSpanIds()
6393
7095
  }));
6394
7096
  }
6395
7097
  async function evaluate(loaded, options, phase1, judgeTracer) {
@@ -6410,9 +7112,9 @@ async function evaluate(loaded, options, phase1, judgeTracer) {
6410
7112
  );
6411
7113
  const judgeConfig = resolveJudgeConfig(options?.judge, task2.judge);
6412
7114
  const judgeTools = resolveJudgeToolsForTask(options?.judgeTools, task2.judgeTools, absoluteDir);
7115
+ const embeddingRuntime = buildEmbeddingRuntime(options?.embedding, task2.embedding);
6413
7116
  const historyPlane = await freezeHistoryPlaneFromEnv(task2.id);
6414
7117
  const runChecks = async () => inlineChecks ? runTraceChecks({
6415
- ...judgeTools ? { judgeTools } : {},
6416
7118
  snapshot: phase1.snapshot,
6417
7119
  deliverables,
6418
7120
  files,
@@ -6421,6 +7123,7 @@ async function evaluate(loaded, options, phase1, judgeTracer) {
6421
7123
  ...judgeTools ? { judgeTools } : {},
6422
7124
  ...judgeTracer ? { judgeTracer } : {},
6423
7125
  ...historyPlane ? { historyPlane } : {},
7126
+ ...embeddingRuntime ? { embedding: embeddingRuntime } : {},
6424
7127
  moduleUrl,
6425
7128
  displayFile: evalFileName
6426
7129
  }) : loadAndRunFlowChecks(
@@ -6433,7 +7136,8 @@ async function evaluate(loaded, options, phase1, judgeTracer) {
6433
7136
  ...judgeConfig ? { judgeConfig } : {},
6434
7137
  ...judgeTools ? { judgeTools } : {},
6435
7138
  ...judgeTracer ? { judgeTracer } : {},
6436
- ...historyPlane ? { historyPlane } : {}
7139
+ ...historyPlane ? { historyPlane } : {},
7140
+ ...embeddingRuntime ? { embedding: embeddingRuntime } : {}
6437
7141
  },
6438
7142
  validationResults.brokenDeliverables
6439
7143
  );
@@ -6993,13 +7697,23 @@ function createOtelAgentTaskTraceClient(config) {
6993
7697
  if (params.provider) {
6994
7698
  span.setAttribute("gen_ai.provider.name", params.provider);
6995
7699
  }
7700
+ if (params.api_provider) {
7701
+ span.setAttribute("apo.llm.api_provider", params.api_provider);
7702
+ }
7703
+ if (params.serving_host) {
7704
+ span.setAttribute("apo.llm.serving_host", params.serving_host);
7705
+ }
7706
+ if (params.observation_type === "GENERATION") {
7707
+ span.setAttribute("apo.llm.request_kind", "request");
7708
+ }
6996
7709
  if (params.route) {
6997
7710
  span.setAttribute("apo.llm.route", params.route);
6998
7711
  }
6999
7712
  const isTool = params.observation_type === "TOOL";
7713
+ const usesToolChannels = isTool || params.observation_type === "RETRIEVER";
7000
7714
  if (params.input) {
7001
- if (isTool) {
7002
- if (params.tool_name) {
7715
+ if (usesToolChannels) {
7716
+ if (isTool && params.tool_name) {
7003
7717
  span.setAttribute("gen_ai.tool.name", params.tool_name);
7004
7718
  }
7005
7719
  span.setAttribute("gen_ai.tool.call.arguments", JSON.stringify(params.input));
@@ -7033,7 +7747,7 @@ function createOtelAgentTaskTraceClient(config) {
7033
7747
  const latency = params.latency_ms ?? round32(monotonicNowMs2() - active.startedAt);
7034
7748
  active.span.setAttribute("latency_ms", latency);
7035
7749
  if (params.output !== void 0) {
7036
- if (active.observationType === "TOOL") {
7750
+ if (active.observationType === "TOOL" || active.observationType === "RETRIEVER") {
7037
7751
  const payload = params.output && typeof params.output === "object" ? params.output : { value: params.output };
7038
7752
  active.span.setAttribute("gen_ai.tool.call.result", JSON.stringify(payload));
7039
7753
  } else if (params.output.text) {
@@ -7092,6 +7806,12 @@ function createOtelAgentTaskTraceClient(config) {
7092
7806
  if (params.provider) {
7093
7807
  active.span.setAttribute("gen_ai.provider.name", params.provider);
7094
7808
  }
7809
+ if (params.serving_host) {
7810
+ active.span.setAttribute("apo.llm.serving_host", params.serving_host);
7811
+ }
7812
+ if (params.api_provider) {
7813
+ active.span.setAttribute("apo.llm.api_provider", params.api_provider);
7814
+ }
7095
7815
  if (params.route) {
7096
7816
  active.span.setAttribute("apo.llm.route", params.route);
7097
7817
  }
@@ -7128,6 +7848,9 @@ function createOtelAgentTaskTraceClient(config) {
7128
7848
  if (params.tags?.length) {
7129
7849
  rootSpan.setAttribute("apo.run.tags", JSON.stringify(params.tags));
7130
7850
  }
7851
+ if (params.environment) {
7852
+ rootSpan.setAttribute("apo.run.environment", params.environment);
7853
+ }
7131
7854
  const rootMeta = params.rootSpan?.metadata;
7132
7855
  const taskRunId = params.taskRunId ?? rootMeta?.["apo.task.run.id"] ?? params.run_metadata?.["agent_task_run_id"];
7133
7856
  if (taskRunId) {
@@ -7423,15 +8146,55 @@ async function loadTaskRuntime(_taskDir) {
7423
8146
  judge: resolveJudgeFromEnv(),
7424
8147
  // Env/file layer for MCP evidence servers (APO_JUDGE_MCP) — seeds the
7425
8148
  // run-level config, so explicit runTask({ judgeTools }) always wins.
7426
- judgeTools: await resolveJudgeToolsFromEnv()
8149
+ judgeTools: await resolveJudgeToolsFromEnv(),
8150
+ embedding: resolveEmbeddingFromEnv()
7427
8151
  };
7428
8152
  }
8153
+ function resolveEmbeddingFromEnv() {
8154
+ const model = process.env.APO_EMBEDDING_MODEL?.trim();
8155
+ if (model) {
8156
+ return {
8157
+ model,
8158
+ ...process.env.APO_EMBEDDING_BASE_URL ? { baseURL: process.env.APO_EMBEDDING_BASE_URL } : {},
8159
+ ...process.env.APO_EMBEDDING_API_KEY ? { apiKey: process.env.APO_EMBEDDING_API_KEY } : {},
8160
+ ...process.env.APO_EMBEDDING_DIMENSIONS ? { dimensions: parseDimensions(process.env.APO_EMBEDDING_DIMENSIONS) } : {}
8161
+ };
8162
+ }
8163
+ if (process.env.OPENROUTER_API_KEY) {
8164
+ return {
8165
+ model: "openai/text-embedding-3-small",
8166
+ // The endpoint is named here, never defaulted at request time — a
8167
+ // host is only ever contacted because a layer resolved it.
8168
+ baseURL: process.env.OPENROUTER_BASE_URL ?? "https://openrouter.ai/api/v1",
8169
+ apiKey: process.env.OPENROUTER_API_KEY
8170
+ };
8171
+ }
8172
+ if (process.env.OPENAI_API_KEY) {
8173
+ return {
8174
+ model: "text-embedding-3-small",
8175
+ apiKey: process.env.OPENAI_API_KEY,
8176
+ baseURL: process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1"
8177
+ };
8178
+ }
8179
+ return void 0;
8180
+ }
8181
+ function parseDimensions(raw) {
8182
+ const parsed = Number.parseInt(raw, 10);
8183
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
8184
+ }
7429
8185
  function resolveJudgeFromEnv() {
7430
- const openRouterModel = process.env.OPENROUTER_MODEL;
7431
- const openAIModel = process.env.OPENAI_MODEL;
7432
- const anthropicModel = process.env.ANTHROPIC_MODEL;
7433
- const model = openRouterModel ?? openAIModel ?? anthropicModel;
8186
+ const judgeOverride = firstEnvValue("AGENT_TASK_JUDGE_MODEL");
8187
+ const openRouterModel = firstEnvValue("AGENT_TASK_OPENROUTER_MODEL", "OPENROUTER_MODEL");
8188
+ const openAIModel = firstEnvValue("OPENAI_MODEL");
8189
+ const anthropicModel = firstEnvValue("ANTHROPIC_MODEL", "AGENT_TASK_ANTHROPIC_MODEL");
8190
+ const model = judgeOverride ?? openRouterModel ?? openAIModel ?? anthropicModel;
7434
8191
  if (!model) return void 0;
8192
+ if (judgeOverride && !openRouterModel && !openAIModel && !anthropicModel) {
8193
+ return {
8194
+ model,
8195
+ ...process.env.APO_JUDGE_MODE?.trim() === "cascade" ? { mode: "cascade" } : {}
8196
+ };
8197
+ }
7435
8198
  return {
7436
8199
  model,
7437
8200
  baseURL: openRouterModel ? process.env.OPENROUTER_BASE_URL : openAIModel ? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1" : process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com",
@@ -7452,6 +8215,13 @@ function resolveJudgeFromEnv() {
7452
8215
  ...process.env.APO_JUDGE_MODE?.trim() === "cascade" ? { mode: "cascade" } : {}
7453
8216
  };
7454
8217
  }
8218
+ function firstEnvValue(...names) {
8219
+ for (const name of names) {
8220
+ const value = process.env[name]?.trim();
8221
+ if (value) return value;
8222
+ }
8223
+ return void 0;
8224
+ }
7455
8225
  async function runTaskDir(taskDir, options) {
7456
8226
  const [loaded, runtime] = await Promise.all([
7457
8227
  loadTask(taskDir),
@@ -7737,14 +8507,17 @@ export {
7737
8507
  compactChecksForSubmission,
7738
8508
  connectMcpServers,
7739
8509
  contentSha256,
8510
+ cosineSimilarity,
7740
8511
  createApoAnthropic,
7741
8512
  createApoOpenAI,
7742
8513
  createApoTracer,
8514
+ createEmbeddingClient,
7743
8515
  defineAdapter,
7744
8516
  defineTask,
7745
8517
  describe2 as describe,
7746
8518
  detectTranscriptSource,
7747
8519
  discoverAgentTaskDirs,
8520
+ embeddingSettingsSignature,
7748
8521
  equals,
7749
8522
  evaluateComputeExpression,
7750
8523
  expandSecretPlaceholders,
@@ -7776,6 +8549,7 @@ export {
7776
8549
  resetTaskRegistry,
7777
8550
  resetTaskSteers,
7778
8551
  resetTaskTurn,
8552
+ resolveEmbeddingConfig,
7779
8553
  resolveMcpServerPaths,
7780
8554
  resolveOtlpTracesUrl,
7781
8555
  resolveTurn,