@crewhaus/tool-proof 0.0.0-stage → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,365 @@
1
+ /**
2
+ * @crewhaus/tool-proof — the tools a harness proves claims with and checks
3
+ * a draft with before it answers (design/0.8.0-provability-release.md §7).
4
+ *
5
+ * ProofCheckers which proof checkers this harness can use, whether
6
+ * each can run here, and the language each checks
7
+ * ProofCheck run a checker on a proof and save the verdict as a
8
+ * `prf_…` artifact the `proven` rung can cite
9
+ * ProvabilityPreflight grade a draft with the gate's own verifier,
10
+ * without gating
11
+ *
12
+ * Nothing here cites for the model and nothing calls a model. A checker's
13
+ * exit status is the only thing `ProofCheck` trusts, and the preflight is
14
+ * `verifyDocument` — the function the gate calls — over the same evidence.
15
+ *
16
+ * `createProofTools` binds the three tools to one configuration: the
17
+ * composition root (`@crewhaus/evaluation-service`) calls it with the spec's
18
+ * lowered `provability:` block, so the gate and the tools agree on the
19
+ * floor, the checkers, the artifacts root and the evidence. The roots are
20
+ * resolved per call, for the tenant the call runs under: an instance built
21
+ * once at boot serves every tenant of a managed daemon (see
22
+ * {@link CreateProofToolsOptions.proofsRoot}). The exported
23
+ * builtin instances are what a spec gets by listing `proofCheck` (or
24
+ * `all-proof`) in `tools:` with no `provability:` block: the compiler's
25
+ * defaults at the `grounded` floor, checkers in `builtin, smt` order,
26
+ * artifacts under `<cwd>/.crewhaus/proofs/default/` (under the tenant's root
27
+ * inside a managed turn, see `./lib/roots`), and the run's own session log
28
+ * as evidence.
29
+ *
30
+ * Each tool is a thin wrapper over `./lib`, where the behaviour lives.
31
+ */
32
+ import { CrewhausError } from "@crewhaus/errors";
33
+ import { createCheckerRegistry, } from "@crewhaus/proof-checker";
34
+ import { createFileArtifactStore, newProofId, proofsRoot as proofsRootFor, } from "@crewhaus/provability";
35
+ import { createSandbox, sandboxAvailableFromEnv } from "@crewhaus/sandbox";
36
+ import { currentTenantContext } from "@crewhaus/tenancy";
37
+ import { buildTool } from "@crewhaus/tool-builder";
38
+ import { z } from "zod";
39
+ import { CHECK_GRACE_MS, PROOF_INPUT_LIMITS, checkReport, clip, proofCheckedFields, runChecker, toArtifact, } from "./lib/check";
40
+ import { buildDefaultCheckers, listCheckers, probeChecker } from "./lib/checkers";
41
+ import { DEFAULT_SPEC_NAME, assertConfig, defaultProvabilityConfig } from "./lib/config";
42
+ import { bridgeSandboxAvailable, eventBusOf, eventLogOf, sessionIdOf, sessionRootOf, specNameOf, warn, } from "./lib/context";
43
+ import { sessionEvidence } from "./lib/evidence";
44
+ import { DRAFT_CHARS, preflightBundle, preflightReport } from "./lib/preflight";
45
+ import { proofRecheck } from "./lib/recheck";
46
+ import { defaultProofsRoot, defaultSessionRoot, fenced, runtimeStateRoots } from "./lib/roots";
47
+ export { AVAILABILITY_TIMEOUT_MS, BUILTIN_ID, buildDefaultCheckers, defaultCheckerIds, listCheckers, probeChecker, sandboxImages, } from "./lib/checkers";
48
+ export { CHECK_GRACE_MS, OUTPUT_CHARS, PROOF_INPUT_LIMITS, REPORT_BYTES, checkReport, runChecker, toArtifact, } from "./lib/check";
49
+ export { DEFAULT_EXECUTION_SELECTORS, DEFAULT_FLOOR, DEFAULT_SPEC_NAME, defaultProvabilityConfig, } from "./lib/config";
50
+ export { currentTurn, sessionEvidence, startsTurn } from "./lib/evidence";
51
+ export { DRAFT_CHARS, NOTES_BYTES, ROW_BINDINGS, TABLE_ROWS, preflightBundle, preflightConfig, preflightReport, } from "./lib/preflight";
52
+ export { proofRecheck, recheckProof } from "./lib/recheck";
53
+ export { STATE_ENV_VARS, defaultProofsRoot, defaultSessionRoot, runtimeStateRoots, tenantRootOf, } from "./lib/roots";
54
+ /** Compact JSON — the reader is a model, not a person. */
55
+ const json = (value) => JSON.stringify(value);
56
+ /** A call the tools refuse: an unknown or unavailable checker, an artifact that could not be saved. */
57
+ export class ProofToolError extends CrewhausError {
58
+ name = "ProofToolError";
59
+ constructor(message, cause) {
60
+ super("tool", message, cause);
61
+ }
62
+ }
63
+ const checkersSchema = z.object({});
64
+ const checkSchema = z.object({
65
+ checker: z
66
+ .string()
67
+ .min(1)
68
+ .max(PROOF_INPUT_LIMITS.checkerChars)
69
+ .describe("The checker to use: an id ProofCheckers lists as available (builtin, smt, lean4)."),
70
+ claim: z
71
+ .string()
72
+ .min(1)
73
+ .max(PROOF_INPUT_LIMITS.claimChars)
74
+ .describe("The sentence your answer will carry, as you will write it. The gate checks that the cited sentence states it."),
75
+ statement: z
76
+ .string()
77
+ .min(1)
78
+ .max(PROOF_INPUT_LIMITS.statementChars)
79
+ .describe("The claim as a formal statement, in the checker's language."),
80
+ proof: z
81
+ .string()
82
+ .min(1)
83
+ .max(PROOF_INPUT_LIMITS.proofChars)
84
+ .describe("The proof, certificate or problem, in the checker's language."),
85
+ });
86
+ const isEvidenceIndex = (value) => typeof value === "object" &&
87
+ value !== null &&
88
+ typeof value.all === "function" &&
89
+ typeof value.bySource === "function" &&
90
+ typeof value.byToolUseId === "function";
91
+ const preflightSchema = z.object({
92
+ draft: z
93
+ .string()
94
+ .min(1)
95
+ .max(DRAFT_CHARS)
96
+ .describe("Your draft answer, exactly as you would send it, with its citation lines."),
97
+ });
98
+ /**
99
+ * Build the three proof tools over one configuration (see the module doc).
100
+ * Nothing is created here — no sandbox, no directory, no log read: each
101
+ * tool resolves what it needs on its first call.
102
+ */
103
+ export function createProofTools(opts = {}) {
104
+ const now = opts.now ?? (() => new Date());
105
+ if (opts.config !== undefined)
106
+ assertConfig(opts.config);
107
+ // A bad name is the caller's mistake: refuse it here, not on every call.
108
+ if (opts.specName !== undefined)
109
+ proofsRootFor(".", opts.specName);
110
+ // Duplicate ids are refused by the registry, also at construction.
111
+ const injected = opts.checkers !== undefined ? createCheckerRegistry(opts.checkers) : undefined;
112
+ let config = opts.config;
113
+ const configOf = () => {
114
+ config ??= defaultProvabilityConfig();
115
+ return config;
116
+ };
117
+ // The default checkers are built once, on first use, and kept: the
118
+ // sandbox checkers remember their image probe across calls.
119
+ let defaults;
120
+ const registryFor = (ctx) => {
121
+ if (injected !== undefined)
122
+ return injected;
123
+ defaults ??= createCheckerRegistry(buildDefaultCheckers(configOf(), {
124
+ sandboxAvailable: bridgeSandboxAvailable(ctx) || sandboxAvailableFromEnv(),
125
+ createSandbox: (sandboxOpts) => createSandbox({ allowedImages: sandboxOpts.allowedImages }),
126
+ }));
127
+ return defaults;
128
+ };
129
+ const workspaceRoot = () => opts.workspaceRoot ?? process.cwd();
130
+ // The tenant is read per call: it is the request's (AsyncLocalStorage),
131
+ // and one tools instance serves every tenant of a managed daemon.
132
+ const tenantNow = () => currentTenantContext()?.tenant;
133
+ const rootFor = (option, tenant, what) => {
134
+ if (typeof option !== "function")
135
+ return option;
136
+ const dir = option(tenant);
137
+ if (typeof dir !== "string" || dir === "") {
138
+ throw new ProofToolError(`the ${what} resolver returned no directory; nothing was done`);
139
+ }
140
+ return dir;
141
+ };
142
+ /** Where proofs are filed and read for this call, fenced to the active tenant. */
143
+ const proofsDirFor = (ctx) => {
144
+ const tenant = tenantNow();
145
+ const rootDir = rootFor(opts.proofsRoot, tenant, "proofsRoot") ??
146
+ defaultProofsRoot(workspaceRoot(), opts.specName ?? specNameOf(ctx) ?? DEFAULT_SPEC_NAME, tenant);
147
+ return fenced(rootDir, tenant);
148
+ };
149
+ const artifactsFor = (ctx) => createFileArtifactStore({ rootDir: proofsDirFor(ctx) });
150
+ /** The session-log root a preflight reads, when one was chosen (else event-log's default). */
151
+ const sessionRootFor = (ctx) => {
152
+ const tenant = tenantNow();
153
+ const chosen = rootFor(opts.sessionRootDir, tenant, "sessionRootDir") ??
154
+ sessionRootOf(ctx) ??
155
+ defaultSessionRoot(tenant);
156
+ // Event-log's default (`.crewhaus/sessions`, cwd-relative) when none was
157
+ // chosen; under a tenant one always is, and it must be the tenant's.
158
+ return chosen !== undefined ? fenced(chosen, tenant) : undefined;
159
+ };
160
+ const evidenceFor = async (ctx, scope) => {
161
+ if (opts.evidence !== undefined) {
162
+ let built;
163
+ try {
164
+ built = await opts.evidence(ctx);
165
+ }
166
+ catch (err) {
167
+ throw new ProofToolError(`ProvabilityPreflight could not read this run's evidence: ${err instanceof Error ? err.message : String(err)}`, err);
168
+ }
169
+ if (isEvidenceIndex(built))
170
+ return { index: built, notes: [] };
171
+ const scoped = built;
172
+ if (typeof scoped === "object" && scoped !== null && isEvidenceIndex(scoped.index)) {
173
+ if (scoped.turnNumbers !== undefined && !(scoped.turnNumbers instanceof Set)) {
174
+ throw new ProofToolError("ProvabilityPreflight could not read this run's evidence: turnNumbers must be a Set of turn numbers");
175
+ }
176
+ return {
177
+ index: scoped.index,
178
+ ...(scoped.turnNumbers !== undefined
179
+ ? { turnNumbers: scoped.turnNumbers }
180
+ : {}),
181
+ notes: [],
182
+ };
183
+ }
184
+ // Grading against nothing would fail every claim as not fetched: refuse.
185
+ throw new ProofToolError("ProvabilityPreflight could not read this run's evidence: the evidence builder returned no evidence index");
186
+ }
187
+ const tenant = tenantNow();
188
+ const sessionId = sessionIdOf(ctx);
189
+ const rootDir = sessionRootFor(ctx);
190
+ return sessionEvidence({
191
+ scope,
192
+ ...(sessionId !== undefined ? { sessionId } : {}),
193
+ ...(rootDir !== undefined ? { rootDir } : {}),
194
+ ...(opts.workspaceRoot !== undefined ? { workspaceRoot: opts.workspaceRoot } : {}),
195
+ // Inside a tenant scope the runtime spills to the tenant's root.
196
+ ...(tenant !== undefined ? { toolResultsRoot: tenant.toolResultRoot } : {}),
197
+ onUnreadable: (info) => warn(ctx, "tool-proof: a session log could not be read to its end", { ...info }),
198
+ });
199
+ };
200
+ /** "available: builtin; listed but not available here: smt (why)". */
201
+ const describeCheckers = async (registry) => {
202
+ const listing = await listCheckers(registry.list());
203
+ const up = listing.filter((c) => c.available).map((c) => c.id);
204
+ const down = listing.filter((c) => !c.available).map((c) => c.id);
205
+ return [
206
+ `available: ${up.length > 0 ? up.join(", ") : "none"}`,
207
+ ...(down.length > 0 ? [`listed but not available here: ${down.join(", ")}`] : []),
208
+ ].join("; ");
209
+ };
210
+ const proofCheckers = buildTool({
211
+ name: "ProofCheckers",
212
+ description: "List the proof checkers this harness can prove claims with, in preference order: whether each can run here (and why not), and the statement language each one checks. Use it before ProofCheck, so you write the statement and the proof in a language an available checker accepts.",
213
+ inputSchema: checkersSchema,
214
+ readOnly: true,
215
+ concurrencySafe: true,
216
+ execute: async (_input, ctx) => {
217
+ const listing = await listCheckers(registryFor(ctx).list());
218
+ return json({
219
+ floor: configOf().level,
220
+ checkers: listing,
221
+ usage: "Run ProofCheck with { checker, claim, statement, proof }, using the first available checker whose language can state the claim. On ok, cite the returned id as [n]: proof:<id> on the sentence that states the claim. A proof meets any floor.",
222
+ });
223
+ },
224
+ });
225
+ const proofCheck = buildTool({
226
+ name: "ProofCheck",
227
+ description: "Check a formal proof of one claim with a proof checker and save the verdict as a proof artifact. Give the checker id (see ProofCheckers), the claim sentence as your answer will state it, the formal statement, and the proof in that checker's language. Use it to back a claim with a machine-checked proof: on ok, cite the returned id as [n]: proof:<id>. A rejected proof is saved too, with the checker's reason and any counterexample, so fix the proof or drop the claim.",
228
+ inputSchema: checkSchema,
229
+ // Writes one new, immutable artifact file per call.
230
+ readOnly: false,
231
+ destructive: true,
232
+ // The one choice a call makes about where it acts is which checker
233
+ // runs: the builtin (a worker thread, no sandbox), or a container (smt,
234
+ // lean4). The artifact's place is fixed by the configuration, never by
235
+ // the call. Declaring it lets an operator allow the sandbox-free checker
236
+ // alone — `alwaysAllow ProofCheck(builtin)` — and ask before the rest.
237
+ operativeArgs: [{ field: "checker", kind: "id" }],
238
+ // Each call writes its own uniquely named artifact and shares no state with
239
+ // another, so two checks may run side by side.
240
+ concurrencySafe: true,
241
+ // NOT requiresSandbox: the builtin checker is a parser, not an executor,
242
+ // and needs none. The sandbox checkers (smt, lean4) refuse themselves when
243
+ // no container sandbox is wired, and on the noop backend (proof-checker's
244
+ // sandbox-common), so the floor is per checker, not per tool — a
245
+ // tool-level flag would deny the builtin on every sandbox-less harness.
246
+ execute: async (input, ctx) => {
247
+ const registry = registryFor(ctx);
248
+ const checker = registry.get(input.checker);
249
+ if (checker === undefined) {
250
+ throw new ProofToolError(`unknown proof checker "${clip(input.checker, 64)}" — ${await describeCheckers(registry)}. Run ProofCheckers to see what each one proves.`);
251
+ }
252
+ const availability = await probeChecker(checker);
253
+ if (!availability.ok) {
254
+ throw new ProofToolError(`the ${checker.id} checker cannot run here: ${availability.reason ?? "unavailable"} — ${await describeCheckers(registry)}. Nothing was checked or saved.`);
255
+ }
256
+ // Resolved (and tenant-fenced) before the check runs: a root the call
257
+ // may not write is refused before any work, not after it.
258
+ const artifacts = artifactsFor(ctx);
259
+ const result = await runChecker(checker, input, {
260
+ ...(ctx?.signal !== undefined ? { signal: ctx.signal } : {}),
261
+ timeoutMs: opts.checkTimeoutMs ?? configOf().formal.timeoutMs + CHECK_GRACE_MS,
262
+ });
263
+ if (ctx?.signal?.aborted === true) {
264
+ // A cancelled check is not a verdict on the proof.
265
+ throw new ProofToolError("ProofCheck was cancelled; nothing was saved");
266
+ }
267
+ const artifact = toArtifact({
268
+ id: newProofId(),
269
+ checker,
270
+ input,
271
+ result,
272
+ checkedAt: now(),
273
+ });
274
+ try {
275
+ await artifacts.put(artifact);
276
+ }
277
+ catch (err) {
278
+ throw new ProofToolError(`the ${checker.id} checker ${artifact.ok ? "accepted" : "rejected"} the proof, but the artifact could not be saved, so it cannot be cited: ${err instanceof Error ? err.message : String(err)}`, err);
279
+ }
280
+ const fields = proofCheckedFields(artifact);
281
+ // The trace event: fire-and-forget, like every bus publish.
282
+ const bus = eventBusOf(ctx);
283
+ if (bus !== undefined) {
284
+ try {
285
+ bus.publish({ ...bus.envelope(), kind: "proof_checked", ...fields });
286
+ }
287
+ catch (err) {
288
+ warn(ctx, "tool-proof: proof_checked could not be published", {
289
+ error: err instanceof Error ? err.message : String(err),
290
+ });
291
+ }
292
+ }
293
+ // The durable twin: the run's session event log, which the bridge hands
294
+ // to tools (`RuntimeBridge.eventLog`). The artifact file is the record a
295
+ // citation resolves against; the log line mirrors it, so a failed append
296
+ // is logged rather than turning a finished check into an error.
297
+ const log = eventLogOf(ctx);
298
+ if (log !== undefined) {
299
+ try {
300
+ await log.append({ kind: "proof_checked", payload: fields });
301
+ }
302
+ catch (err) {
303
+ warn(ctx, "tool-proof: proof_checked could not be appended to the event log", {
304
+ error: err instanceof Error ? err.message : String(err),
305
+ });
306
+ }
307
+ }
308
+ return json(checkReport(artifact));
309
+ },
310
+ });
311
+ const provabilityPreflight = buildTool({
312
+ name: "ProvabilityPreflight",
313
+ description: "Check a draft answer against the provability gate before you send it. Runs the verifier the gate runs, over the evidence this session has gathered, and returns the verdict, each claim with its evidence and status, and what to fix. Sends nothing and gates nothing. Use it on your final draft whenever your answers must carry cited, grounded, checked or proven claims.",
314
+ inputSchema: preflightSchema,
315
+ readOnly: true,
316
+ concurrencySafe: true,
317
+ execute: async (input, ctx) => {
318
+ const config = configOf();
319
+ const artifacts = artifactsFor(ctx);
320
+ const evidence = await evidenceFor(ctx, config.scope);
321
+ const tenant = tenantNow();
322
+ const sessionRootDir = sessionRootFor(ctx);
323
+ const { bundle, notes } = await preflightBundle(input.draft, {
324
+ config,
325
+ evidence: evidence.index,
326
+ artifacts,
327
+ workspaceRoot: workspaceRoot(),
328
+ // The directories the gate refuses path sources under, as the gate
329
+ // names them (`runtimeStateRoots`), so the two agree (design §6).
330
+ stateRoots: runtimeStateRoots({
331
+ workspaceRoot: workspaceRoot(),
332
+ proofsRoot: proofsDirFor(ctx),
333
+ ...(sessionRootDir !== undefined ? { sessionRootDir } : {}),
334
+ ...(tenant !== undefined ? { tenant } : {}),
335
+ }),
336
+ ...(evidence.turnNumbers !== undefined ? { turnNumbers: evidence.turnNumbers } : {}),
337
+ // The gate's re-check of a stored builtin proof, so a draft resting
338
+ // on a rewritten artifact is told what the gate will say.
339
+ recheckProof: proofRecheck({
340
+ timeoutMs: config.formal.timeoutMs,
341
+ ...(ctx?.signal !== undefined ? { signal: ctx.signal } : {}),
342
+ }),
343
+ now,
344
+ });
345
+ return json(preflightReport(bundle, [...evidence.notes, ...notes]));
346
+ },
347
+ });
348
+ return {
349
+ proofCheckers,
350
+ proofCheck,
351
+ provabilityPreflight,
352
+ all: [proofCheckers, proofCheck, provabilityPreflight],
353
+ };
354
+ }
355
+ /**
356
+ * The builtin instances (see the module doc). Building them creates
357
+ * nothing: the default config, checkers and sandbox are resolved on the
358
+ * first call.
359
+ */
360
+ const BUILTIN_PROOF_TOOLS = createProofTools();
361
+ export const proofCheckers = BUILTIN_PROOF_TOOLS.proofCheckers;
362
+ export const proofCheck = BUILTIN_PROOF_TOOLS.proofCheck;
363
+ export const provabilityPreflight = BUILTIN_PROOF_TOOLS.provabilityPreflight;
364
+ /** Every builtin proof tool, registration-ready. */
365
+ export const PROOF_TOOLS = BUILTIN_PROOF_TOOLS.all;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * One `ProofCheck`: run a checker, turn its verdict into the artifact the
3
+ * `proven` rung reads, and say what the model should do next (design §6.4,
4
+ * §7).
5
+ *
6
+ * The checker's verdict is the only thing trusted, and only `ok === true`
7
+ * is a proof. Everything a checker returns is normalised here, because a
8
+ * plugin checker implements the same interface and may get it wrong: a
9
+ * thrown error, a non-array of assumptions or an oversized output is a
10
+ * rejection or a clip, never a crash and never an accepted proof.
11
+ */
12
+ import type { CheckResult, ProofChecker } from "@crewhaus/proof-checker";
13
+ import { type ProofArtifact } from "@crewhaus/provability";
14
+ /**
15
+ * Input bounds. The statement and proof bounds are the largest any shipped
16
+ * checker accepts (`BUILTIN_LIMITS.maxStatementChars`, `.maxProofChars`), so
17
+ * the schema never refuses what a checker would take; each checker applies
18
+ * its own tighter limit and says so. A claim is one sentence of an answer.
19
+ */
20
+ export declare const PROOF_INPUT_LIMITS: {
21
+ readonly checkerChars: 64;
22
+ readonly claimChars: 2000;
23
+ readonly statementChars: 20000;
24
+ readonly proofChars: 1000000;
25
+ };
26
+ /** Checker output kept in an artifact (proof-checker's "a few KB"). */
27
+ export declare const OUTPUT_CHARS = 4000;
28
+ /**
29
+ * The most UTF-8 bytes a proof tool's report may be. The runtime spills any
30
+ * tool result over 10 KB (`@crewhaus/tool-result-store`'s
31
+ * `DEFAULT_THRESHOLD_BYTES`, 10 240) to a file and shows the model only its
32
+ * first 10 KB, so a report past that loses its tail — and the tail is where
33
+ * a JSON report's instructions would sit. Every report is cut to fit under
34
+ * it, with the instructions placed first, so the model always reads the
35
+ * whole of it.
36
+ */
37
+ export declare const REPORT_BYTES = 9000;
38
+ /**
39
+ * How much longer than `formal.timeoutMs` one check may take before it is
40
+ * recorded as a rejection: a container start and the checker's own
41
+ * reporting. The sandbox checkers stop themselves at `formal.timeoutMs`;
42
+ * this is the backstop for a plugin checker that never settles.
43
+ */
44
+ export declare const CHECK_GRACE_MS = 30000;
45
+ /** Keep the head and the tail of long text, marking the cut. */
46
+ export declare function clipMiddle(text: string, cap: number): string;
47
+ /** Text clipped to `cap` characters, an ellipsis marking the cut. */
48
+ export declare function clip(text: string, cap: number): string;
49
+ /** The UTF-8 bytes `value` takes in a compact JSON report. */
50
+ export declare function jsonBytes(value: unknown): number;
51
+ /**
52
+ * `text` cut (by `cut`, `clipMiddle` by default) until it takes at most
53
+ * `maxBytes` as a JSON string. Measured, not estimated: escaping turns a
54
+ * newline into two bytes and a control character into six, and a character
55
+ * can take three bytes of UTF-8. Empty when not even the cut marker fits.
56
+ */
57
+ export declare function fitText(text: string, maxBytes: number, cut?: (text: string, cap: number) => string): string;
58
+ export type RunCheckerOptions = {
59
+ /** The call's signal: an abort ends the wait at once (the caller then saves nothing). */
60
+ readonly signal?: AbortSignal;
61
+ /**
62
+ * The longest the check may take before it is recorded as a rejection
63
+ * (`formal.timeoutMs` + {@link CHECK_GRACE_MS} from the tool). Absent: no
64
+ * backstop.
65
+ */
66
+ readonly timeoutMs?: number;
67
+ };
68
+ /**
69
+ * Run `checker` and return a well-formed `CheckResult`, whatever it does.
70
+ * A checker that throws has not proved anything: the result is a rejection
71
+ * whose reason names the failure, and it is persisted like any rejection.
72
+ * So is one that does not settle within `timeoutMs`. An abort of `signal`
73
+ * stops the wait too, with a rejection the caller recognises by the aborted
74
+ * signal and does not save. Neither stops a checker that ignores its signal
75
+ * — nothing can — but the call returns, so one hung plugin cannot hang the
76
+ * turn.
77
+ */
78
+ export declare function runChecker(checker: ProofChecker, input: {
79
+ readonly claim: string;
80
+ readonly statement: string;
81
+ readonly proof: string;
82
+ }, opts?: RunCheckerOptions): Promise<CheckResult>;
83
+ /**
84
+ * The artifact for one check — accepted or rejected alike: a rejected proof
85
+ * is persisted too, because proof pressure is signal (the `action_proof`
86
+ * rule from tool-plan), and `proof_rejected` in a bundle can then name the
87
+ * checker's reason. The `checker` is the id the model asked for, which is
88
+ * the registry's identity for the checker that ran.
89
+ */
90
+ export declare function toArtifact(args: {
91
+ readonly id: string;
92
+ readonly checker: ProofChecker;
93
+ readonly input: {
94
+ readonly claim: string;
95
+ readonly statement: string;
96
+ readonly proof: string;
97
+ };
98
+ readonly result: CheckResult;
99
+ readonly checkedAt: Date;
100
+ }): ProofArtifact;
101
+ /** The `proof_checked` payload: the trace event's fields, and the event-log line's whole payload. */
102
+ export declare function proofCheckedFields(artifact: ProofArtifact): {
103
+ readonly checker: string;
104
+ readonly ok: boolean;
105
+ readonly artifactId: string;
106
+ readonly durationMs: number;
107
+ readonly axioms: readonly string[];
108
+ };
109
+ /**
110
+ * As many of `items` as fit in `maxBytes`, each clipped to `itemChars`, in
111
+ * order, then `more(n)` counting the n left out — which the caller says
112
+ * where to find (the artifact keeps a ProofCheck's whole).
113
+ */
114
+ export declare function listed(items: ReadonlyArray<string>, maxBytes: number, more?: (rest: number) => string, itemChars?: number): string[];
115
+ /**
116
+ * What the model reads back: the verdict, the artifact id and one
117
+ * instruction — how to cite it, or what to do instead — first, then why
118
+ * (reason, counterexample), what the proof rests on and the checker's
119
+ * output. The whole report fits in {@link REPORT_BYTES}, the output taking
120
+ * what the rest leaves; the artifact keeps everything whole.
121
+ */
122
+ export declare function checkReport(artifact: ProofArtifact): Record<string, unknown>;