@attest-ai/core 1.0.0-rc.4 → 1.0.0-rc.5

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/esm/index.js CHANGED
@@ -219,8 +219,8 @@ function extractId(response) {
219
219
  }
220
220
 
221
221
  // src/version.ts
222
- var VERSION = "1.0.0-rc.4";
223
- var ENGINE_VERSION = "1.0.0-rc.4";
222
+ var VERSION = "1.0.0-rc.5";
223
+ var ENGINE_VERSION = "1.0.0-rc.5";
224
224
 
225
225
  // src/engine-manager.ts
226
226
  import { spawn, spawnSync } from "child_process";
@@ -288,7 +288,7 @@ function httpsGet(url, redirectCount = 0) {
288
288
  new Error(`Too many redirects (>${MAX_REDIRECTS}) following ${url}`)
289
289
  );
290
290
  }
291
- return new Promise((resolve4, reject) => {
291
+ return new Promise((resolve5, reject) => {
292
292
  const client = url.startsWith("https:") ? https : http;
293
293
  client.get(url, (res) => {
294
294
  const status = res.statusCode ?? 0;
@@ -299,7 +299,7 @@ function httpsGet(url, redirectCount = 0) {
299
299
  return;
300
300
  }
301
301
  res.resume();
302
- resolve4(httpsGet(location, redirectCount + 1));
302
+ resolve5(httpsGet(location, redirectCount + 1));
303
303
  return;
304
304
  }
305
305
  if (status < 200 || status >= 300) {
@@ -309,7 +309,7 @@ function httpsGet(url, redirectCount = 0) {
309
309
  }
310
310
  const chunks = [];
311
311
  res.on("data", (chunk) => chunks.push(chunk));
312
- res.on("end", () => resolve4(Buffer.concat(chunks)));
312
+ res.on("end", () => resolve5(Buffer.concat(chunks)));
313
313
  res.on("error", reject);
314
314
  }).on("error", reject);
315
315
  });
@@ -536,11 +536,11 @@ var EngineManager = class {
536
536
  }
537
537
  this.requestId += 1;
538
538
  const requestStr = encodeRequest(this.requestId, method, params);
539
- return new Promise((resolve4, reject) => {
539
+ return new Promise((resolve5, reject) => {
540
540
  const onLine = (line) => {
541
541
  try {
542
542
  const response = decodeResponse(line);
543
- resolve4(extractResult(response));
543
+ resolve5(extractResult(response));
544
544
  } catch (err) {
545
545
  reject(err);
546
546
  }
@@ -907,12 +907,12 @@ var AttestClient = class {
907
907
  throw new Error("Engine process not started.");
908
908
  }
909
909
  let capturedReqId;
910
- const requestPromise = new Promise((resolve4, reject) => {
910
+ const requestPromise = new Promise((resolve5, reject) => {
911
911
  this.writeChain = this.writeChain.then(() => {
912
912
  this.requestId += 1;
913
913
  const reqId = this.requestId;
914
914
  capturedReqId = reqId;
915
- this.pending.set(reqId, { resolve: resolve4, reject });
915
+ this.pending.set(reqId, { resolve: resolve5, reject });
916
916
  const requestStr = encodeRequest(reqId, method, params);
917
917
  cp.stdin.write(requestStr, (err) => {
918
918
  if (err) {
@@ -1027,6 +1027,26 @@ function requireSimulationCapability(init) {
1027
1027
 
1028
1028
  // src/trace.ts
1029
1029
  import { randomUUID } from "crypto";
1030
+
1031
+ // src/simulation/context.ts
1032
+ import { AsyncLocalStorage } from "async_hooks";
1033
+ var activeMockRegistry = new AsyncLocalStorage();
1034
+ function createRng(seed) {
1035
+ if (seed === void 0) return Math.random;
1036
+ let state = seed >>> 0;
1037
+ return () => {
1038
+ state = state + 1831565813 >>> 0;
1039
+ let t = state;
1040
+ t = Math.imul(t ^ t >>> 15, t | 1);
1041
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1042
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1043
+ };
1044
+ }
1045
+ function sleep(ms) {
1046
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
1047
+ }
1048
+
1049
+ // src/trace.ts
1030
1050
  var TraceBuilder = class {
1031
1051
  traceId;
1032
1052
  agentId;
@@ -1058,11 +1078,22 @@ var TraceBuilder = class {
1058
1078
  return this;
1059
1079
  }
1060
1080
  addToolCall(name, options2) {
1081
+ const mock = activeMockRegistry.getStore()?.get(name);
1082
+ let result = options2?.result;
1083
+ if (mock !== void 0) {
1084
+ const mocked = mock(options2?.args ?? {});
1085
+ if (typeof mocked?.then === "function") {
1086
+ throw new Error(`mock for tool '${name}' returned a Promise; mock tools must be synchronous`);
1087
+ }
1088
+ if (typeof mocked === "object" && mocked !== null && !Array.isArray(mocked)) {
1089
+ result = mocked;
1090
+ }
1091
+ }
1061
1092
  this.steps.push({
1062
1093
  type: STEP_TOOL_CALL,
1063
1094
  name,
1064
1095
  args: options2?.args,
1065
- result: options2?.result,
1096
+ result,
1066
1097
  metadata: options2?.metadata
1067
1098
  });
1068
1099
  return this;
@@ -2717,18 +2748,18 @@ function serve(engine, args) {
2717
2748
  let stdout = "";
2718
2749
  let stderr = "";
2719
2750
  let resolveReady = () => void 0;
2720
- const ready = new Promise((resolve4) => {
2721
- resolveReady = resolve4;
2751
+ const ready = new Promise((resolve5) => {
2752
+ resolveReady = resolve5;
2722
2753
  });
2723
- const exited = new Promise((resolve4) => {
2754
+ const exited = new Promise((resolve5) => {
2724
2755
  child.on("close", (code, signal) => {
2725
2756
  resolveReady(null);
2726
- resolve4({ code, signal });
2757
+ resolve5({ code, signal });
2727
2758
  });
2728
2759
  child.on("error", (err) => {
2729
2760
  stderr += err.message;
2730
2761
  resolveReady(null);
2731
- resolve4({ code: null, signal: null });
2762
+ resolve5({ code: null, signal: null });
2732
2763
  });
2733
2764
  });
2734
2765
  child.stdout.setEncoding("utf8").on("data", (chunk) => {
@@ -2985,18 +3016,18 @@ async function start(name, mode, opts) {
2985
3016
  let stdout = "";
2986
3017
  let stderr = "";
2987
3018
  let resolveReady = () => void 0;
2988
- const ready = new Promise((resolve4) => {
2989
- resolveReady = resolve4;
3019
+ const ready = new Promise((resolve5) => {
3020
+ resolveReady = resolve5;
2990
3021
  });
2991
- const exited = new Promise((resolve4) => {
3022
+ const exited = new Promise((resolve5) => {
2992
3023
  child.on("close", (code) => {
2993
3024
  resolveReady(null);
2994
- resolve4(code);
3025
+ resolve5(code);
2995
3026
  });
2996
3027
  child.on("error", (err) => {
2997
3028
  stderr += err.message;
2998
3029
  resolveReady(null);
2999
- resolve4(null);
3030
+ resolve5(null);
3000
3031
  });
3001
3032
  });
3002
3033
  child.stdout.setEncoding("utf8").on("data", (chunk) => {
@@ -3121,18 +3152,18 @@ async function start2(file, opts) {
3121
3152
  let stdout = "";
3122
3153
  let stderr = "";
3123
3154
  let resolveReady = () => void 0;
3124
- const ready = new Promise((resolve4) => {
3125
- resolveReady = resolve4;
3155
+ const ready = new Promise((resolve5) => {
3156
+ resolveReady = resolve5;
3126
3157
  });
3127
- const exited = new Promise((resolve4) => {
3158
+ const exited = new Promise((resolve5) => {
3128
3159
  child.on("close", (code) => {
3129
3160
  resolveReady(null);
3130
- resolve4(code);
3161
+ resolve5(code);
3131
3162
  });
3132
3163
  child.on("error", (err) => {
3133
3164
  stderr += err.message;
3134
3165
  resolveReady(null);
3135
- resolve4(null);
3166
+ resolve5(null);
3136
3167
  });
3137
3168
  });
3138
3169
  child.stdout.setEncoding("utf8").on("data", (chunk) => {
@@ -3213,8 +3244,8 @@ function parseFinal(out) {
3213
3244
  }
3214
3245
 
3215
3246
  // src/context.ts
3216
- import { AsyncLocalStorage } from "async_hooks";
3217
- var activeBuilder = new AsyncLocalStorage();
3247
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
3248
+ var activeBuilder = new AsyncLocalStorage2();
3218
3249
 
3219
3250
  // src/agent.ts
3220
3251
  var Agent = class {
@@ -3301,6 +3332,9 @@ function tier(level) {
3301
3332
  }
3302
3333
 
3303
3334
  // src/plugins.ts
3335
+ import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "fs";
3336
+ import { dirname as dirname2, join as join4, relative as relative2, resolve as resolve4, isAbsolute, sep as sep2 } from "path";
3337
+ import { pathToFileURL } from "url";
3304
3338
  var PluginRegistry = class {
3305
3339
  plugins = /* @__PURE__ */ new Map();
3306
3340
  register(name, plugin) {
@@ -3331,10 +3365,19 @@ var PluginRegistry = class {
3331
3365
  };
3332
3366
  async function executePluginAssertion(plugin, trace, spec, client, traceId2, assertionId2, timeoutMs = 3e4) {
3333
3367
  const resultPromise = Promise.resolve(plugin.execute(trace, spec));
3368
+ let timer;
3334
3369
  const timeoutPromise = new Promise((_, reject) => {
3335
- setTimeout(() => reject(new Error(`Plugin '${plugin.name}' timed out after ${timeoutMs}ms`)), timeoutMs);
3370
+ timer = setTimeout(
3371
+ () => reject(new Error(`Plugin '${plugin.name}' timed out after ${timeoutMs}ms`)),
3372
+ timeoutMs
3373
+ );
3336
3374
  });
3337
- const result = await Promise.race([resultPromise, timeoutPromise]);
3375
+ let result;
3376
+ try {
3377
+ result = await Promise.race([resultPromise, timeoutPromise]);
3378
+ } finally {
3379
+ clearTimeout(timer);
3380
+ }
3338
3381
  await client.submitPluginResult(
3339
3382
  traceId2,
3340
3383
  plugin.name,
@@ -3345,6 +3388,100 @@ async function executePluginAssertion(plugin, trace, spec, client, traceId2, ass
3345
3388
  );
3346
3389
  return result;
3347
3390
  }
3391
+ function registerPlugin(registry, name, plugin) {
3392
+ registry.register(name, plugin);
3393
+ }
3394
+ function isAttestPlugin(value) {
3395
+ if (typeof value !== "object" || value === null) return false;
3396
+ const v = value;
3397
+ return typeof v["name"] === "string" && typeof v["pluginType"] === "string" && typeof v["execute"] === "function";
3398
+ }
3399
+ function readJson(path5) {
3400
+ return JSON.parse(readFileSync2(path5, "utf8"));
3401
+ }
3402
+ function findInstalledPackage(projectRoot, name) {
3403
+ let dir = resolve4(projectRoot);
3404
+ for (; ; ) {
3405
+ const candidate = join4(dir, "node_modules", name);
3406
+ if (existsSync2(join4(candidate, "package.json"))) return candidate;
3407
+ const parent = dirname2(dir);
3408
+ if (parent === dir) return void 0;
3409
+ dir = parent;
3410
+ }
3411
+ }
3412
+ function declaredPluginPaths(pkgJson, dep) {
3413
+ const attest = pkgJson["attest"];
3414
+ if (attest === void 0) return [];
3415
+ const plugins = typeof attest === "object" && attest !== null ? attest["plugins"] : void 0;
3416
+ if (plugins === void 0) return [];
3417
+ if (!Array.isArray(plugins) || !plugins.every((p) => typeof p === "string")) {
3418
+ throw new Error(`'${dep}': "attest.plugins" must be an array of strings`);
3419
+ }
3420
+ return plugins;
3421
+ }
3422
+ function pluginsOf(mod) {
3423
+ const def = mod["default"];
3424
+ const nested = typeof def === "object" && def !== null ? def : void 0;
3425
+ for (const candidate of [def, nested?.["default"]]) {
3426
+ if (isAttestPlugin(candidate)) return [candidate];
3427
+ }
3428
+ return [.../* @__PURE__ */ new Set([...Object.values(mod), ...Object.values(nested ?? {})])].filter(isAttestPlugin);
3429
+ }
3430
+ function isInside(root, file) {
3431
+ const within = (r, f) => {
3432
+ const rel = relative2(r, f);
3433
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
3434
+ };
3435
+ return within(root, file) && within(realpathSync(root), realpathSync(file));
3436
+ }
3437
+ async function loadPackagePlugins(registry, projectRoot = process.cwd()) {
3438
+ let project;
3439
+ try {
3440
+ project = readJson(join4(projectRoot, "package.json"));
3441
+ } catch (err) {
3442
+ console.error(`attest: cannot read ${join4(projectRoot, "package.json")}; no plugins loaded:`, err);
3443
+ return 0;
3444
+ }
3445
+ const depNames = /* @__PURE__ */ new Set();
3446
+ for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
3447
+ const deps = project[field];
3448
+ if (typeof deps === "object" && deps !== null) {
3449
+ for (const dep of Object.keys(deps)) depNames.add(dep);
3450
+ }
3451
+ }
3452
+ let loaded = 0;
3453
+ for (const dep of depNames) {
3454
+ const pkgDir = findInstalledPackage(projectRoot, dep);
3455
+ if (pkgDir === void 0) continue;
3456
+ let entries;
3457
+ try {
3458
+ entries = declaredPluginPaths(readJson(join4(pkgDir, "package.json")), dep);
3459
+ } catch (err) {
3460
+ console.error(`attest: failed to read plugins declared by '${dep}':`, err);
3461
+ continue;
3462
+ }
3463
+ for (const entry of entries) {
3464
+ try {
3465
+ const file = resolve4(pkgDir, entry);
3466
+ if (!isInside(pkgDir, file)) {
3467
+ throw new Error(`plugin path '${entry}' escapes package '${dep}'`);
3468
+ }
3469
+ const mod = await import(pathToFileURL(file).href);
3470
+ const plugins = pluginsOf(mod);
3471
+ if (plugins.length === 0) {
3472
+ throw new Error(`'${entry}' exports no plugin (name, pluginType, execute)`);
3473
+ }
3474
+ for (const plugin of plugins) {
3475
+ registry.register(plugin.name, plugin);
3476
+ loaded++;
3477
+ }
3478
+ } catch (err) {
3479
+ console.error(`attest: failed to load plugin '${entry}' from '${dep}':`, err);
3480
+ }
3481
+ }
3482
+ }
3483
+ return loaded;
3484
+ }
3348
3485
 
3349
3486
  // src/continuous.ts
3350
3487
  import { EventEmitter } from "events";
@@ -3724,6 +3861,172 @@ the assistant's response meets your needs. Keep responses concise (1-3 sentences
3724
3861
  max_tokens: 200
3725
3862
  };
3726
3863
 
3864
+ // src/simulation/mock-tools.ts
3865
+ var MockToolRegistry = class {
3866
+ tools = /* @__PURE__ */ new Map();
3867
+ register(name, fn) {
3868
+ this.tools.set(name, fn);
3869
+ }
3870
+ /**
3871
+ * Run `fn` with the registered mocks active. `TraceBuilder.addToolCall`
3872
+ * returns the mock's result for a registered tool name. Scopes are
3873
+ * isolated per async call chain (AsyncLocalStorage).
3874
+ */
3875
+ run(fn) {
3876
+ return activeMockRegistry.run(new Map(this.tools), fn);
3877
+ }
3878
+ };
3879
+ function mockTool(name, fn) {
3880
+ const wrapper = ((args) => fn(args));
3881
+ return Object.assign(wrapper, { mockToolName: name });
3882
+ }
3883
+
3884
+ // src/simulation/fault-inject.ts
3885
+ function faultInject(options2, fn) {
3886
+ const errorRate = options2.errorRate ?? 0;
3887
+ const latencyJitterMs = options2.latencyJitterMs ?? 0;
3888
+ const rng = createRng(options2.seed);
3889
+ return async (...args) => {
3890
+ if (errorRate > 0 && rng() < errorRate) {
3891
+ throw new Error(`Injected fault in ${fn.name || "anonymous"}`);
3892
+ }
3893
+ if (latencyJitterMs > 0) {
3894
+ await sleep(rng() * latencyJitterMs);
3895
+ }
3896
+ return fn(...args);
3897
+ };
3898
+ }
3899
+
3900
+ // src/simulation/verdict.ts
3901
+ function aggregateOutcomes(outcomes) {
3902
+ if (outcomes.includes(OUTCOME_FAIL)) return OUTCOME_FAIL;
3903
+ if (outcomes.includes(OUTCOME_SKIP)) return OUTCOME_SKIP;
3904
+ return OUTCOME_PASS;
3905
+ }
3906
+ function sumNotEvaluatedReasons(results) {
3907
+ const counts = {};
3908
+ for (const r of results) {
3909
+ for (const [reason, n] of Object.entries(r.notEvaluatedReasons)) {
3910
+ counts[reason] = (counts[reason] ?? 0) + n;
3911
+ }
3912
+ }
3913
+ return counts;
3914
+ }
3915
+ function formatSkipReason(counts) {
3916
+ const parts = Object.entries(counts).map(([reason, n]) => n === 1 ? reason : `${reason} \xD7${n}`);
3917
+ return `not evaluated: ${parts.join(", ")}`;
3918
+ }
3919
+ function renderVerdictDiagnostics(verdict, color = false) {
3920
+ return verdict instanceof AgentResult ? renderDiagnostics(verdict, { color }) : verdict.renderDiagnostics?.({ color }) ?? "";
3921
+ }
3922
+ function verdictsDiagnostics(results, label, color = false) {
3923
+ const blocks = results.flatMap(
3924
+ (r, i) => r.outcome() === OUTCOME_PASS ? [] : [`${label} ${i + 1}/${results.length}:${renderVerdictDiagnostics(r, color)}`]
3925
+ );
3926
+ return blocks.length > 0 ? blocks.join("\n") : ` (no failing ${label}s)
3927
+ `;
3928
+ }
3929
+
3930
+ // src/simulation/repeat.ts
3931
+ var RepeatResult = class {
3932
+ results;
3933
+ constructor(results = []) {
3934
+ this.results = results;
3935
+ }
3936
+ get count() {
3937
+ return this.results.length;
3938
+ }
3939
+ /** Trials skipped because an assertion was not evaluated. */
3940
+ get notEvaluated() {
3941
+ return this.results.filter((r) => r.outcome() === OUTCOME_SKIP).length;
3942
+ }
3943
+ /** Passing / evaluated trials; null when no trial was evaluated. */
3944
+ get passRate() {
3945
+ const evaluated = this.count - this.notEvaluated;
3946
+ if (evaluated === 0) return null;
3947
+ return this.results.filter((r) => r.outcome() === OUTCOME_PASS).length / evaluated;
3948
+ }
3949
+ /** True when every trial's outcome is pass (soft failures included). */
3950
+ get allPassed() {
3951
+ return this.results.every((r) => r.outcome() === OUTCOME_PASS);
3952
+ }
3953
+ outcome(options2) {
3954
+ return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
3955
+ }
3956
+ get notEvaluatedReasons() {
3957
+ return sumNotEvaluatedReasons(this.results);
3958
+ }
3959
+ skipReason() {
3960
+ return formatSkipReason(this.notEvaluatedReasons);
3961
+ }
3962
+ renderDiagnostics(options2) {
3963
+ return verdictsDiagnostics(this.results, "trial", options2?.color);
3964
+ }
3965
+ };
3966
+ function repeat(n, fn) {
3967
+ return async (...args) => {
3968
+ const results = [];
3969
+ for (let i = 0; i < n; i++) {
3970
+ results.push(await fn(...args));
3971
+ }
3972
+ return new RepeatResult(results);
3973
+ };
3974
+ }
3975
+
3976
+ // src/simulation/scenario.ts
3977
+ var ScenarioResult = class {
3978
+ config;
3979
+ results;
3980
+ constructor(config2, results = []) {
3981
+ this.config = config2;
3982
+ this.results = results;
3983
+ }
3984
+ /** True when the aggregate `outcome()` is pass. */
3985
+ get passed() {
3986
+ return this.outcome() === OUTCOME_PASS;
3987
+ }
3988
+ outcome(options2) {
3989
+ return aggregateOutcomes(this.results.map((r) => r.outcome(options2)));
3990
+ }
3991
+ get notEvaluatedReasons() {
3992
+ return sumNotEvaluatedReasons(this.results);
3993
+ }
3994
+ skipReason() {
3995
+ return formatSkipReason(this.notEvaluatedReasons);
3996
+ }
3997
+ renderDiagnostics(options2) {
3998
+ return verdictsDiagnostics(this.results, "turn", options2?.color);
3999
+ }
4000
+ };
4001
+ function scenario(options2, fn) {
4002
+ const config2 = {
4003
+ persona: options2.persona,
4004
+ mockTools: { ...options2.mockTools },
4005
+ faultErrorRate: options2.faultErrorRate ?? 0,
4006
+ faultLatencyJitterMs: options2.faultLatencyJitterMs ?? 0,
4007
+ maxTurns: options2.maxTurns ?? 1,
4008
+ seed: options2.seed
4009
+ };
4010
+ return async (...args) => {
4011
+ const run = async () => {
4012
+ const results = [];
4013
+ const rng = createRng(config2.seed);
4014
+ for (let turn = 0; turn < config2.maxTurns; turn++) {
4015
+ if (config2.faultErrorRate > 0 && rng() < config2.faultErrorRate) {
4016
+ throw new Error(`Injected fault at turn ${turn}`);
4017
+ }
4018
+ if (config2.faultLatencyJitterMs > 0) {
4019
+ await sleep(rng() * config2.faultLatencyJitterMs);
4020
+ }
4021
+ results.push(await fn({ persona: config2.persona, turn }, ...args));
4022
+ }
4023
+ return new ScenarioResult(config2, results);
4024
+ };
4025
+ const mocks = new Map(Object.entries(config2.mockTools));
4026
+ return mocks.size > 0 ? activeMockRegistry.run(mocks, run) : run();
4027
+ };
4028
+ }
4029
+
3727
4030
  // src/adapters/manual.ts
3728
4031
  var ManualAdapter = class {
3729
4032
  agentId;
@@ -3935,7 +4238,7 @@ var OTelAdapter = class _OTelAdapter {
3935
4238
  }
3936
4239
  };
3937
4240
  function runConvert(engine, args, payload) {
3938
- return new Promise((resolve4, reject) => {
4241
+ return new Promise((resolve5, reject) => {
3939
4242
  const child = spawn5(engine, args, { stdio: ["pipe", "pipe", "pipe"] });
3940
4243
  let timer;
3941
4244
  child.on("error", (e) => {
@@ -3988,7 +4291,7 @@ function runConvert(engine, args, payload) {
3988
4291
  );
3989
4292
  return;
3990
4293
  }
3991
- resolve4(Buffer.concat(out).toString("utf8"));
4294
+ resolve5(Buffer.concat(out).toString("utf8"));
3992
4295
  });
3993
4296
  stdin.end(payload);
3994
4297
  });
@@ -4155,6 +4458,7 @@ export {
4155
4458
  GeminiAdapter,
4156
4459
  LangChainAdapter,
4157
4460
  ManualAdapter,
4461
+ MockToolRegistry,
4158
4462
  OTLPConversionError,
4159
4463
  OTelAdapter,
4160
4464
  OUTCOME_FAIL,
@@ -4174,6 +4478,7 @@ export {
4174
4478
  REASON_REPLAY_MISS,
4175
4479
  REASON_SHORT_CIRCUITED,
4176
4480
  REASON_SIMULATION,
4481
+ RepeatResult,
4177
4482
  ReplayError,
4178
4483
  ReplayMissError,
4179
4484
  ReplayNotEvaluatedError,
@@ -4186,6 +4491,7 @@ export {
4186
4491
  STEP_RETRIEVAL,
4187
4492
  STEP_TOOL_CALL,
4188
4493
  Sampler,
4494
+ ScenarioResult,
4189
4495
  TIER_1,
4190
4496
  TIER_2,
4191
4497
  TIER_3,
@@ -4220,6 +4526,7 @@ export {
4220
4526
  executePluginAssertion,
4221
4527
  extractId,
4222
4528
  extractResult,
4529
+ faultInject,
4223
4530
  firstMiss,
4224
4531
  formatUsage,
4225
4532
  getSampleRate,
@@ -4231,17 +4538,23 @@ export {
4231
4538
  loadFixture,
4232
4539
  loadLabelsCSV,
4233
4540
  loadLabelsJSONL,
4541
+ loadPackagePlugins,
4542
+ mockTool,
4234
4543
  parseUsage,
4235
4544
  previewLine,
4236
4545
  promptHash,
4237
4546
  record,
4547
+ registerPlugin,
4238
4548
  renderAssertionFailure,
4239
4549
  renderDiagnostics,
4240
4550
  renderSummary,
4551
+ renderVerdictDiagnostics,
4241
4552
  renderVitest,
4553
+ repeat,
4242
4554
  requireSimulationCapability,
4243
4555
  resetConfig,
4244
4556
  resolveMode,
4557
+ scenario,
4245
4558
  simulationEnvEnabled,
4246
4559
  startReplay,
4247
4560
  startWorld,