@zanii/blackbox 0.22.0 → 0.23.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.
Files changed (87) hide show
  1. package/README.md +1 -0
  2. package/dist/analysis/accepted.d.ts +26 -0
  3. package/dist/analysis/accepted.js +61 -0
  4. package/dist/analysis/detectors.js +12 -9
  5. package/dist/analysis/faults.js +71 -0
  6. package/dist/analysis/grounding.js +5 -9
  7. package/dist/analysis/guard.d.ts +72 -0
  8. package/dist/analysis/guard.js +240 -0
  9. package/dist/analysis/hallucination.js +2 -2
  10. package/dist/analysis/index.d.ts +21 -1
  11. package/dist/analysis/index.js +101 -19
  12. package/dist/analysis/injection.d.ts +2 -1
  13. package/dist/analysis/injection.js +16 -16
  14. package/dist/analysis/landing.js +60 -14
  15. package/dist/analysis/plan-checks.d.ts +55 -0
  16. package/dist/analysis/plan-checks.js +748 -0
  17. package/dist/analysis/taxonomy.js +1 -0
  18. package/dist/analysis/waste.js +2 -3
  19. package/dist/approvals/index.d.ts +12 -3
  20. package/dist/approvals/index.js +36 -10
  21. package/dist/authority/index.js +3 -3
  22. package/dist/aviation/index.d.ts +422 -0
  23. package/dist/aviation/index.js +1014 -0
  24. package/dist/aviation/qrh.d.ts +4 -0
  25. package/dist/aviation/qrh.js +171 -0
  26. package/dist/budgets/index.d.ts +63 -0
  27. package/dist/budgets/index.js +161 -0
  28. package/dist/cli.js +172 -24
  29. package/dist/compliance/index.d.ts +2 -0
  30. package/dist/compliance/index.js +26 -2
  31. package/dist/cost/batch.d.ts +26 -0
  32. package/dist/cost/batch.js +173 -0
  33. package/dist/cost/index.d.ts +21 -0
  34. package/dist/cost/index.js +115 -30
  35. package/dist/cv/index.js +1 -1
  36. package/dist/duty/index.js +2 -1
  37. package/dist/evals/index.d.ts +233 -0
  38. package/dist/evals/index.js +488 -0
  39. package/dist/federation/index.d.ts +9 -0
  40. package/dist/federation/index.js +8 -1
  41. package/dist/finance/index.js +3 -4
  42. package/dist/fleet/index.d.ts +25 -0
  43. package/dist/fleet/index.js +57 -17
  44. package/dist/fleet/insights.js +4 -1
  45. package/dist/health/index.js +2 -2
  46. package/dist/hooks/ai-sdk.d.ts +17 -19
  47. package/dist/hooks/ai-sdk.js +13 -7
  48. package/dist/hooks/index.d.ts +1 -0
  49. package/dist/hooks/index.js +1 -0
  50. package/dist/hooks/mastra.d.ts +26 -0
  51. package/dist/hooks/mastra.js +63 -0
  52. package/dist/index.d.ts +22 -9
  53. package/dist/index.js +23 -9
  54. package/dist/mcp-requests/index.d.ts +26 -0
  55. package/dist/mcp-requests/index.js +154 -0
  56. package/dist/mcp-server/index.d.ts +12 -0
  57. package/dist/mcp-server/index.js +84 -2
  58. package/dist/mcp-wrap/index.js +8 -4
  59. package/dist/otlp/index.d.ts +155 -0
  60. package/dist/otlp/index.js +468 -0
  61. package/dist/otlp/protobuf.d.ts +7 -0
  62. package/dist/otlp/protobuf.js +314 -0
  63. package/dist/packs/index.d.ts +2 -0
  64. package/dist/packs/index.js +5 -1
  65. package/dist/pdf/arabic.d.ts +45 -0
  66. package/dist/pdf/arabic.js +373 -0
  67. package/dist/pdf/index.d.ts +77 -0
  68. package/dist/pdf/index.js +673 -0
  69. package/dist/policy/index.d.ts +0 -4
  70. package/dist/policy/index.js +13 -5
  71. package/dist/realtime/index.d.ts +33 -0
  72. package/dist/realtime/index.js +204 -0
  73. package/dist/reconcile/record.d.ts +12 -0
  74. package/dist/reconcile/record.js +114 -65
  75. package/dist/session/index.d.ts +43 -0
  76. package/dist/session/index.js +83 -0
  77. package/dist/undo/index.js +3 -3
  78. package/dist/verify/anchor.d.ts +5 -1
  79. package/dist/verify/anchor.js +13 -4
  80. package/dist/verify/envelope.d.ts +1 -1
  81. package/dist/verify/envelope.js +5 -0
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/dist/walls/index.js +2 -2
  85. package/dist/warehouse/index.d.ts +62 -0
  86. package/dist/warehouse/index.js +142 -0
  87. package/package.json +6 -3
