@suveren/gateway 0.37.0 → 0.38.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.
@@ -3600,7 +3600,6 @@ function setAsBaseUrl(url) {
3600
3600
  AS_BASE = url.replace(/\/$/, "");
3601
3601
  }
3602
3602
  var OPERATOR_NAME = process.env.SUVEREN_OPERATOR_NAME ?? "Suveren";
3603
- var HAP_URL = "https://www.humanagencyprotocol.org/";
3604
3603
  var FOOTER_RE = /\n*(?:—|--) (?:Sent|Published) by (?:an AI agent|[^\n]+?'s AI agent)[\s\S]*$/;
3605
3604
  function shouldAttachFooter() {
3606
3605
  return true;
@@ -3608,16 +3607,11 @@ function shouldAttachFooter() {
3608
3607
  function receiptUrl(receiptId) {
3609
3608
  return `${AS_BASE}/t/${receiptId}`;
3610
3609
  }
3611
- function hapLine() {
3612
- return `-- ${OPERATOR_NAME} implements HAP, the open Human Agency Protocol: ${HAP_URL}`;
3613
- }
3614
3610
  function footerText(receiptId, verb, subject) {
3615
3611
  const identity = deriveIdentityLine(subject, { operatorName: OPERATOR_NAME }).replace(/^Sent\b/, verb);
3616
3612
  return `
3617
3613
 
3618
- -- ${identity}. Ticket: ${receiptUrl(receiptId)}
3619
-
3620
- ${hapLine()}`;
3614
+ -- ${identity}. Ticket: ${receiptUrl(receiptId)}`;
3621
3615
  }
3622
3616
  function detectContentField(tool) {
3623
3617
  return tool.gating?.message?.text ?? null;
@@ -3650,6 +3644,8 @@ import { getProfile } from "@hap/core";
3650
3644
  // src/lib/tool-gating-types.ts
3651
3645
  var MAX_PREVIEW_READS = 10;
3652
3646
  var MAX_FOREACH_ELEMENTS = 50;
3647
+ var CHECK_STATUSES = ["pass", "fail", "notice", "not_checked"];
3648
+ var MAX_CHECKS_PER_TOOL = 12;
3653
3649
  var MAX_RECORD_KEY_STEPS = 2;
3654
3650
 
3655
3651
  // src/lib/scope-specificity.ts
@@ -3937,8 +3933,28 @@ function invalidReadConfigReason(toolName, idx, entry) {
3937
3933
  if (p.kind !== void 0 && p.kind !== "document") {
3938
3934
  return `${label}.kind must be "document" (the only recognized value)`;
3939
3935
  }
3936
+ if (p.when !== void 0 && (typeof p.when !== "string" || !PLAIN_PATH_RE.test(p.when))) {
3937
+ return `${label}.when must be a dotted argument path (e.g. "uid" or "item_id")`;
3938
+ }
3939
+ if (p.displayOnly !== void 0 && typeof p.displayOnly !== "boolean") {
3940
+ return `${label}.displayOnly must be a boolean`;
3941
+ }
3942
+ if (p.as !== void 0) {
3943
+ if (typeof p.as !== "string" || !/^[A-Za-z_][\w-]*$/.test(p.as)) return `${label}.as must be a plain name`;
3944
+ if (p.attach !== void 0 || p.forEach !== void 0 || p.kind !== void 0) {
3945
+ return `${label}.as cannot be combined with attach, forEach or kind`;
3946
+ }
3947
+ }
3948
+ if (p.label !== void 0 && (typeof p.label !== "string" || p.label.trim().length === 0 || p.label.length > 60)) {
3949
+ return `${label}.label must be a non-empty string of at most 60 characters`;
3950
+ }
3951
+ if (p.displayOnly === true && p.version !== void 0) {
3952
+ return `${label} cannot be both displayOnly and version-checked \u2014 a versioned read is the record the action changes`;
3953
+ }
3940
3954
  return null;
3941
3955
  }
3956
+ var PLAIN_PATH_RE = /^[A-Za-z_][\w-]*(\.[A-Za-z_][\w-]*)*$/;
3957
+ var CHECK_PATH_RE = /^(?:[A-Za-z_][\w-]*(?:\.(?:[A-Za-z_][\w-]*|\$version))*|[A-Za-z_][\w-]*\[\](?:\.[A-Za-z_][\w-]*)+)$/;
3942
3958
  function invalidPreviewReason(toolName, entry) {
3943
3959
  if (entry === null || typeof entry !== "object") return null;
3944
3960
  const preview = entry.preview;
@@ -3990,12 +4006,30 @@ function invalidDisplayHintsReason(toolName, entry) {
3990
4006
  }
3991
4007
  }
3992
4008
  }
4009
+ if (d.compare !== void 0) {
4010
+ if (d.compare === null || typeof d.compare !== "object" || Array.isArray(d.compare)) {
4011
+ return `override "${toolName}".display.compare must be an object mapping arguments to paths`;
4012
+ }
4013
+ for (const [arg, path] of Object.entries(d.compare)) {
4014
+ if (typeof path !== "string" || !PLAIN_PATH_RE.test(path) || !path.includes(".")) {
4015
+ return `override "${toolName}".display.compare.${arg} must be "<attach>.<field>"`;
4016
+ }
4017
+ }
4018
+ }
4019
+ if (d.names !== void 0) {
4020
+ if (d.names === null || typeof d.names !== "object" || Array.isArray(d.names) || !Object.values(d.names).every((v) => typeof v === "string" && PLAIN_PATH_RE.test(v))) {
4021
+ return `override "${toolName}".display.names must map attach keys to dotted paths`;
4022
+ }
4023
+ }
4024
+ if (d.verb !== void 0 && d.verb !== "created" && d.verb !== "changed") {
4025
+ return `override "${toolName}".display.verb must be "created" or "changed"`;
4026
+ }
3993
4027
  if (d.aiText !== void 0 && (typeof d.aiText !== "string" || d.aiText.length === 0)) {
3994
4028
  return `override "${toolName}".display.aiText must be a non-empty string naming an argument`;
3995
4029
  }
3996
4030
  return null;
3997
4031
  }
3998
- var SUMMARY_FORMATS = ["money", "percent", "list", "short", "date"];
4032
+ var SUMMARY_FORMATS = ["money", "percent", "list", "short", "date", "ref"];
3999
4033
  function invalidSummaryReason(toolName, entry) {
4000
4034
  if (entry === null || typeof entry !== "object") return null;
4001
4035
  const summary = entry.summary;
@@ -4029,6 +4063,170 @@ function invalidSummaryReason(toolName, entry) {
4029
4063
  }
4030
4064
  return null;
4031
4065
  }
4066
+ var CHECK_KEYS = /* @__PURE__ */ new Set([
4067
+ "key",
4068
+ "label",
4069
+ "path",
4070
+ "when",
4071
+ "forEach",
4072
+ "pass",
4073
+ "fail",
4074
+ "notice",
4075
+ "notChecked",
4076
+ "compare",
4077
+ "onTrue",
4078
+ "onFalse",
4079
+ "missing",
4080
+ "detail",
4081
+ "details",
4082
+ "blocking",
4083
+ "present",
4084
+ "absent",
4085
+ "showOnly"
4086
+ ]);
4087
+ var CHECK_LISTS = ["pass", "fail", "notice", "notChecked"];
4088
+ var DETAIL_FORMATS = ["money", "percent", "list", "short", "date"];
4089
+ function checkPathReason(at, path, forEach) {
4090
+ if (typeof path !== "string" || !CHECK_PATH_RE.test(path)) return `${at} has an invalid path ${JSON.stringify(path)}`;
4091
+ const b = path.indexOf("[]");
4092
+ if (b >= 0 && path.slice(0, b) !== forEach) return `${at} path "${path}" reads an element but forEach is not "${path.slice(0, b)}"`;
4093
+ return null;
4094
+ }
4095
+ function checkDetailReason(at, detail, forEach) {
4096
+ if (typeof detail !== "string" || detail.trim().length === 0) return `${at}.detail must be a non-empty string`;
4097
+ if (detail.length > 200) return `${at}.detail must be at most 200 characters`;
4098
+ let brace = 0;
4099
+ let bracket = 0;
4100
+ for (const ch of detail) {
4101
+ if (ch === "{") {
4102
+ brace += 1;
4103
+ if (brace > 1) return `${at}.detail has nested { }`;
4104
+ continue;
4105
+ }
4106
+ if (ch === "}") {
4107
+ brace -= 1;
4108
+ if (brace < 0) return `${at}.detail has unbalanced { }`;
4109
+ continue;
4110
+ }
4111
+ if (brace > 0) continue;
4112
+ if (ch === "[") {
4113
+ bracket += 1;
4114
+ if (bracket > 1) return `${at}.detail has nested [ ]`;
4115
+ }
4116
+ if (ch === "]") {
4117
+ bracket -= 1;
4118
+ if (bracket < 0) return `${at}.detail has unbalanced [ ]`;
4119
+ }
4120
+ }
4121
+ if (brace !== 0 || bracket !== 0) return `${at}.detail has unbalanced brackets`;
4122
+ for (const m of detail.matchAll(/\{([^{}]+)\}/g)) {
4123
+ const [path, fmt, extra] = m[1].split("|").map((x) => x.trim());
4124
+ if (extra !== void 0) return `${at}.detail {${m[1]}} has more than one format`;
4125
+ if (path === "#") {
4126
+ if (typeof forEach !== "string") return `${at}.detail uses {#} without forEach`;
4127
+ } else {
4128
+ const r = checkPathReason(`${at}.detail`, path, forEach);
4129
+ if (r) return r;
4130
+ }
4131
+ const isCount = fmt !== void 0 && /^count:[^/{}|]+\/[^/{}|]+$/.test(fmt);
4132
+ if (fmt !== void 0 && !DETAIL_FORMATS.includes(fmt) && !isCount) return `${at}.detail {${m[1]}} has unknown format "${fmt}"`;
4133
+ }
4134
+ return null;
4135
+ }
4136
+ function checkWhenReason(at, when, forEach) {
4137
+ if (when === void 0) return null;
4138
+ if (typeof when === "string") return checkPathReason(`${at}.when`, when, forEach);
4139
+ if (when === null || typeof when !== "object" || Array.isArray(when)) return `${at}.when must be a path or {path, in}`;
4140
+ const w = when;
4141
+ const extra = Object.keys(w).find((k) => k !== "path" && k !== "in");
4142
+ if (extra) return `${at}.when has unknown key "${extra}"`;
4143
+ return checkPathReason(`${at}.when.path`, w.path, forEach) ?? checkValueListReason(`${at}.when.in`, w.in);
4144
+ }
4145
+ function checkValueListReason(at, list) {
4146
+ if (!Array.isArray(list) || list.length === 0) return `${at} must be a non-empty list of values`;
4147
+ const ok = list.every((v) => v === null || ["string", "boolean"].includes(typeof v) || typeof v === "number" && Number.isFinite(v));
4148
+ return ok ? null : `${at} may hold only strings, numbers, booleans or null`;
4149
+ }
4150
+ function invalidCheckReason(at, raw, seen) {
4151
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return `${at} must be an object`;
4152
+ const c = raw;
4153
+ const unknownKey = Object.keys(c).find((k) => !CHECK_KEYS.has(k));
4154
+ if (unknownKey) return `${at} has unknown key "${unknownKey}"`;
4155
+ if (typeof c.key !== "string" || c.key.length === 0) return `${at}.key must be a non-empty string`;
4156
+ if (seen.has(c.key)) return `${at}.key "${c.key}" is declared twice`;
4157
+ seen.add(c.key);
4158
+ if (typeof c.label !== "string" || c.label.trim().length === 0 || c.label.length > 80) {
4159
+ return `${at}.label must be a non-empty string of at most 80 characters`;
4160
+ }
4161
+ if (c.forEach !== void 0 && (typeof c.forEach !== "string" || !/^[A-Za-z_][\w-]*$/.test(c.forEach))) {
4162
+ return `${at}.forEach must name an array argument`;
4163
+ }
4164
+ const pathReason = checkPathReason(`${at}.path`, c.path, c.forEach) ?? checkWhenReason(at, c.when, c.forEach);
4165
+ if (pathReason) return pathReason;
4166
+ const hasList = CHECK_LISTS.some((k) => c[k] !== void 0);
4167
+ const hasCompare = c.compare !== void 0;
4168
+ const hasPresence = c.present !== void 0 || c.absent !== void 0;
4169
+ if ([hasList, hasCompare, hasPresence].filter(Boolean).length !== 1) {
4170
+ return `${at} must declare exactly one form: value lists (pass/fail/notice/notChecked), compare, or present/absent`;
4171
+ }
4172
+ if (hasPresence) {
4173
+ for (const k of ["present", "absent"]) {
4174
+ if (!CHECK_STATUSES.includes(c[k])) return `${at}.${k} must be one of ${CHECK_STATUSES.join(", ")} (both required)`;
4175
+ }
4176
+ }
4177
+ for (const k of CHECK_LISTS) {
4178
+ if (c[k] === void 0) continue;
4179
+ const r = checkValueListReason(`${at}.${k}`, c[k]);
4180
+ if (r) return r;
4181
+ }
4182
+ if (hasCompare) {
4183
+ const cmp = c.compare;
4184
+ if (cmp === null || typeof cmp !== "object" || Array.isArray(cmp)) return `${at}.compare must be an object`;
4185
+ if (!["eq", "ne", "gt", "lt"].includes(cmp.op)) return `${at}.compare.op must be one of eq, ne, gt, lt`;
4186
+ if (typeof cmp.to === "number") {
4187
+ if (!Number.isFinite(cmp.to)) return `${at}.compare.to must be a finite number or a path`;
4188
+ } else {
4189
+ const r = checkPathReason(`${at}.compare.to`, cmp.to, c.forEach);
4190
+ if (r) return r;
4191
+ }
4192
+ const extra = Object.keys(cmp).find((k) => k !== "op" && k !== "to");
4193
+ if (extra) return `${at}.compare has unknown key "${extra}"`;
4194
+ }
4195
+ for (const k of ["onTrue", "onFalse"]) {
4196
+ if (c[k] === void 0) continue;
4197
+ if (!hasCompare) return `${at}.${k} applies to compare only`;
4198
+ if (!CHECK_STATUSES.includes(c[k])) return `${at}.${k} must be one of ${CHECK_STATUSES.join(", ")}`;
4199
+ }
4200
+ if (c.blocking !== void 0 && typeof c.blocking !== "boolean") return `${at}.blocking must be a boolean`;
4201
+ if (c.showOnly !== void 0 && c.showOnly !== "fail" && c.showOnly !== "notice") return `${at}.showOnly must be "fail" or "notice"`;
4202
+ if (c.missing !== void 0 && c.missing !== "skip" && c.missing !== "not_checked") {
4203
+ return `${at}.missing must be "skip" or "not_checked"`;
4204
+ }
4205
+ if (c.details !== void 0) {
4206
+ if (c.details === null || typeof c.details !== "object" || Array.isArray(c.details)) return `${at}.details must map a status to a detail`;
4207
+ for (const [status, d] of Object.entries(c.details)) {
4208
+ if (!CHECK_STATUSES.includes(status)) return `${at}.details.${status} is not a status (${CHECK_STATUSES.join(", ")})`;
4209
+ const r = checkDetailReason(`${at}.details.${status}`, d, c.forEach);
4210
+ if (r) return r;
4211
+ }
4212
+ }
4213
+ if (c.detail !== void 0) return checkDetailReason(at, c.detail, c.forEach);
4214
+ return null;
4215
+ }
4216
+ function invalidChecksReason(toolName, entry) {
4217
+ if (entry === null || typeof entry !== "object") return null;
4218
+ const checks = entry.checks;
4219
+ if (checks === void 0) return null;
4220
+ const base = `override "${toolName}".checks`;
4221
+ if (!Array.isArray(checks) || checks.length === 0) return `${base} must be a non-empty list`;
4222
+ if (checks.length > MAX_CHECKS_PER_TOOL) return `${base} declares ${checks.length} checks \u2014 max ${MAX_CHECKS_PER_TOOL}`;
4223
+ const seen = /* @__PURE__ */ new Set();
4224
+ for (const [i, raw] of checks.entries()) {
4225
+ const reason = invalidCheckReason(`${base}[${i}]`, raw, seen);
4226
+ if (reason) return reason;
4227
+ }
4228
+ return null;
4229
+ }
4032
4230
  function invalidInternalReason(toolName, entry) {
4033
4231
  if (entry === null || typeof entry !== "object") return null;
4034
4232
  const internal = entry.internal;
@@ -4092,7 +4290,7 @@ function invalidManifestMessageReason(manifest) {
4092
4290
  function invalidManifestPreviewReason(manifest) {
4093
4291
  const overrides = manifest.toolGating?.overrides ?? {};
4094
4292
  for (const [toolName, entry] of Object.entries(overrides)) {
4095
- const reason = invalidPreviewReason(toolName, entry) ?? invalidDisplayHintsReason(toolName, entry) ?? invalidSummaryReason(toolName, entry);
4293
+ const reason = invalidPreviewReason(toolName, entry) ?? invalidDisplayHintsReason(toolName, entry) ?? invalidSummaryReason(toolName, entry) ?? invalidChecksReason(toolName, entry);
4096
4294
  if (reason) return reason;
4097
4295
  }
4098
4296
  return null;
@@ -4448,6 +4646,36 @@ function invalidManifestReadGateReason(manifest) {
4448
4646
  }
4449
4647
  return null;
4450
4648
  }
4649
+ function invalidNetworkReason(manifest) {
4650
+ const network = manifest.network;
4651
+ if (network === void 0) return null;
4652
+ if (!Array.isArray(network) || network.length === 0) return "network must be a non-empty list of outside calls";
4653
+ for (const [i, raw] of network.entries()) {
4654
+ const at = `network[${i}]`;
4655
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return `${at} must be an object`;
4656
+ const n = raw;
4657
+ const extra = Object.keys(n).find((k) => !["modes", "host", "purpose", "sends"].includes(k));
4658
+ if (extra) return `${at} has unknown key "${extra}"`;
4659
+ if (typeof n.host !== "string" || !/^(?=.{1,253}$)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+$/.test(n.host)) {
4660
+ return `${at}.host must be a bare host name, e.g. "api.example.com"`;
4661
+ }
4662
+ for (const k of ["purpose", "sends"]) {
4663
+ if (typeof n[k] !== "string" || n[k].trim().length === 0 || n[k].length > 200) {
4664
+ return `${at}.${k} must be a non-empty string of at most 200 characters`;
4665
+ }
4666
+ }
4667
+ if (n.modes !== void 0) {
4668
+ if (n.modes === null || typeof n.modes !== "object" || Array.isArray(n.modes) || Object.keys(n.modes).length === 0) {
4669
+ return `${at}.modes must be an object mapping a mode to true or a list of adapters`;
4670
+ }
4671
+ for (const [mode, v] of Object.entries(n.modes)) {
4672
+ const ok = v === true || Array.isArray(v) && v.length > 0 && v.every((a) => typeof a === "string" && a.length > 0);
4673
+ if (!ok) return `${at}.modes.${mode} must be true or a non-empty list of adapter names`;
4674
+ }
4675
+ }
4676
+ }
4677
+ return null;
4678
+ }
4451
4679
  function invalidModeLabelsReason(manifest) {
4452
4680
  if (!manifest.modeLabels) return null;
4453
4681
  if (manifest.modeLabels === null || typeof manifest.modeLabels !== "object" || Array.isArray(manifest.modeLabels)) {
@@ -4558,6 +4786,11 @@ function loadManifests(integrationsDir) {
4558
4786
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${valuesReason}`);
4559
4787
  continue;
4560
4788
  }
4789
+ const networkReason = invalidNetworkReason(manifest);
4790
+ if (networkReason) {
4791
+ console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${networkReason}`);
4792
+ continue;
4793
+ }
4561
4794
  const modeLabelsReason = invalidModeLabelsReason(manifest) ?? (manifest.modeNotes ? invalidModeLabelsReason({ ...manifest, modeLabels: manifest.modeNotes })?.replace(/modeLabels/g, "modeNotes") ?? null : null);
4562
4795
  if (modeLabelsReason) {
4563
4796
  console.error(`[ManifestLoader] Refusing manifest ${id} (${manifestPath}): ${modeLabelsReason}`);
@@ -4727,6 +4960,241 @@ function candidatesAllowingAttachments(candidates, refs, profileOf) {
4727
4960
 
4728
4961
  // src/lib/preview.ts
4729
4962
  import { computeContentHash } from "@hap/core";
4963
+
4964
+ // src/lib/checks.ts
4965
+ var ABSENT = { ok: false, why: "absent" };
4966
+ var NO_ANSWER = { ok: false, why: "no_answer" };
4967
+ function isPlainRecord(v) {
4968
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4969
+ }
4970
+ function fromOutcome(outcome, rest) {
4971
+ if (!outcome || outcome.status !== "ok") return outcome?.status === "none" ? ABSENT : NO_ANSWER;
4972
+ const body = structuredOrJsonText(outcome.body);
4973
+ if (!body) return NO_ANSWER;
4974
+ const v = digNullAware(body, rest);
4975
+ return v === void 0 ? ABSENT : { ok: true, value: v };
4976
+ }
4977
+ function digNullAware(source, segs) {
4978
+ let cur = source;
4979
+ for (const part of segs) {
4980
+ if (cur === null) return null;
4981
+ if (typeof cur !== "object" || Array.isArray(cur)) return void 0;
4982
+ cur = cur[part];
4983
+ }
4984
+ return cur;
4985
+ }
4986
+ function resolveCheckPath(path, scope2) {
4987
+ const { ctx, reads, element } = scope2;
4988
+ const sections = ctx.sections ?? [];
4989
+ const bracket = path.indexOf("[]");
4990
+ if (bracket >= 0) {
4991
+ const arrayName = path.slice(0, bracket);
4992
+ const segs2 = path.slice(bracket + 3).split(".").filter(Boolean);
4993
+ if (!element || element.forEach !== arrayName || segs2.length === 0) return ABSENT;
4994
+ const readIdx = reads.findIndex((r) => r.forEach === arrayName && r.attach === segs2[0]);
4995
+ if (readIdx >= 0) {
4996
+ return fromOutcome(sections[readIdx]?.elements?.[element.readIndex], segs2.slice(1));
4997
+ }
4998
+ const v2 = valueAtPath(element.value, segs2.join("."));
4999
+ return v2 === void 0 ? ABSENT : { ok: true, value: v2 };
5000
+ }
5001
+ const segs = path.split(".");
5002
+ if (segs.length > 1) {
5003
+ const readIdx = reads.findIndex((r) => !r.forEach && (r.attach === segs[0] || r.as === segs[0]));
5004
+ if (readIdx >= 0 && segs[1] === "$version") {
5005
+ const version = sections[readIdx]?.version;
5006
+ if (!version) return ABSENT;
5007
+ const v2 = valueAtPath({ approved: version.approved, current: version.current, stale: version.stale }, segs.slice(2).join("."));
5008
+ return v2 === void 0 ? ABSENT : { ok: true, value: v2 };
5009
+ }
5010
+ if (readIdx >= 0) return fromOutcome(sections[readIdx]?.result, segs.slice(1));
5011
+ }
5012
+ const v = valueAtPath(ctx.args, path);
5013
+ return v === void 0 ? ABSENT : { ok: true, value: v };
5014
+ }
5015
+ function sameValue(a, b) {
5016
+ if (typeof a === "number" && typeof b === "number") return Math.abs(a - b) < 1e-9;
5017
+ return a === b;
5018
+ }
5019
+ function inList(list, v) {
5020
+ return Array.isArray(list) && list.some((x) => sameValue(x, v));
5021
+ }
5022
+ function show(v) {
5023
+ return typeof v === "string" ? v : JSON.stringify(v);
5024
+ }
5025
+ function statusFor(decl, value, scope2) {
5026
+ if (decl.compare) {
5027
+ let to = decl.compare.to;
5028
+ if (typeof decl.compare.to === "string") {
5029
+ const r = resolveCheckPath(decl.compare.to, scope2);
5030
+ if (!r.ok) return r;
5031
+ to = r.value;
5032
+ }
5033
+ if (value === null || to === null) return ABSENT;
5034
+ let result;
5035
+ switch (decl.compare.op) {
5036
+ case "eq":
5037
+ result = sameValue(value, to);
5038
+ break;
5039
+ case "ne":
5040
+ result = !sameValue(value, to);
5041
+ break;
5042
+ case "gt":
5043
+ case "lt":
5044
+ if (typeof value !== "number" || typeof to !== "number") {
5045
+ return { status: "not_checked", reason: `unexpected value ${show(value)}` };
5046
+ }
5047
+ result = decl.compare.op === "gt" ? value > to : value < to;
5048
+ break;
5049
+ }
5050
+ return { status: result ? decl.onTrue ?? "pass" : decl.onFalse ?? "fail" };
5051
+ }
5052
+ if (decl.present !== void 0 || decl.absent !== void 0) {
5053
+ return { status: isPresentForWhen(value) || value === false ? decl.present : decl.absent };
5054
+ }
5055
+ if (inList(decl.fail, value)) return { status: "fail" };
5056
+ if (inList(decl.pass, value)) return { status: "pass" };
5057
+ if (inList(decl.notChecked, value)) return { status: "not_checked" };
5058
+ if (inList(decl.notice, value)) return { status: "notice" };
5059
+ return { status: "not_checked", reason: `unexpected value ${show(value)}` };
5060
+ }
5061
+ function evaluateOne(decl, scope2) {
5062
+ if (typeof decl.when === "string") {
5063
+ const w = resolveCheckPath(decl.when, scope2);
5064
+ if (!w.ok || !isPresentForWhen(w.value)) return null;
5065
+ } else if (decl.when !== void 0) {
5066
+ const w = resolveCheckPath(decl.when.path, scope2);
5067
+ if (!w.ok || !inList(decl.when.in, w.value)) return null;
5068
+ }
5069
+ const base = {
5070
+ key: decl.key,
5071
+ label: decl.label,
5072
+ status: "not_checked",
5073
+ ...decl.blocking ? { blocking: true } : {},
5074
+ ...scope2.element ? { index: scope2.element.position } : {}
5075
+ };
5076
+ const missing = (why) => {
5077
+ if ((decl.missing ?? "skip") === "skip" || decl.showOnly) return null;
5078
+ return { ...base, status: "not_checked", reason: why === "no_answer" ? "no answer from the connector" : "no value to check" };
5079
+ };
5080
+ const v = resolveCheckPath(decl.path, scope2);
5081
+ if (!v.ok) return missing(v.why);
5082
+ const outcome = statusFor(decl, v.value, scope2);
5083
+ if ("ok" in outcome) return outcome.ok || decl.showOnly ? null : missing(outcome.why);
5084
+ if (decl.showOnly === "fail" && outcome.status !== "fail") return null;
5085
+ if (decl.showOnly === "notice" && outcome.status !== "fail" && outcome.status !== "notice") return null;
5086
+ const template = decl.details?.[outcome.status] ?? decl.detail;
5087
+ const detail = template !== void 0 ? renderCheckDetail(template, scope2) : null;
5088
+ const tested = v.value;
5089
+ const plain = tested === null || ["string", "number", "boolean"].includes(typeof tested);
5090
+ return {
5091
+ ...base,
5092
+ ...plain ? { value: tested } : {},
5093
+ status: outcome.status,
5094
+ ...outcome.reason ? { reason: outcome.reason } : {},
5095
+ ...detail ? { detail } : {}
5096
+ };
5097
+ }
5098
+ function evaluateChecks(checks, ctx) {
5099
+ if (!checks || checks.length === 0) return [];
5100
+ const reads = ctx.declaration === void 0 ? [] : Array.isArray(ctx.declaration) ? ctx.declaration : [];
5101
+ const out = [];
5102
+ for (const decl of checks) {
5103
+ if (!decl.forEach) {
5104
+ const r = evaluateOne(decl, { ctx, reads });
5105
+ if (r) out.push(r);
5106
+ continue;
5107
+ }
5108
+ const source = ctx.args[decl.forEach];
5109
+ const list = Array.isArray(source) ? source.slice(0, MAX_FOREACH_ELEMENTS) : [];
5110
+ let readIndex = 0;
5111
+ list.forEach((el, i) => {
5112
+ if (!isPlainRecord(el)) return;
5113
+ const r = evaluateOne(decl, { ctx, reads, element: { forEach: decl.forEach, value: el, readIndex, position: i + 1 } });
5114
+ readIndex += 1;
5115
+ if (r) out.push(r);
5116
+ });
5117
+ }
5118
+ return out;
5119
+ }
5120
+ var PLACEHOLDER = /\{([^{}]+)\}/g;
5121
+ function formatValue(v, fmt, currency) {
5122
+ if (fmt === "money") {
5123
+ const n = typeof v === "number" ? v : Number(v);
5124
+ if (!Number.isFinite(n)) return show(v);
5125
+ return currency ? `${n.toFixed(2)} ${currency}` : n.toFixed(2);
5126
+ }
5127
+ if (fmt === "percent") return `${show(v)}%`;
5128
+ if (fmt === "date") {
5129
+ const d = new Date(String(v));
5130
+ return Number.isNaN(d.getTime()) ? show(v) : d.toISOString().slice(0, 10);
5131
+ }
5132
+ if (fmt === "list" && Array.isArray(v)) return v.map(show).join(", ");
5133
+ if (fmt?.startsWith("count:")) {
5134
+ const [one, many] = fmt.slice("count:".length).split("/");
5135
+ return `${show(v)} ${Number(v) === 1 ? one : many}`;
5136
+ }
5137
+ const s = show(v);
5138
+ if (fmt === "short" && s.length > 60) return `${s.slice(0, 59).trimEnd()}\u2026`;
5139
+ return s;
5140
+ }
5141
+ function fillDetail(text, scope2, currency) {
5142
+ let missing = false;
5143
+ const out = text.replace(PLACEHOLDER, (_m, inner) => {
5144
+ const [path, fmt] = inner.split("|").map((x) => x.trim());
5145
+ if (path === "#") {
5146
+ if (!scope2.element) {
5147
+ missing = true;
5148
+ return "";
5149
+ }
5150
+ return String(scope2.element.position);
5151
+ }
5152
+ const r = resolveCheckPath(path, scope2);
5153
+ if (!r.ok || !isPresentForWhen(r.value) && r.value !== false) {
5154
+ missing = true;
5155
+ return "";
5156
+ }
5157
+ return formatValue(r.value, fmt, currency);
5158
+ });
5159
+ return missing ? null : out;
5160
+ }
5161
+ function renderCheckDetail(template, scope2) {
5162
+ const curPath = scope2.ctx.currencyField ?? "currency";
5163
+ const cur = resolveCheckPath(curPath, scope2);
5164
+ const currency = cur.ok && typeof cur.value === "string" ? cur.value : void 0;
5165
+ const segments = [{ text: "", optional: false }];
5166
+ let depth = 0;
5167
+ for (const ch of template) {
5168
+ if (ch === "{") depth += 1;
5169
+ if (ch === "}") depth -= 1;
5170
+ const last = segments[segments.length - 1];
5171
+ if (depth === 0 && ch === "[" && !last.optional) {
5172
+ segments.push({ text: "", optional: true });
5173
+ continue;
5174
+ }
5175
+ if (depth === 0 && ch === "]" && last.optional) {
5176
+ segments.push({ text: "", optional: false });
5177
+ continue;
5178
+ }
5179
+ last.text += ch;
5180
+ }
5181
+ let out = "";
5182
+ for (const seg of segments) {
5183
+ const filled = fillDetail(seg.text, scope2, currency);
5184
+ if (filled === null && !seg.optional) return null;
5185
+ out += filled ?? "";
5186
+ }
5187
+ const clean = out.replace(/\s+/g, " ").trim().replace(/^[—–,;:\s]+/, "").replace(/[—–,;:\s]+$/, "");
5188
+ return clean.length > 0 ? clean : null;
5189
+ }
5190
+ function blockingFailures(results) {
5191
+ return results.filter((r) => r.blocking === true && r.status === "fail");
5192
+ }
5193
+ function blockingRefusalReason(failures) {
5194
+ return failures.map((f) => `${f.label}${f.index !== void 0 ? ` (line ${f.index})` : ""}: failed${f.value !== void 0 ? ` (${f.value === null ? "none" : String(f.value)})` : ""}${f.detail ? ` \u2014 ${f.detail}` : ""}`).join("; ");
5195
+ }
5196
+
5197
+ // src/lib/preview.ts
4730
5198
  var PREVIEW_READ_TIMEOUT_MS = 5e3;
4731
5199
  var PREVIEW_MESSAGE_CAP = 500;
4732
5200
  function capText(text, max = PREVIEW_MESSAGE_CAP) {
@@ -4749,16 +5217,39 @@ function getToolDisplayHints(integrationManager2, integrationId, toolName) {
4749
5217
  const entry = overrides[toolName];
4750
5218
  return entry?.display;
4751
5219
  }
5220
+ function getToolChecks(integrationManager2, integrationId, toolName) {
5221
+ if (integrationManager2.isBuiltin?.(integrationId)) return void 0;
5222
+ const entry = getManifest(integrationId)?.toolGating?.overrides?.[toolName];
5223
+ return entry?.checks;
5224
+ }
4752
5225
  function mapPreviewArgs(preview, actionArgs) {
4753
5226
  const mapped = {};
4754
5227
  for (const [targetArg, sourceArg] of Object.entries(preview.args)) {
4755
- if (sourceArg in actionArgs) mapped[targetArg] = actionArgs[sourceArg];
5228
+ const value = actionArgs[sourceArg];
5229
+ if (value !== void 0 && value !== null) mapped[targetArg] = value;
4756
5230
  }
4757
5231
  return mapped;
4758
5232
  }
5233
+ function isPresentForWhen(v) {
5234
+ if (v === void 0 || v === null || v === false) return false;
5235
+ if (typeof v === "string") return v.trim().length > 0;
5236
+ if (Array.isArray(v)) return v.length > 0;
5237
+ return true;
5238
+ }
5239
+ function valueAtPath(source, path) {
5240
+ let cur = source;
5241
+ for (const part of path.split(".")) {
5242
+ if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return void 0;
5243
+ cur = cur[part];
5244
+ }
5245
+ return cur;
5246
+ }
4759
5247
  function previewHasNoTarget(preview, mapped) {
4760
5248
  return Object.keys(preview.args).length > 0 && Object.keys(mapped).length === 0;
4761
5249
  }
5250
+ function whenAbsent(config, source) {
5251
+ return config.when !== void 0 && !isPresentForWhen(valueAtPath(source, config.when));
5252
+ }
4762
5253
  var PreviewTimeoutError = class extends Error {
4763
5254
  };
4764
5255
  function withTimeout(p, ms) {
@@ -4780,7 +5271,7 @@ async function readPreview(integrationManager2, integrationId, previewTool, args
4780
5271
  );
4781
5272
  const text = result.content?.[0]?.text;
4782
5273
  if (result.isError) {
4783
- return { status: "not_found", message: capText(text) };
5274
+ return /^\s*Refused\b/i.test(text ?? "") ? { status: "refused", message: capText(text) } : { status: "not_found", message: capText(text) };
4784
5275
  }
4785
5276
  return {
4786
5277
  status: "ok",
@@ -4816,7 +5307,7 @@ function hashPreviewBody(body) {
4816
5307
  }
4817
5308
  return computeContentHash({ kind: "text", version: "1" }, body.text ?? "");
4818
5309
  }
4819
- function isPlainRecord(v) {
5310
+ function isPlainRecord2(v) {
4820
5311
  return typeof v === "object" && v !== null && !Array.isArray(v);
4821
5312
  }
4822
5313
  function planPreviewReads(declaration, actionArgs) {
@@ -4828,13 +5319,16 @@ function planPreviewReads(declaration, actionArgs) {
4828
5319
  fields: config.fields,
4829
5320
  version: config.version,
4830
5321
  kind: config.kind,
4831
- argsMapping: config.args
5322
+ argsMapping: config.args,
5323
+ ...config.displayOnly ? { displayOnly: true } : {},
5324
+ ...config.as ? { as: config.as } : {},
5325
+ ...config.label ? { label: config.label } : {}
4832
5326
  };
4833
5327
  if (config.forEach) {
4834
5328
  const source = actionArgs[config.forEach];
4835
5329
  const list = Array.isArray(source) ? source : [];
4836
5330
  const capped = list.slice(0, MAX_FOREACH_ELEMENTS);
4837
- const elements = capped.filter(isPlainRecord).map((el) => ({ args: mapPreviewArgs(config, el) }));
5331
+ const elements = capped.filter(isPlainRecord2).map((el) => config.when !== void 0 && !isPresentForWhen(valueAtPath(el, config.when)) ? { args: {}, skip: true } : { args: mapPreviewArgs(config, el) });
4838
5332
  return {
4839
5333
  ...base,
4840
5334
  forEach: config.forEach,
@@ -4842,6 +5336,9 @@ function planPreviewReads(declaration, actionArgs) {
4842
5336
  ...list.length > MAX_FOREACH_ELEMENTS ? { overflow: { shown: MAX_FOREACH_ELEMENTS, total: list.length } } : {}
4843
5337
  };
4844
5338
  }
5339
+ if (config.when !== void 0 && !isPresentForWhen(valueAtPath(actionArgs, config.when))) {
5340
+ return { ...base, args: {}, rawSource: actionArgs, noTarget: true };
5341
+ }
4845
5342
  const mapped = mapPreviewArgs(config, actionArgs);
4846
5343
  return { ...base, args: mapped, rawSource: actionArgs, noTarget: previewHasNoTarget(config, mapped) };
4847
5344
  });
@@ -4855,12 +5352,15 @@ async function runPreviewReadPlan(integrationManager2, integrationId, plan) {
4855
5352
  ...item.attach ? { attach: item.attach } : {},
4856
5353
  ...sourceArgNames.length === 1 ? { sourceArg: sourceArgNames[0] } : {},
4857
5354
  ...item.fields ? { fields: item.fields } : {},
4858
- ...item.kind ? { kind: item.kind } : {}
5355
+ ...item.kind ? { kind: item.kind } : {},
5356
+ ...item.displayOnly ? { displayOnly: true } : {},
5357
+ ...item.as ? { as: item.as } : {},
5358
+ ...item.label ? { label: item.label } : {}
4859
5359
  };
4860
5360
  if (item.elements !== void 0) {
4861
5361
  const elements = [];
4862
5362
  for (const el of item.elements) {
4863
- elements.push(await readPreview(integrationManager2, integrationId, item.tool, el.args));
5363
+ elements.push(el.skip ? { status: "none", reason: "no_target" } : await readPreview(integrationManager2, integrationId, item.tool, el.args));
4864
5364
  }
4865
5365
  sections.push({
4866
5366
  ...base,
@@ -4887,11 +5387,12 @@ async function runPreviewReadPlan(integrationManager2, integrationId, plan) {
4887
5387
  const { [item.version.arg]: _omit, ...withoutVersion } = item.rawSource;
4888
5388
  const mappedCurrentArgs = mapPreviewArgs({ args: item.argsMapping }, withoutVersion);
4889
5389
  const read2 = await readPreview(integrationManager2, integrationId, item.tool, mappedCurrentArgs);
4890
- if (read2.status !== "ok" || read2.body.structured === void 0) {
5390
+ const current2 = read2.status === "ok" ? structuredOrJsonText(read2.body) : void 0;
5391
+ if (current2 === void 0) {
4891
5392
  sections.push({ ...base, result: read1 });
4892
5393
  continue;
4893
5394
  }
4894
- const current = read2.body.structured[item.version.field];
5395
+ const current = current2[item.version.field];
4895
5396
  if (typeof current !== "string" && typeof current !== "number") {
4896
5397
  sections.push({ ...base, result: read1 });
4897
5398
  continue;
@@ -4905,7 +5406,7 @@ async function runPreviewReadPlan(integrationManager2, integrationId, plan) {
4905
5406
  approved,
4906
5407
  current,
4907
5408
  stale,
4908
- ...stale ? { currentBody: read2.body } : {}
5409
+ ...stale && read2.status === "ok" ? { currentBody: read2.body } : {}
4909
5410
  }
4910
5411
  });
4911
5412
  }
@@ -4916,9 +5417,9 @@ async function computeSubmissionPreviewHash(integrationManager2, integrationId,
4916
5417
  const preview = getToolPreviewConfig(integrationManager2, integrationId, toolName);
4917
5418
  if (!preview) return void 0;
4918
5419
  if (Array.isArray(preview)) return combinedPreviewReadsHash(integrationManager2, integrationId, preview, actionArgs);
4919
- if (preview.version) return void 0;
5420
+ if (preview.version || preview.displayOnly) return void 0;
4920
5421
  const mapped = mapPreviewArgs(preview, actionArgs);
4921
- if (previewHasNoTarget(preview, mapped)) return void 0;
5422
+ if (previewHasNoTarget(preview, mapped) || whenAbsent(preview, actionArgs)) return void 0;
4922
5423
  const read = await readPreview(integrationManager2, integrationId, preview.tool, mapped);
4923
5424
  if (read.status !== "ok") return void 0;
4924
5425
  return hashPreviewBody(read.body);
@@ -4937,11 +5438,12 @@ async function previewChangedSinceSubmission(integrationManager2, integrationId,
4937
5438
  return hashPreviewBody(read.body) !== previousHash;
4938
5439
  }
4939
5440
  async function combinedPreviewReadsHash(integrationManager2, integrationId, reads, actionArgs) {
4940
- const plan = planPreviewReads(reads, actionArgs).filter((item) => !item.version);
5441
+ const plan = planPreviewReads(reads, actionArgs).filter((item) => !item.version && !item.displayOnly);
4941
5442
  const hashes = [];
4942
5443
  for (const item of plan) {
4943
5444
  if (item.elements !== void 0) {
4944
5445
  for (const el of item.elements) {
5446
+ if (el.skip) continue;
4945
5447
  const read2 = await readPreview(integrationManager2, integrationId, item.tool, el.args);
4946
5448
  if (read2.status !== "ok") return void 0;
4947
5449
  hashes.push(hashPreviewBody(read2.body));
@@ -4957,6 +5459,32 @@ async function combinedPreviewReadsHash(integrationManager2, integrationId, read
4957
5459
  return computeContentHash({ kind: "jcs", version: "1" }, { reads: hashes });
4958
5460
  }
4959
5461
  async function buildInternalPreview(integrationManager2, integrationId, toolName, actionArgs) {
5462
+ const result = await buildPreviewReads(integrationManager2, integrationId, toolName, actionArgs);
5463
+ const checks = getToolChecks(integrationManager2, integrationId, toolName);
5464
+ if (!checks || checks.length === 0) return result;
5465
+ const display = getToolDisplayHints(integrationManager2, integrationId, toolName);
5466
+ return {
5467
+ ...result,
5468
+ checks: evaluateChecks(checks, {
5469
+ args: actionArgs,
5470
+ declaration: getToolPreviewConfig(integrationManager2, integrationId, toolName),
5471
+ sections: "reads" in result ? result.reads : void 0,
5472
+ currencyField: display?.currencyField
5473
+ })
5474
+ };
5475
+ }
5476
+ async function failedBlockingChecks(integrationManager2, integrationId, toolName, actionArgs) {
5477
+ const declared = getToolChecks(integrationManager2, integrationId, toolName);
5478
+ if (!declared?.some((c) => c.blocking === true)) return [];
5479
+ try {
5480
+ const result = await buildInternalPreview(integrationManager2, integrationId, toolName, actionArgs);
5481
+ return blockingFailures(result.checks ?? []);
5482
+ } catch (err) {
5483
+ console.error(`[Suveren MCP] blocking checks for ${integrationId}__${toolName} could not run:`, err);
5484
+ return [];
5485
+ }
5486
+ }
5487
+ async function buildPreviewReads(integrationManager2, integrationId, toolName, actionArgs) {
4960
5488
  const preview = getToolPreviewConfig(integrationManager2, integrationId, toolName);
4961
5489
  if (!preview) return { status: "none" };
4962
5490
  if (Array.isArray(preview)) {
@@ -4965,7 +5493,7 @@ async function buildInternalPreview(integrationManager2, integrationId, toolName
4965
5493
  return { status: "ok", integration: integrationId, reads, ...display ? { display } : {} };
4966
5494
  }
4967
5495
  const mappedArgs = mapPreviewArgs(preview, actionArgs);
4968
- if (previewHasNoTarget(preview, mappedArgs)) return { status: "none", reason: "no_target" };
5496
+ if (previewHasNoTarget(preview, mappedArgs) || whenAbsent(preview, actionArgs)) return { status: "none", reason: "no_target" };
4969
5497
  const read1 = await readPreview(integrationManager2, integrationId, preview.tool, mappedArgs);
4970
5498
  if (read1.status !== "ok") return read1;
4971
5499
  const ok = (extra = {}) => ({
@@ -4986,10 +5514,11 @@ async function buildInternalPreview(integrationManager2, integrationId, toolName
4986
5514
  const { [preview.version.arg]: _omit, ...withoutVersion } = actionArgs;
4987
5515
  const mappedCurrentArgs = mapPreviewArgs(preview, withoutVersion);
4988
5516
  const read2 = await readPreview(integrationManager2, integrationId, preview.tool, mappedCurrentArgs);
4989
- if (read2.status !== "ok" || read2.body.structured === void 0) {
5517
+ const current2 = read2.status === "ok" ? structuredOrJsonText(read2.body) : void 0;
5518
+ if (current2 === void 0) {
4990
5519
  return ok();
4991
5520
  }
4992
- const current = read2.body.structured[preview.version.field];
5521
+ const current = current2[preview.version.field];
4993
5522
  if (typeof current !== "string" && typeof current !== "number") {
4994
5523
  return ok();
4995
5524
  }
@@ -5000,7 +5529,7 @@ async function buildInternalPreview(integrationManager2, integrationId, toolName
5000
5529
  approved,
5001
5530
  current,
5002
5531
  stale,
5003
- ...stale ? { currentBody: read2.body } : {}
5532
+ ...stale && read2.status === "ok" ? { currentBody: read2.body } : {}
5004
5533
  }
5005
5534
  });
5006
5535
  }
@@ -5190,7 +5719,7 @@ async function readContextRecord(integrationManager2, connectorId, tool, args) {
5190
5719
  if (record === void 0) return { status: "unavailable", reason: "connector_error" };
5191
5720
  return { status: "ok", record };
5192
5721
  }
5193
- if (read.status === "not_found") return { status: "not_found" };
5722
+ if (read.status === "not_found" || read.status === "refused") return { status: "not_found" };
5194
5723
  return { status: "unavailable", reason: read.reason };
5195
5724
  }
5196
5725
  function parseExplicitSourceLink(raw) {
@@ -5466,7 +5995,7 @@ async function resolveDocumentReference(integrationManager2, ref) {
5466
5995
  }
5467
5996
  const args = substitute(docConfig.args, { id: ref.id });
5468
5997
  const read = await readPreview(integrationManager2, ref.connector, docConfig.tool, args);
5469
- if (read.status === "not_found") {
5998
+ if (read.status === "not_found" || read.status === "refused") {
5470
5999
  return { ok: false, error: { code: "NOT_FOUND", message: read.message ?? `No document "${ref.kind}"/"${ref.id}" on "${ref.connector}".` } };
5471
6000
  }
5472
6001
  if (read.status !== "ok") {
@@ -5548,7 +6077,7 @@ async function resolveInsertValue(integrationManager2, ref) {
5548
6077
  }
5549
6078
  const args = substitute(valueConfig.args, { id: ref.id });
5550
6079
  const read = await readPreview(integrationManager2, ref.connector, valueConfig.tool, args);
5551
- if (read.status === "not_found") {
6080
+ if (read.status === "not_found" || read.status === "refused") {
5552
6081
  return { ok: false, error: { code: "NOT_FOUND", message: read.message ?? `No value "${ref.kind}"/"${ref.id}" on "${ref.connector}".` } };
5553
6082
  }
5554
6083
  if (read.status !== "ok") {
@@ -6680,6 +7209,24 @@ function createGatedToolHandlerInner(tool, integrationManager2, state2) {
6680
7209
  return { content: [{ type: "text", text: noMandateRefusal(tool.namespacedName, mandateLabel) }], isError: true };
6681
7210
  }
6682
7211
  if (matchingAuths.length === 0) return noEligibleMandate(refusals);
7212
+ const blockingFails = await failedBlockingChecks(integrationManager2, tool.integrationId, tool.originalName, args);
7213
+ if (blockingFails.length > 0) {
7214
+ recordBlocked(state2, tool, {
7215
+ kind: "check",
7216
+ // A sentence without content: the check's own label only.
7217
+ detail: `Check failed: ${blockingFails.map((f) => f.label).join("; ")}.`,
7218
+ field: blockingFails[0].key,
7219
+ who: "gateway",
7220
+ code: "CHECK_FAILED"
7221
+ });
7222
+ return {
7223
+ content: [{
7224
+ type: "text",
7225
+ text: `Refused: ${blockingRefusalReason(blockingFails)}. Nothing was proposed and nothing ran. This check reads the system's own data, so an approval cannot override it \u2014 correct the request (or the data in the system) and try again.`
7226
+ }],
7227
+ isError: true
7228
+ };
7229
+ }
6683
7230
  const errors = [];
6684
7231
  const passers = [];
6685
7232
  const structuredRefusals = [];