@@ -8,6 +8,7 @@ import { healthFindings } from "../health/index.js";
8
8
  import { policyFindings } from "../policy/index.js";
9
9
  import { precogFindings } from "../precog/index.js";
10
10
  import { preflightFindings } from "../preflight/index.js";
11
+ import { envelopesOf, foldState, trackRecord } from "../reconcile/record.js";
11
12
  import { canonical } from "../reconcile/shared.js";
12
13
  import { undoFindings } from "../undo/index.js";
13
14
  import { wallFindings } from "../walls/index.js";
@@ -26,27 +27,68 @@ export { checkFlightPlan, landing, } from "./landing.js";
26
27
  export { checkAgainstPacks, loadReferencePacks, packsFor, referenceContext, } from "./reference.js";
27
28
  export { OUTCOMES, rollup, waste, } from "./waste.js";
28
29
  const decoder = new TextDecoder();
29
- /** Parses the lines once, attaching SDK event bodies. */
30
+ /** Parses the lines once, attaching SDK event bodies. On a tracked record (`Analyzer`), only the
31
+ * lines added since the last call are read. */
30
32
  export function eventsOf(lines, bodies) {
31
- // N4 (idea R6): a re-sent SDK event (meta.duplicate_of) stays on the record but counts once.
32
- return lines.flatMap((line) => {
33
- const e = JSON.parse(line);
34
- if (e.kind === "sdk.event" && e.meta.duplicate_of !== undefined)
35
- return [];
36
- if (e.kind === "sdk.event") {
37
- const b = bodies(e.body_hash);
38
- try {
39
- const parsed = b === undefined ? {} : JSON.parse(decoder.decode(b));
40
- e.data =
41
- typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
42
- }
43
- catch {
44
- e.data = {};
45
- }
33
+ const fold = foldState(lines, "eventsOf", () => ({ done: 0, out: [], bodies }));
34
+ if (fold && fold.bodies === bodies) {
35
+ const all = envelopesOf(lines);
36
+ for (; fold.done < all.length; fold.done++) {
37
+ const e = withData(all[fold.done], bodies);
38
+ if (e)
39
+ fold.out.push(e);
46
40
  }
47
- return [e];
41
+ return [...fold.out];
42
+ }
43
+ return envelopesOf(lines).flatMap((e) => {
44
+ const out = withData(e, bodies);
45
+ return out ? [out] : [];
48
46
  });
49
47
  }
48
+ function withData(e, bodies) {
49
+ // N4 (idea R6): a re-sent SDK event (meta.duplicate_of) stays on the record but counts once.
50
+ if (e.kind === "sdk.event" && e.meta.duplicate_of !== undefined)
51
+ return null;
52
+ if (e.kind !== "sdk.event")
53
+ return e;
54
+ const b = bodies(e.body_hash);
55
+ let data = {};
56
+ try {
57
+ const parsed = b === undefined ? {} : JSON.parse(decoder.decode(b));
58
+ data = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
59
+ }
60
+ catch { }
61
+ return Object.isFrozen(e) ? Object.freeze({ ...e, data: deepFreezeJson(data) }) : { ...e, data };
62
+ }
63
+ function deepFreezeJson(value) {
64
+ if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
65
+ Object.freeze(value);
66
+ for (const v of Object.values(value))
67
+ deepFreezeJson(v);
68
+ }
69
+ return value;
70
+ }
71
+ /**
72
+ * spec/findings.md §14 (G33): the analyses over one session's growing record. Each `findings` call
73
+ * reads only the lines added since the last one (each line is parsed once, the calls are rebuilt
74
+ * from the new events only) and gives exactly what `analyze` gives over the whole record.
75
+ */
76
+ export class Analyzer {
77
+ lines = [];
78
+ bodies;
79
+ constructor(bodies) {
80
+ this.bodies = bodies;
81
+ trackRecord(this.lines);
82
+ }
83
+ /** Appends the record's new lines (in seq order; a record only grows). */
84
+ push(lines) {
85
+ for (const l of lines)
86
+ this.lines.push(l);
87
+ }
88
+ findings(options) {
89
+ return analyze(this.lines, { ...options, bodies: this.bodies });
90
+ }
91
+ }
50
92
  /** Every analysis, in spec order. */
51
93
  export function analyze(lines, options) {
52
94
  const events = eventsOf(lines, options.bodies);
@@ -60,6 +102,7 @@ export function analyze(lines, options) {
60
102
  ...toolChanged(events),
61
103
  ...toolDrifted(events),
62
104
  ...secretSent(events),
105
+ ...elicitationSecret(events),
63
106
  ...userDownvotes(events),
64
107
  ...selfChanges(events),
65
108
  ...promptChanged(events),
@@ -473,16 +516,55 @@ export function secretSent(events) {
473
516
  n = count(e.meta.redacted);
474
517
  to = "tool";
475
518
  }
519
+ else if ((e.kind === "tool.elicitation" || e.kind === "tool.sampling") &&
520
+ e.meta.phase === "reply" &&
521
+ typeof e.meta.method === "string") {
522
+ // spec/mcp.md §6: the client's answer to a server's request went to that MCP server
523
+ n = count(e.meta.redacted);
524
+ to = "server";
525
+ }
476
526
  if (!to || n <= 0)
477
527
  continue;
478
528
  out.push({
479
529
  code: "SECRET_SENT",
480
530
  source: "detectors",
481
531
  severity: "warning",
482
- ref: { seq: e.seq, to, count: n, ...(to === "tool" ? { tool: e.meta.tool } : {}) },
532
+ ref: {
533
+ seq: e.seq,
534
+ to: to === "server" ? "tool" : to,
535
+ count: n,
536
+ ...(to === "model"
537
+ ? {}
538
+ : { tool: (to === "tool" ? e.meta.tool : e.meta.method) }),
539
+ },
483
540
  detail: to === "model"
484
541
  ? "A credential (a key, a token, a password) went to the model provider in the request."
485
- : "A credential (a key, a token, a password) went to a tool in its arguments; the gateway's credential broker keeps them out of the agent's hands.",
542
+ : to === "tool"
543
+ ? "A credential (a key, a token, a password) went to a tool in its arguments; the gateway's credential broker keeps them out of the agent's hands."
544
+ : "A credential (a key, a token, a password) went to an MCP server in the client's answer to its request.",
545
+ });
546
+ }
547
+ return out;
548
+ }
549
+ /** spec/mcp.md §6: an MCP server asked the user for a credential through `elicitation/create`. */
550
+ export function elicitationSecret(events) {
551
+ const out = [];
552
+ for (const e of events) {
553
+ const fields = e.meta.secret_fields;
554
+ if (e.kind !== "tool.elicitation" || e.meta.phase !== "request")
555
+ continue;
556
+ if (!Array.isArray(fields) || fields.length === 0)
557
+ continue;
558
+ out.push({
559
+ code: "ELICITATION_SECRET",
560
+ source: "detectors",
561
+ severity: "warning",
562
+ ref: {
563
+ seq: e.seq,
564
+ ...(typeof e.meta.server === "string" ? { server: e.meta.server } : {}),
565
+ fields: fields.map(String).join(","),
566
+ },
567
+ detail: "An MCP server asked the user for a credential (a password, a token, a key) through elicitation; a server should never collect one this way.",
486
568
  });
487
569
  }
488
570
  return out;
@@ -7,7 +7,8 @@ export declare const INJECTION_RULES: ReadonlyArray<{
7
7
  export declare function hiddenText(text: string): boolean;
8
8
  /** The first rule a text breaks, or null. */
9
9
  export declare function injectionRule(text: string): string | null;
10
- /** spec/findings.md §1b: PROMPT_INJECTION findings over a record. */
10
+ /** spec/findings.md §1b: PROMPT_INJECTION findings over a record. Each event is judged on its own
11
+ * (on a tracked record, once: G33). */
11
12
  export declare function injectionFindings(lines: readonly string[], bodies: (hash: string) => Uint8Array | undefined): {
12
13
  code: string;
13
14
  source: string;
@@ -4,6 +4,7 @@
4
4
  // role hijacks, exfiltration asks and hidden text, in English and Arabic. One finding per event and
5
5
  // source; the finding names the rule, never the text. Mirrors analysis/injection.py; pinned by
6
6
  // spec/vectors/injection.json.
7
+ import { perEvent } from "../reconcile/record.js";
7
8
  const decoder = new TextDecoder();
8
9
  const MAX_TEXT = 200_000; // characters scanned per event: an injection is near the start or not worth the cost
9
10
  /** The rules, in the order they're tried. Patterns run on ASCII-lowercased, space-folded text. */
@@ -120,10 +121,10 @@ function newestTurn(body) {
120
121
  }
121
122
  return out;
122
123
  }
123
- /** spec/findings.md §1b: PROMPT_INJECTION findings over a record. */
124
+ /** spec/findings.md §1b: PROMPT_INJECTION findings over a record. Each event is judged on its own
125
+ * (on a tracked record, once: G33). */
124
126
  export function injectionFindings(lines, bodies) {
125
- const out = [];
126
- const add = (seq, source, rule, tool) => out.push({
127
+ const found = (seq, source, rule, tool) => ({
127
128
  code: "PROMPT_INJECTION",
128
129
  source: "detectors",
129
130
  severity: "warning",
@@ -132,32 +133,31 @@ export function injectionFindings(lines, bodies) {
132
133
  ? "Text the reader can't see (hidden characters) reached the model: a classic way to smuggle instructions."
133
134
  : "Text that reached the model tries to give it new instructions: an override, a role change, or a request to send out secrets.",
134
135
  });
135
- for (const l of lines) {
136
- const e = JSON.parse(l);
136
+ return perEvent(lines, "injection", bodies, (envelope) => {
137
+ const e = envelope;
137
138
  if (e.kind !== "tool.result" && e.kind !== "llm.request")
138
- continue;
139
+ return [];
139
140
  const raw = bodies(e.body_hash);
140
141
  if (!raw || raw.length === 0)
141
- continue;
142
+ return [];
142
143
  let doc;
143
144
  try {
144
145
  doc = JSON.parse(decoder.decode(raw));
145
146
  }
146
147
  catch {
147
- continue;
148
+ return [];
148
149
  }
149
150
  if (e.kind === "tool.result") {
150
151
  const result = doc?.result;
151
152
  if (result === undefined)
152
- continue;
153
+ return [];
153
154
  const rule = injectionRule(textOf(result));
154
155
  const tool = typeof e.meta.tool === "string" ? e.meta.tool : undefined;
155
- if (rule)
156
- add(e.seq, "tool_result", rule, tool);
157
- continue;
156
+ return rule ? [found(e.seq, "tool_result", rule, tool)] : [];
158
157
  }
159
158
  if (!doc || typeof doc !== "object" || Array.isArray(doc))
160
- continue;
159
+ return [];
160
+ const out = [];
161
161
  const seen = new Set();
162
162
  for (const part of newestTurn(doc)) {
163
163
  if (seen.has(part.source))
@@ -165,9 +165,9 @@ export function injectionFindings(lines, bodies) {
165
165
  const rule = injectionRule(part.text);
166
166
  if (rule) {
167
167
  seen.add(part.source);
168
- add(e.seq, part.source, rule);
168
+ out.push(found(e.seq, part.source, rule));
169
169
  }
170
170
  }
171
- }
172
- return out;
171
+ return out;
172
+ });
173
173
  }
@@ -4,6 +4,8 @@ import { checkDuty } from "../duty/index.js";
4
4
  import { callsOf } from "../reconcile/record.js";
5
5
  import { isObj } from "../reconcile/shared.js";
6
6
  import { toolCallsOf, toolResultsOf } from "./detectors.js";
7
+ import { answerText } from "./hallucination.js";
8
+ import { checkPredicate, judgeAnswer, judgeArgs } from "./plan-checks.js";
7
9
  /** Every tool call in the record, with what its result said (provider tool_use ids; MCP call_seq). */
8
10
  function usesOf(events, lines, bodies) {
9
11
  const calls = callsOf(lines, bodies);
@@ -13,16 +15,26 @@ function usesOf(events, lines, bodies) {
13
15
  results.set(r.id, r);
14
16
  const uses = toolCallsOf(calls).map((t) => {
15
17
  const r = t.id === undefined ? undefined : results.get(t.id);
16
- return r
17
- ? { seq: t.seq, tool: t.tool, ok: !r.error, resultSeq: r.seq }
18
- : { seq: t.seq, tool: t.tool };
18
+ const base = { seq: t.seq, tool: t.tool, args: t.args ?? null, hasArgs: true };
19
+ return r ? { ...base, ok: !r.error, resultSeq: r.seq } : base;
19
20
  });
20
21
  const mcp = new Map();
21
22
  for (const e of events) {
22
23
  if (e.kind === "tool.call" &&
23
24
  e.meta.method === "tools/call" &&
24
25
  typeof e.meta.tool === "string") {
25
- const u = { seq: e.seq, tool: e.meta.tool };
26
+ const u = { seq: e.seq, tool: e.meta.tool, hasArgs: false };
27
+ // the arguments are the request body's params.arguments (gone when the body was erased)
28
+ try {
29
+ const body = bodies(e.body_hash);
30
+ if (body !== undefined) {
31
+ const rpc = JSON.parse(new TextDecoder().decode(body));
32
+ const params = isObj(rpc) ? rpc.params : undefined;
33
+ u.args = isObj(params) && "arguments" in params ? params.arguments : {};
34
+ u.hasArgs = true;
35
+ }
36
+ }
37
+ catch { }
26
38
  mcp.set(e.seq, u);
27
39
  uses.push(u);
28
40
  }
@@ -62,13 +74,33 @@ function plansOf(events) {
62
74
  return { plan: null, invalid };
63
75
  }
64
76
  const listOf = (v) => (Array.isArray(v) ? v : []);
65
- /** One predicate over the tool calls (and results) recorded before `before`. */
66
- function judge(p, uses, before) {
77
+ /** The final answers on the record (a complete model response with text and no tool call). */
78
+ function answersOf(lines, bodies) {
79
+ const calls = callsOf(lines, bodies);
80
+ const ends = new Set(toolCallsOf(calls).map((t) => t.seq));
81
+ return [...calls]
82
+ .sort((a, b) => a.endSeq - b.endSeq)
83
+ .filter((c) => c.complete && c.request && !ends.has(c.endSeq))
84
+ .map((c) => ({ seq: c.endSeq, text: answerText(c) }))
85
+ .filter((a) => a.text.length > 0);
86
+ }
87
+ /** One predicate over the tool calls (and results, and answers) recorded before `before`. */
88
+ function judge(p, uses, before, answers = []) {
67
89
  const seen = uses.filter((u) => u.seq < before);
68
90
  const one = isObj(p) && Object.keys(p).length === 1 ? Object.entries(p)[0] : null;
69
91
  const [key, arg] = one ?? ["", null];
70
92
  const named = typeof arg === "string" ? seen.filter((u) => u.tool === arg) : [];
71
93
  const out = (result, gap) => ({ predicate: p, result, ...(gap ? { gap } : {}) });
94
+ if (key === "tool_args" || key === "answer") {
95
+ // spec/findings.md §5a: a claim's own asserts aren't checked when filed, so here
96
+ const bad = checkPredicate(p);
97
+ if (bad)
98
+ return out("cannot_verify", `invalid predicate: ${bad}`);
99
+ const r = key === "tool_args"
100
+ ? judgeArgs(arg, uses, before)
101
+ : judgeAnswer(arg, answers, before);
102
+ return out(r.result, r.gap);
103
+ }
72
104
  if (key === "tool_called" && typeof arg === "string")
73
105
  return named.length ? out("pass") : out("fail", `${arg} was never called`);
74
106
  if (key === "tool_not_called" && typeof arg === "string")
@@ -94,6 +126,7 @@ function judge(p, uses, before) {
94
126
  export function landing(events, lines, bodies) {
95
127
  const plan = planOf(events);
96
128
  const uses = usesOf(events, lines, bodies);
129
+ const answers = answersOf(lines, bodies);
97
130
  const claims = [];
98
131
  for (const e of events) {
99
132
  if (e.kind !== "sdk.event" || e.meta.type !== "claim")
@@ -102,11 +135,11 @@ export function landing(events, lines, bodies) {
102
135
  const criteria = [
103
136
  ...listOf(plan?.criteria).map((p, i) => ({
104
137
  criterion: `plan:${i}`,
105
- ...judge(p, uses, e.seq),
138
+ ...judge(p, uses, e.seq, answers),
106
139
  })),
107
140
  ...listOf(data.asserts).map((p, i) => ({
108
141
  criterion: `assert:${i}`,
109
- ...judge(p, uses, e.seq),
142
+ ...judge(p, uses, e.seq, answers),
110
143
  })),
111
144
  ];
112
145
  const verdict = criteria.some((c) => c.result === "fail")
@@ -145,7 +178,9 @@ export function flightPlan(events, lines, bodies) {
145
178
  }
146
179
  }
147
180
  listOf(plan.criteria).forEach((p, i) => {
148
- if (!isObj(p) || !("tool_not_called" in p || "max_tool_calls" in p))
181
+ // broken as soon as it is: a limit, or a rule on every call's arguments (§5a.2)
182
+ const every = isObj(p) && isObj(p.tool_args) && p.tool_args.for === "every";
183
+ if (!isObj(p) || !("tool_not_called" in p || "max_tool_calls" in p || every))
149
184
  return;
150
185
  const r = judge(p, uses, Number.POSITIVE_INFINITY);
151
186
  if (r.result === "fail")
@@ -196,11 +231,22 @@ export function checkFlightPlan(plan) {
196
231
  tools.every((x) => typeof x === "string" && x.length > 0 && x.length <= 128)))
197
232
  return "flight_plan.expected_tools must be at most 100 tool names";
198
233
  const value = (v) => typeof v === "string" ? v.length <= 128 : Number.isSafeInteger(v) && v >= 0;
199
- if (criteria !== undefined &&
200
- !(Array.isArray(criteria) &&
201
- criteria.length <= 50 &&
202
- criteria.every((c) => isObj(c) && Object.keys(c).length === 1 && value(Object.values(c)[0]))))
203
- return 'flight_plan.criteria must be at most 50 predicates like {"tool_called": "run_tests"}';
234
+ const legacy = 'flight_plan.criteria must be at most 50 predicates like {"tool_called": "run_tests"}';
235
+ if (criteria !== undefined) {
236
+ if (!Array.isArray(criteria) || criteria.length > 50)
237
+ return legacy;
238
+ for (const [i, c] of criteria.entries()) {
239
+ if (!isObj(c) || Object.keys(c).length !== 1)
240
+ return legacy;
241
+ if ("tool_args" in c || "answer" in c) {
242
+ const bad = checkPredicate(c); // spec/findings.md §5a
243
+ if (bad)
244
+ return `flight_plan.criteria[${i}]: ${bad}`;
245
+ }
246
+ else if (!value(Object.values(c)[0]))
247
+ return legacy;
248
+ }
249
+ }
204
250
  const name = (x) => typeof x === "string" && x.length > 0 && x.length <= 128;
205
251
  const { sterile } = plan;
206
252
  if (sterile !== undefined &&
@@ -0,0 +1,55 @@
1
+ type Test = (c: string) => boolean;
2
+ type Inst = {
3
+ op: "char";
4
+ test: Test;
5
+ } | {
6
+ op: "split";
7
+ x: number;
8
+ y: number;
9
+ } | {
10
+ op: "jmp";
11
+ x: number;
12
+ } | {
13
+ op: "assert";
14
+ kind: "bol" | "eol" | "word" | "nonword";
15
+ } | {
16
+ op: "match";
17
+ };
18
+ export interface Pattern {
19
+ prog: Inst[];
20
+ }
21
+ /** Text longer than this (in code points) isn't matched (cannot_verify). */
22
+ export declare const MAX_MATCH_TEXT = 100000;
23
+ /** Parses and compiles a pattern, or says why it isn't one (spec/findings.md §5a.1). */
24
+ export declare function compilePattern(source: unknown): Pattern | string;
25
+ /** Whether the pattern matches anywhere in the text (a search, not a full match). */
26
+ export declare function patternMatches(p: Pattern, text: string): boolean;
27
+ /** A value in integer milli-units (spec/findings.md §5a.2), or null. */
28
+ export declare function milliOf(v: unknown): number | null;
29
+ /** Why a schema is outside the subset, or undefined. */
30
+ export declare function checkSchema(schema: unknown): string | undefined;
31
+ /** The first place a value breaks a (checked) schema, or undefined (spec/findings.md §5a.3). */
32
+ export declare function schemaError(value: unknown, schema: unknown, path?: string): string | undefined;
33
+ /** Why a `tool_args` or `answer` predicate is invalid, or undefined. */
34
+ export declare function checkPredicate(p: unknown): string | undefined;
35
+ /** A tool call with its arguments, when they're on the record (`args` absent: unknown). */
36
+ export interface ArgUse {
37
+ seq: number;
38
+ tool: string;
39
+ args?: unknown;
40
+ hasArgs: boolean;
41
+ }
42
+ type Out = {
43
+ result: "pass" | "fail" | "cannot_verify";
44
+ gap?: string;
45
+ };
46
+ /** A `tool_args` predicate over the tool calls before `before` (spec/findings.md §5a.2). */
47
+ export declare function judgeArgs(arg: Record<string, unknown>, uses: readonly ArgUse[], before: number): Out;
48
+ /** The JSON an answer holds: the whole text, or one ``` fence around it; ABSENT when it isn't JSON. */
49
+ export declare function answerJson(text: string): unknown;
50
+ /** An `answer` predicate over the last final answer before `before` (spec/findings.md §5a.4). */
51
+ export declare function judgeAnswer(arg: Record<string, unknown>, answers: ReadonlyArray<{
52
+ seq: number;
53
+ text: string;
54
+ }>, before: number): Out;
55
+ export {};