ai-capability-framework 1.0.3 → 1.1.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.
- package/CHANGELOG.md +42 -0
- package/README.md +38 -30
- package/ROADMAP.md +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +32 -1
- package/dist/discovery/bindings.d.ts +7 -0
- package/dist/discovery/bindings.d.ts.map +1 -0
- package/dist/discovery/bindings.js +24 -0
- package/dist/discovery/budget.d.ts +17 -0
- package/dist/discovery/budget.d.ts.map +1 -0
- package/dist/discovery/budget.js +77 -0
- package/dist/discovery/configuration.d.ts +10 -0
- package/dist/discovery/configuration.d.ts.map +1 -0
- package/dist/discovery/configuration.js +63 -0
- package/dist/discovery/corpus.d.ts +8 -0
- package/dist/discovery/corpus.d.ts.map +1 -0
- package/dist/discovery/corpus.js +64 -0
- package/dist/discovery/diagnostics.d.ts +15 -0
- package/dist/discovery/diagnostics.d.ts.map +1 -0
- package/dist/discovery/diagnostics.js +11 -0
- package/dist/discovery/discoverer.d.ts +23 -0
- package/dist/discovery/discoverer.d.ts.map +1 -0
- package/dist/discovery/discoverer.js +261 -0
- package/dist/discovery/embedding-configuration.d.ts +12 -0
- package/dist/discovery/embedding-configuration.d.ts.map +1 -0
- package/dist/discovery/embedding-configuration.js +81 -0
- package/dist/discovery/embedding-types.d.ts +75 -0
- package/dist/discovery/embedding-types.d.ts.map +1 -0
- package/dist/discovery/embedding-types.js +1 -0
- package/dist/discovery/embeddings.d.ts +38 -0
- package/dist/discovery/embeddings.d.ts.map +1 -0
- package/dist/discovery/embeddings.js +71 -0
- package/dist/discovery/evaluation-validation.d.ts +8 -0
- package/dist/discovery/evaluation-validation.d.ts.map +1 -0
- package/dist/discovery/evaluation-validation.js +19 -0
- package/dist/discovery/evaluation.d.ts +84 -0
- package/dist/discovery/evaluation.d.ts.map +1 -0
- package/dist/discovery/evaluation.js +310 -0
- package/dist/discovery/export.d.ts +14 -0
- package/dist/discovery/export.d.ts.map +1 -0
- package/dist/discovery/export.js +81 -0
- package/dist/discovery/facade.d.ts +82 -0
- package/dist/discovery/facade.d.ts.map +1 -0
- package/dist/discovery/facade.js +266 -0
- package/dist/discovery/fingerprint.d.ts +71 -0
- package/dist/discovery/fingerprint.d.ts.map +1 -0
- package/dist/discovery/fingerprint.js +67 -0
- package/dist/discovery/index-builder.d.ts +3 -0
- package/dist/discovery/index-builder.d.ts.map +1 -0
- package/dist/discovery/index-builder.js +129 -0
- package/dist/discovery/index-validation.d.ts +12 -0
- package/dist/discovery/index-validation.d.ts.map +1 -0
- package/dist/discovery/index-validation.js +117 -0
- package/dist/discovery/index.d.ts +32 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +18 -0
- package/dist/discovery/json-snapshot.d.ts +3 -0
- package/dist/discovery/json-snapshot.d.ts.map +1 -0
- package/dist/discovery/json-snapshot.js +11 -0
- package/dist/discovery/lexical.d.ts +12 -0
- package/dist/discovery/lexical.d.ts.map +1 -0
- package/dist/discovery/lexical.js +26 -0
- package/dist/discovery/metadata.d.ts +4 -0
- package/dist/discovery/metadata.d.ts.map +1 -0
- package/dist/discovery/metadata.js +33 -0
- package/dist/discovery/ranking.d.ts +7 -0
- package/dist/discovery/ranking.d.ts.map +1 -0
- package/dist/discovery/ranking.js +68 -0
- package/dist/discovery/replay.d.ts +50 -0
- package/dist/discovery/replay.d.ts.map +1 -0
- package/dist/discovery/replay.js +175 -0
- package/dist/discovery/retrieval-policy.d.ts +5 -0
- package/dist/discovery/retrieval-policy.d.ts.map +1 -0
- package/dist/discovery/retrieval-policy.js +10 -0
- package/dist/discovery/router.d.ts +39 -0
- package/dist/discovery/router.d.ts.map +1 -0
- package/dist/discovery/router.js +98 -0
- package/dist/discovery/selection-attempt.d.ts +20 -0
- package/dist/discovery/selection-attempt.d.ts.map +1 -0
- package/dist/discovery/selection-attempt.js +39 -0
- package/dist/discovery/selection-cards.d.ts +12 -0
- package/dist/discovery/selection-cards.d.ts.map +1 -0
- package/dist/discovery/selection-cards.js +79 -0
- package/dist/discovery/selection-configuration.d.ts +18 -0
- package/dist/discovery/selection-configuration.d.ts.map +1 -0
- package/dist/discovery/selection-configuration.js +71 -0
- package/dist/discovery/selection-response.d.ts +13 -0
- package/dist/discovery/selection-response.d.ts.map +1 -0
- package/dist/discovery/selection-response.js +47 -0
- package/dist/discovery/selection-types.d.ts +18 -0
- package/dist/discovery/selection-types.d.ts.map +1 -0
- package/dist/discovery/selection-types.js +1 -0
- package/dist/discovery/selection.d.ts +6 -0
- package/dist/discovery/selection.d.ts.map +1 -0
- package/dist/discovery/selection.js +145 -0
- package/dist/discovery/types.d.ts +291 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +1 -0
- package/dist/discovery/usage.d.ts +2 -0
- package/dist/discovery/usage.d.ts.map +1 -0
- package/dist/discovery/usage.js +1 -0
- package/dist/discovery/vectors.d.ts +5 -0
- package/dist/discovery/vectors.d.ts.map +1 -0
- package/dist/discovery/vectors.js +43 -0
- package/dist/discovery/versions.d.ts +37 -0
- package/dist/discovery/versions.d.ts.map +1 -0
- package/dist/discovery/versions.js +36 -0
- package/dist/evals-live/live-eval-runner.d.ts +1 -0
- package/dist/evals-live/live-eval-runner.d.ts.map +1 -1
- package/dist/evals-live/live-eval-runner.js +12 -1
- package/dist/evals-live/types.d.ts +3 -0
- package/dist/evals-live/types.d.ts.map +1 -1
- package/dist/generated/manifest-types.d.ts +3655 -0
- package/dist/generated/manifest-types.d.ts.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +8 -0
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +11 -2
- package/dist/mcp-server/types.d.ts +4 -0
- package/dist/mcp-server/types.d.ts.map +1 -1
- package/dist/observability/redaction.d.ts.map +1 -1
- package/dist/observability/redaction.js +6 -2
- package/dist/observability/types.d.ts +1 -1
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/openai/index.d.ts +2 -0
- package/dist/openai/index.d.ts.map +1 -1
- package/dist/openai/index.js +1 -0
- package/dist/openai/responses-runner.d.ts +1 -1
- package/dist/openai/responses-runner.d.ts.map +1 -1
- package/dist/openai/responses-runner.js +134 -7
- package/dist/openai/selection-client.d.ts +28 -0
- package/dist/openai/selection-client.d.ts.map +1 -0
- package/dist/openai/selection-client.js +95 -0
- package/dist/openai/tool-loop.js +1 -1
- package/dist/openai/types.d.ts +19 -2
- package/dist/openai/types.d.ts.map +1 -1
- package/dist/replay/discovery-snapshot.d.ts +10 -0
- package/dist/replay/discovery-snapshot.d.ts.map +1 -0
- package/dist/replay/discovery-snapshot.js +17 -0
- package/dist/replay/recorder.d.ts.map +1 -1
- package/dist/replay/recorder.js +4 -0
- package/dist/replay/redaction.js +5 -1
- package/dist/replay/replayer.d.ts.map +1 -1
- package/dist/replay/replayer.js +14 -1
- package/dist/replay/types.d.ts +3 -0
- package/dist/replay/types.d.ts.map +1 -1
- package/dist/replay/validation.d.ts.map +1 -1
- package/dist/replay/validation.js +6 -0
- package/dist/runtime/bounded-json.d.ts +4 -0
- package/dist/runtime/bounded-json.d.ts.map +1 -0
- package/dist/runtime/bounded-json.js +74 -0
- package/dist/runtime/eligibility.d.ts +4 -0
- package/dist/runtime/eligibility.d.ts.map +1 -0
- package/dist/runtime/eligibility.js +128 -0
- package/dist/runtime/index.d.ts +5 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/provider-attempt.d.ts +11 -0
- package/dist/runtime/provider-attempt.d.ts.map +1 -0
- package/dist/runtime/provider-attempt.js +221 -0
- package/dist/runtime/provider-run.d.ts +10 -0
- package/dist/runtime/provider-run.d.ts.map +1 -0
- package/dist/runtime/provider-run.js +123 -0
- package/dist/runtime/provider-stop.d.ts +9 -0
- package/dist/runtime/provider-stop.d.ts.map +1 -0
- package/dist/runtime/provider-stop.js +28 -0
- package/dist/runtime/provider-types.d.ts +189 -0
- package/dist/runtime/provider-types.d.ts.map +1 -0
- package/dist/runtime/provider-types.js +1 -0
- package/dist/runtime/provider-usage.d.ts +9 -0
- package/dist/runtime/provider-usage.d.ts.map +1 -0
- package/dist/runtime/provider-usage.js +69 -0
- package/dist/runtime/router.d.ts.map +1 -1
- package/dist/runtime/router.js +3 -118
- package/dist/runtime/types.d.ts +9 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validator.d.ts +3 -0
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +32 -0
- package/docs/api/public-api-policy.md +10 -0
- package/docs/api/stable-workflow.md +58 -0
- package/docs/api.md +7 -0
- package/docs/capability-manifests.md +24 -0
- package/docs/development.md +22 -0
- package/docs/discovery.md +186 -0
- package/docs/evals/discovery-live.md +199 -0
- package/docs/evals/discovery-qualification.md +158 -0
- package/docs/evals/overview.md +9 -0
- package/docs/getting-started/installation.md +11 -12
- package/docs/getting-started/openai-quickstart.md +16 -5
- package/docs/getting-started/provider-neutral-quickstart.md +247 -88
- package/docs/getting-started/quickstart.md +8 -30
- package/docs/index.md +10 -0
- package/docs/release.md +29 -5
- package/docs/runtime.md +27 -0
- package/docs/start-here.md +23 -134
- package/examples/12-capability-discovery-selection/README.md +346 -0
- package/examples/12-capability-discovery-selection/customer-cases.mjs +52 -0
- package/examples/12-capability-discovery-selection/customer-harness.mjs +91 -0
- package/examples/12-capability-discovery-selection/customer-request-example.mjs +320 -0
- package/examples/12-capability-discovery-selection/discovery-example.mjs +175 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.suite.json +7887 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v2.suite.json +8167 -0
- package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v3.suite.json +8342 -0
- package/examples/12-capability-discovery-selection/evaluation/application.mjs +305 -0
- package/examples/12-capability-discovery-selection/evaluation/campaign.mjs +248 -0
- package/examples/12-capability-discovery-selection/evaluation/clients.mjs +433 -0
- package/examples/12-capability-discovery-selection/evaluation/continuation.mjs +208 -0
- package/examples/12-capability-discovery-selection/evaluation/durable-budget.mjs +420 -0
- package/examples/12-capability-discovery-selection/evaluation/freeze.mjs +9 -0
- package/examples/12-capability-discovery-selection/evaluation/harness.mjs +618 -0
- package/examples/12-capability-discovery-selection/evaluation/mock-host.mjs +260 -0
- package/examples/12-capability-discovery-selection/evaluation/preflight.mjs +115 -0
- package/examples/12-capability-discovery-selection/evaluation/qualification-workflows.mjs +87 -0
- package/examples/12-capability-discovery-selection/evaluation/regrade.mjs +281 -0
- package/examples/12-capability-discovery-selection/evaluation/report.mjs +517 -0
- package/examples/12-capability-discovery-selection/evaluation/runner.mjs +613 -0
- package/examples/12-capability-discovery-selection/evaluation/saved-campaign.mjs +385 -0
- package/examples/12-capability-discovery-selection/evaluation/wording.mjs +115 -0
- package/examples/12-capability-discovery-selection/evaluation/workflow-application.mjs +378 -0
- package/examples/12-capability-discovery-selection/evaluation/workflow-cases.mjs +90 -0
- package/examples/12-capability-discovery-selection/evaluation-example.mjs +154 -0
- package/examples/12-capability-discovery-selection/hybrid-example.mjs +174 -0
- package/examples/12-capability-discovery-selection/mock-embeddings.mjs +101 -0
- package/examples/12-capability-discovery-selection/mock-selection.mjs +78 -0
- package/examples/12-capability-discovery-selection/pipeline-example.mjs +496 -0
- package/examples/12-capability-discovery-selection/retrieval-cases.mjs +31 -0
- package/examples/12-capability-discovery-selection/run-campaign-mock.mjs +40 -0
- package/examples/12-capability-discovery-selection/run-customer-mock.mjs +15 -0
- package/examples/12-capability-discovery-selection/run-evaluation-live.mjs +10 -0
- package/examples/12-capability-discovery-selection/run-evaluation-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-evaluation-regrade.mjs +71 -0
- package/examples/12-capability-discovery-selection/run-hybrid-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/run-pipeline-mock.mjs +2 -0
- package/examples/12-capability-discovery-selection/run-selector-mock.mjs +3 -0
- package/examples/12-capability-discovery-selection/selection-example.mjs +88 -0
- package/package.json +17 -4
- package/schemas/discovery/embedding-index.schema.json +71 -0
- package/schemas/discovery/eval-observations.schema.json +409 -0
- package/schemas/discovery/eval-suite.schema.json +375 -0
- package/schemas/discovery/metadata.schema.json +30 -0
- package/schemas/discovery/selection-response.schema.json +47 -0
- package/schemas/replay/discovery-snapshot.schema.json +335 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import {
|
|
5
|
+
buildCapabilitySearchCorpus,
|
|
6
|
+
hashDiscoveryContent,
|
|
7
|
+
fingerprintDiscoveryEvaluationSuite,
|
|
8
|
+
validateCapabilityEmbeddingIndex,
|
|
9
|
+
DISCOVERY_STANDARD_PROFILE
|
|
10
|
+
} from "ai-capability-framework/discovery";
|
|
11
|
+
import {
|
|
12
|
+
campaignBinding,
|
|
13
|
+
loadSuite,
|
|
14
|
+
registryFor,
|
|
15
|
+
trialSchedule,
|
|
16
|
+
MODES,
|
|
17
|
+
CAPS
|
|
18
|
+
} from "./campaign.mjs";
|
|
19
|
+
import { QUALIFICATION_WORKFLOWS } from "./qualification-workflows.mjs";
|
|
20
|
+
import { FROZEN_V3, FROZEN_QUALIFICATION_WORKFLOWS } from "./freeze.mjs";
|
|
21
|
+
|
|
22
|
+
export const byteHash = (bytes) =>
|
|
23
|
+
`sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
24
|
+
const requireEvidence = (condition) => {
|
|
25
|
+
if (!condition)
|
|
26
|
+
throw new Error("Saved campaign evidence is incomplete or incompatible.");
|
|
27
|
+
};
|
|
28
|
+
export function readBounded(filename, maximum) {
|
|
29
|
+
requireEvidence(
|
|
30
|
+
fs.statSync(filename).isFile() && fs.statSync(filename).size <= maximum
|
|
31
|
+
);
|
|
32
|
+
const bytes = fs.readFileSync(filename);
|
|
33
|
+
requireEvidence(bytes.length <= maximum);
|
|
34
|
+
return bytes;
|
|
35
|
+
}
|
|
36
|
+
const parse = (filename, maximum) =>
|
|
37
|
+
JSON.parse(readBounded(filename, maximum).toString("utf8"));
|
|
38
|
+
const fingerprint = (value) =>
|
|
39
|
+
typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value);
|
|
40
|
+
const quantities = ["providerCalls", "inputTokens", "outputTokens", "estimatedCost"];
|
|
41
|
+
const same = (left, right) =>
|
|
42
|
+
hashDiscoveryContent(left) === hashDiscoveryContent(right);
|
|
43
|
+
const close = (left, right) =>
|
|
44
|
+
typeof right === "number" &&
|
|
45
|
+
Math.abs(left - right) <= Math.max(1e-10, Math.abs(left) * 1e-10);
|
|
46
|
+
|
|
47
|
+
export function readSavedArtifact(directory, reference) {
|
|
48
|
+
requireEvidence(
|
|
49
|
+
reference &&
|
|
50
|
+
/^[a-zA-Z0-9_.-]+\.json$/.test(reference.name) &&
|
|
51
|
+
fingerprint(reference.hash)
|
|
52
|
+
);
|
|
53
|
+
const filename = path.join(directory, reference.name);
|
|
54
|
+
requireEvidence(
|
|
55
|
+
path.dirname(fs.realpathSync(filename)) === fs.realpathSync(directory)
|
|
56
|
+
);
|
|
57
|
+
const bytes = readBounded(filename, 16 * 1024 * 1024),
|
|
58
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
59
|
+
requireEvidence(hashDiscoveryContent(value) === reference.hash);
|
|
60
|
+
return { value, bytesHash: byteHash(bytes) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function validateSavedSchedule(records, trials, suite) {
|
|
64
|
+
const scheduled = [
|
|
65
|
+
"administrative-index",
|
|
66
|
+
...["train", "baseline", "qualify"].flatMap((stage) =>
|
|
67
|
+
trialSchedule(suite, stage).map((t) => t.key)
|
|
68
|
+
),
|
|
69
|
+
...QUALIFICATION_WORKFLOWS.flatMap((w) =>
|
|
70
|
+
MODES.map((mode) => `workflow.${w.id}.${mode.replace("/", ".")}`)
|
|
71
|
+
)
|
|
72
|
+
];
|
|
73
|
+
requireEvidence(
|
|
74
|
+
same([...trials.keys()], scheduled) && [...trials.values()].every((t) => !t.pending)
|
|
75
|
+
);
|
|
76
|
+
requireEvidence(
|
|
77
|
+
same(
|
|
78
|
+
records.filter((r) => r.type === "stage.complete").map((r) => r.data),
|
|
79
|
+
[
|
|
80
|
+
...["train", "baseline", "qualify"].map((stage) => ({
|
|
81
|
+
stage,
|
|
82
|
+
hash: hashDiscoveryContent(trialSchedule(suite, stage)),
|
|
83
|
+
count: trialSchedule(suite, stage).length
|
|
84
|
+
})),
|
|
85
|
+
{ stage: "workflows", count: 48 }
|
|
86
|
+
]
|
|
87
|
+
) &&
|
|
88
|
+
records.filter((r) => r.type === "profile.freeze").length === 1 &&
|
|
89
|
+
records.find((r) => r.type === "profile.freeze").data.k === 8
|
|
90
|
+
);
|
|
91
|
+
return scheduled;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Reads immutable evidence only. Never opens a campaign ledger or host configuration. */
|
|
95
|
+
export function readSavedCampaign(inputPath) {
|
|
96
|
+
const manifest = parse(inputPath, 64_000);
|
|
97
|
+
requireEvidence(
|
|
98
|
+
Object.keys(manifest).sort().join() ===
|
|
99
|
+
"campaignDirectory,campaignFingerprint,journalHash,reportHash,reportPath,schemaVersion"
|
|
100
|
+
);
|
|
101
|
+
requireEvidence(
|
|
102
|
+
manifest.schemaVersion === "1.0" &&
|
|
103
|
+
[manifest.campaignFingerprint, manifest.journalHash, manifest.reportHash].every(
|
|
104
|
+
fingerprint
|
|
105
|
+
) &&
|
|
106
|
+
[manifest.campaignDirectory, manifest.reportPath].every(
|
|
107
|
+
(p) => typeof p === "string" && p.trim()
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
const directory = fs.realpathSync(
|
|
111
|
+
path.resolve(path.dirname(inputPath), manifest.campaignDirectory)
|
|
112
|
+
);
|
|
113
|
+
const reportPath = fs.realpathSync(
|
|
114
|
+
path.resolve(path.dirname(inputPath), manifest.reportPath)
|
|
115
|
+
);
|
|
116
|
+
requireEvidence(!fs.existsSync(path.join(directory, "campaign.lock")));
|
|
117
|
+
const journal = readBounded(path.join(directory, "journal.jsonl"), 64 * 1024 * 1024);
|
|
118
|
+
const reportBytes = readBounded(reportPath, 16 * 1024 * 1024);
|
|
119
|
+
requireEvidence(
|
|
120
|
+
byteHash(journal) === manifest.journalHash &&
|
|
121
|
+
byteHash(reportBytes) === manifest.reportHash &&
|
|
122
|
+
journal.at(-1) === 10
|
|
123
|
+
);
|
|
124
|
+
const original = JSON.parse(reportBytes.toString("utf8"));
|
|
125
|
+
requireEvidence(
|
|
126
|
+
original.status === "evaluation_completed" &&
|
|
127
|
+
original.report?.complete &&
|
|
128
|
+
original.campaignFingerprint === manifest.campaignFingerprint
|
|
129
|
+
);
|
|
130
|
+
const suite = loadSuite(),
|
|
131
|
+
registry = registryFor(suite),
|
|
132
|
+
corpus = buildCapabilitySearchCorpus(registry);
|
|
133
|
+
requireEvidence(
|
|
134
|
+
fingerprintDiscoveryEvaluationSuite(suite) === FROZEN_V3 &&
|
|
135
|
+
hashDiscoveryContent(QUALIFICATION_WORKFLOWS) === FROZEN_QUALIFICATION_WORKFLOWS
|
|
136
|
+
);
|
|
137
|
+
const records = journal
|
|
138
|
+
.toString("utf8")
|
|
139
|
+
.trimEnd()
|
|
140
|
+
.split("\n")
|
|
141
|
+
.map((line) => {
|
|
142
|
+
requireEvidence(line.length <= 128_000);
|
|
143
|
+
return JSON.parse(line);
|
|
144
|
+
});
|
|
145
|
+
const head = records[0]?.data;
|
|
146
|
+
requireEvidence(
|
|
147
|
+
records[0]?.type === "campaign" &&
|
|
148
|
+
head?.fingerprint === manifest.campaignFingerprint &&
|
|
149
|
+
hashDiscoveryContent({ binding: head.binding, caps: head.caps }) ===
|
|
150
|
+
head.fingerprint
|
|
151
|
+
);
|
|
152
|
+
const { predecessor, ...binding } = head.binding;
|
|
153
|
+
requireEvidence(
|
|
154
|
+
same(
|
|
155
|
+
binding,
|
|
156
|
+
campaignBinding(
|
|
157
|
+
suite,
|
|
158
|
+
QUALIFICATION_WORKFLOWS,
|
|
159
|
+
binding.sourceFingerprint,
|
|
160
|
+
binding.hostIdentity
|
|
161
|
+
)
|
|
162
|
+
) &&
|
|
163
|
+
fingerprint(binding.sourceFingerprint) &&
|
|
164
|
+
binding.hostIdentity.mock === false &&
|
|
165
|
+
predecessor &&
|
|
166
|
+
Object.keys(CAPS).every(
|
|
167
|
+
(key) =>
|
|
168
|
+
Number.isFinite(head.caps[key]) &&
|
|
169
|
+
head.caps[key] >= 0 &&
|
|
170
|
+
head.caps[key] <= CAPS[key]
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
const artifacts = [];
|
|
174
|
+
const readArtifact = (reference) => {
|
|
175
|
+
const { value, bytesHash } = readSavedArtifact(directory, reference);
|
|
176
|
+
artifacts.push({
|
|
177
|
+
name: reference.name,
|
|
178
|
+
hash: reference.hash,
|
|
179
|
+
bytesHash
|
|
180
|
+
});
|
|
181
|
+
return value;
|
|
182
|
+
};
|
|
183
|
+
const { values, trials, charged, httpRequests, index } = verifySavedJournal(
|
|
184
|
+
records,
|
|
185
|
+
readArtifact
|
|
186
|
+
);
|
|
187
|
+
const scheduled = validateSavedSchedule(records, trials, suite);
|
|
188
|
+
const ledger = original.report.ledger;
|
|
189
|
+
requireEvidence(
|
|
190
|
+
!ledger.halted &&
|
|
191
|
+
ledger.pendingReservations === 0 &&
|
|
192
|
+
ledger.pendingHttp === 0 &&
|
|
193
|
+
ledger.pendingTrials === 0 &&
|
|
194
|
+
ledger.completedTrials === scheduled.length &&
|
|
195
|
+
ledger.httpRequests === httpRequests &&
|
|
196
|
+
ledger.fingerprint === head.fingerprint &&
|
|
197
|
+
same(ledger.caps, head.caps) &&
|
|
198
|
+
quantities.every(
|
|
199
|
+
(key) =>
|
|
200
|
+
close(charged[key], ledger.charged[key]) && charged[key] <= head.caps[key]
|
|
201
|
+
) &&
|
|
202
|
+
httpRequests <= head.caps.httpRequests
|
|
203
|
+
);
|
|
204
|
+
const identity = hashDiscoveryContent({
|
|
205
|
+
execution: binding.execution,
|
|
206
|
+
tokenizerIdentity: binding.hostIdentity.tokenizerIdentity,
|
|
207
|
+
pricing: binding.hostIdentity.pricing,
|
|
208
|
+
mock: false
|
|
209
|
+
});
|
|
210
|
+
requireEvidence(
|
|
211
|
+
index &&
|
|
212
|
+
index.indexFingerprint === original.indexFingerprint &&
|
|
213
|
+
validateCapabilityEmbeddingIndex(index, {
|
|
214
|
+
corpus,
|
|
215
|
+
configuration: {
|
|
216
|
+
provider: "openai",
|
|
217
|
+
model: binding.execution.embeddingModel,
|
|
218
|
+
dimensions: 1536,
|
|
219
|
+
configurationId: `evaluation.embedding.${identity.slice(7, 23)}`
|
|
220
|
+
},
|
|
221
|
+
mock: false,
|
|
222
|
+
limits: DISCOVERY_STANDARD_PROFILE.indexLimits
|
|
223
|
+
}).valid
|
|
224
|
+
);
|
|
225
|
+
const expectedTrials = ["train", "baseline", "qualify"].flatMap((stage) =>
|
|
226
|
+
trialSchedule(suite, stage)
|
|
227
|
+
);
|
|
228
|
+
const taskValues = values.filter((v) => v.observation),
|
|
229
|
+
workflows = values.filter((v) => v.workflowId);
|
|
230
|
+
requireEvidence(
|
|
231
|
+
same(
|
|
232
|
+
taskValues.map((v) => v.trial),
|
|
233
|
+
expectedTrials
|
|
234
|
+
) &&
|
|
235
|
+
workflows.length === 48 &&
|
|
236
|
+
values.length === scheduled.length
|
|
237
|
+
);
|
|
238
|
+
for (const value of taskValues)
|
|
239
|
+
requireEvidence(
|
|
240
|
+
trials.get(value.trial.key).binding ===
|
|
241
|
+
hashDiscoveryContent({
|
|
242
|
+
trial: value.trial,
|
|
243
|
+
campaign: head.fingerprint,
|
|
244
|
+
index: value.trial.mode.startsWith("hybrid") ? index.indexFingerprint : null
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
for (const scenario of QUALIFICATION_WORKFLOWS)
|
|
248
|
+
for (const mode of MODES) {
|
|
249
|
+
const matching = workflows.filter(
|
|
250
|
+
(w) => w.workflowId === scenario.id && w.mode === mode
|
|
251
|
+
);
|
|
252
|
+
requireEvidence(
|
|
253
|
+
matching.length === 1 &&
|
|
254
|
+
matching[0].kind === scenario.kind &&
|
|
255
|
+
trials.get(`workflow.${scenario.id}.${mode.replace("/", ".")}`).binding ===
|
|
256
|
+
hashDiscoveryContent({
|
|
257
|
+
workflow: hashDiscoveryContent(scenario),
|
|
258
|
+
mode,
|
|
259
|
+
k: 8,
|
|
260
|
+
campaign: head.fingerprint
|
|
261
|
+
})
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
manifest,
|
|
266
|
+
original,
|
|
267
|
+
suite,
|
|
268
|
+
registry,
|
|
269
|
+
taskValues,
|
|
270
|
+
workflows,
|
|
271
|
+
ledger,
|
|
272
|
+
artifacts,
|
|
273
|
+
directory,
|
|
274
|
+
reportPath,
|
|
275
|
+
binding,
|
|
276
|
+
trialArtifacts: Object.fromEntries(
|
|
277
|
+
[...trials].map(([key, value]) => [key, value.artifact])
|
|
278
|
+
),
|
|
279
|
+
journalRecords: records.length
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Pure journal reconciliation, also used by synthetic integrity regressions. */
|
|
284
|
+
export function verifySavedJournal(records, readArtifact) {
|
|
285
|
+
requireEvidence(records[0]?.type === "campaign");
|
|
286
|
+
const values = [],
|
|
287
|
+
trials = new Map(),
|
|
288
|
+
reservations = new Map(),
|
|
289
|
+
settled = new Set(),
|
|
290
|
+
http = new Set(),
|
|
291
|
+
httpSeen = new Set();
|
|
292
|
+
const charged = Object.fromEntries(quantities.map((key) => [key, 0]));
|
|
293
|
+
let previous = null,
|
|
294
|
+
httpRequests = 0,
|
|
295
|
+
index = null;
|
|
296
|
+
for (const [offset, { hash, ...record }] of records.entries()) {
|
|
297
|
+
const d = record.data;
|
|
298
|
+
requireEvidence(
|
|
299
|
+
record.sequence === offset + 1 &&
|
|
300
|
+
record.previous === previous &&
|
|
301
|
+
hashDiscoveryContent(record) === hash
|
|
302
|
+
);
|
|
303
|
+
previous = hash;
|
|
304
|
+
requireEvidence(
|
|
305
|
+
[
|
|
306
|
+
"campaign",
|
|
307
|
+
"capacity.preflight",
|
|
308
|
+
"reservation",
|
|
309
|
+
"settlement",
|
|
310
|
+
"http.start",
|
|
311
|
+
"http.end",
|
|
312
|
+
"trial.start",
|
|
313
|
+
"trial.end",
|
|
314
|
+
"index.ready",
|
|
315
|
+
"schedule",
|
|
316
|
+
"stage.complete",
|
|
317
|
+
"profile.freeze",
|
|
318
|
+
"halt"
|
|
319
|
+
].includes(record.type)
|
|
320
|
+
);
|
|
321
|
+
if (record.type === "campaign") requireEvidence(offset === 0);
|
|
322
|
+
if (record.type === "halt") requireEvidence(false);
|
|
323
|
+
if (record.type === "reservation") {
|
|
324
|
+
requireEvidence(!reservations.has(d.id) && !settled.has(d.id));
|
|
325
|
+
reservations.set(d.id, d);
|
|
326
|
+
}
|
|
327
|
+
if (record.type === "settlement") {
|
|
328
|
+
const reservation = reservations.get(d.id),
|
|
329
|
+
attempts = Math.max(d.providerAttempts, d.completedResponses);
|
|
330
|
+
requireEvidence(
|
|
331
|
+
reservation &&
|
|
332
|
+
reservation.runId === d.runId &&
|
|
333
|
+
[d.providerAttempts, d.completedResponses].every(
|
|
334
|
+
(n) => Number.isSafeInteger(n) && n >= 0 && n <= 1
|
|
335
|
+
) &&
|
|
336
|
+
Object.values(d.usage).every((n) => Number.isFinite(n) && n >= 0)
|
|
337
|
+
);
|
|
338
|
+
for (const key of quantities) {
|
|
339
|
+
const amount =
|
|
340
|
+
key === "providerCalls"
|
|
341
|
+
? attempts
|
|
342
|
+
: (d.usage[key] ?? (attempts ? reservation.amounts[key] : 0));
|
|
343
|
+
requireEvidence(
|
|
344
|
+
Number.isFinite(amount) &&
|
|
345
|
+
amount >= 0 &&
|
|
346
|
+
d.charged[key] === amount &&
|
|
347
|
+
amount <= reservation.amounts[key]
|
|
348
|
+
);
|
|
349
|
+
charged[key] += amount;
|
|
350
|
+
}
|
|
351
|
+
reservations.delete(d.id);
|
|
352
|
+
settled.add(d.id);
|
|
353
|
+
}
|
|
354
|
+
if (record.type === "http.start") {
|
|
355
|
+
requireEvidence(!httpSeen.has(d.id) && Number.isFinite(d.fee) && d.fee >= 0);
|
|
356
|
+
httpSeen.add(d.id);
|
|
357
|
+
http.add(d.id);
|
|
358
|
+
httpRequests++;
|
|
359
|
+
charged.estimatedCost += d.fee;
|
|
360
|
+
}
|
|
361
|
+
if (record.type === "http.end") requireEvidence(http.delete(d.id));
|
|
362
|
+
if (record.type === "trial.start") {
|
|
363
|
+
requireEvidence(!trials.has(d.key));
|
|
364
|
+
trials.set(d.key, { binding: d.binding, pending: true });
|
|
365
|
+
}
|
|
366
|
+
if (record.type === "trial.end") {
|
|
367
|
+
const trial = trials.get(d.key);
|
|
368
|
+
requireEvidence(trial?.pending && trial.binding === d.binding);
|
|
369
|
+
trial.pending = false;
|
|
370
|
+
trial.artifact = d.artifact;
|
|
371
|
+
const value = readArtifact(d.artifact);
|
|
372
|
+
values.push(value);
|
|
373
|
+
if (d.key === "administrative-index") {
|
|
374
|
+
requireEvidence(value.status === "built");
|
|
375
|
+
index = value.index;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (record.type === "index.ready")
|
|
379
|
+
requireEvidence(same(readArtifact(d.artifact).index, index));
|
|
380
|
+
}
|
|
381
|
+
requireEvidence(
|
|
382
|
+
!reservations.size && !http.size && [...trials.values()].every((t) => !t.pending)
|
|
383
|
+
);
|
|
384
|
+
return { values, trials, charged, httpRequests, index };
|
|
385
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Bounded English wording support for the synthetic application, not a prose verifier.
|
|
2
|
+
export const WORDING_CHECKER_VERSION = "synthetic-wording.v2";
|
|
3
|
+
|
|
4
|
+
const months = [
|
|
5
|
+
"January",
|
|
6
|
+
"February",
|
|
7
|
+
"March",
|
|
8
|
+
"April",
|
|
9
|
+
"May",
|
|
10
|
+
"June",
|
|
11
|
+
"July",
|
|
12
|
+
"August",
|
|
13
|
+
"September",
|
|
14
|
+
"October",
|
|
15
|
+
"November",
|
|
16
|
+
"December"
|
|
17
|
+
];
|
|
18
|
+
const monthPattern = months
|
|
19
|
+
.map((month) => `${month}|${month.slice(0, 3)}\\.?`)
|
|
20
|
+
.join("|");
|
|
21
|
+
const namedDate = `(?:${monthPattern})\\s+\\d{1,2},?\\s+\\d{4}|\\d{1,2}\\s+(?:${monthPattern}),?\\s+\\d{4}`;
|
|
22
|
+
const values = new RegExp(
|
|
23
|
+
`(?:[\\w.+-]+@example\\.com|\\b(?:\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}Z)?|${namedDate}|\\d{1,2}/\\d{1,2}/\\d{4}|(?:TCK|SKU|WH|USR|ORD|EVT)-[A-Z0-9]+|\\d{2}:\\d{2}|\\d+(?:\\.\\d+)?)\\b)`,
|
|
24
|
+
"gi"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function dateKey(year, month, day) {
|
|
28
|
+
if (![year, month, day].every(Number.isInteger)) return null;
|
|
29
|
+
if (year < 1 || year > 9999 || month < 1 || month > 12 || day < 1) return null;
|
|
30
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
31
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
32
|
+
if (day > days[month - 1]) return null;
|
|
33
|
+
return `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function calendarValue(value) {
|
|
37
|
+
const iso = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})Z)?$/.exec(value);
|
|
38
|
+
if (iso) {
|
|
39
|
+
if (iso[4] !== undefined && (+iso[4] > 23 || +iso[5] > 59 || +iso[6] > 59))
|
|
40
|
+
return null;
|
|
41
|
+
return dateKey(+iso[1], +iso[2], +iso[3]);
|
|
42
|
+
}
|
|
43
|
+
const parts = value.replace(/[.,]/g, "").toLowerCase().split(/\s+/);
|
|
44
|
+
if (parts.length !== 3) return null;
|
|
45
|
+
const dayFirst = /^\d+$/.test(parts[0]);
|
|
46
|
+
const name = parts[dayFirst ? 1 : 0];
|
|
47
|
+
const month =
|
|
48
|
+
months.findIndex((entry) =>
|
|
49
|
+
[entry.toLowerCase(), entry.slice(0, 3).toLowerCase()].includes(name)
|
|
50
|
+
) + 1;
|
|
51
|
+
return dateKey(+parts[2], month, +parts[dayFirst ? 0 : 1]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function hasUnsupportedWordingValue(summary, facts) {
|
|
55
|
+
const permitted = new Set(),
|
|
56
|
+
dates = new Set();
|
|
57
|
+
const collect = (value) => {
|
|
58
|
+
if (value && typeof value === "object") {
|
|
59
|
+
for (const child of Object.values(value)) collect(child);
|
|
60
|
+
} else if (typeof value === "string" || typeof value === "number") {
|
|
61
|
+
const authored = String(value);
|
|
62
|
+
permitted.add(authored);
|
|
63
|
+
const date = calendarValue(authored);
|
|
64
|
+
if (date) {
|
|
65
|
+
dates.add(date);
|
|
66
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(authored)) {
|
|
67
|
+
permitted.add(date);
|
|
68
|
+
permitted.add(authored.slice(11, 16));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
collect(facts);
|
|
74
|
+
for (const match of summary.matchAll(values)) {
|
|
75
|
+
const value = match[0];
|
|
76
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
77
|
+
if (!calendarValue(value) || !permitted.has(value)) return true;
|
|
78
|
+
} else if (
|
|
79
|
+
/^\d{4}-\d{2}-\d{2}$/.test(value) ||
|
|
80
|
+
(/[a-z]/i.test(value) && /\s/.test(value))
|
|
81
|
+
) {
|
|
82
|
+
const date = calendarValue(value);
|
|
83
|
+
if (!date || !dates.has(date)) return true;
|
|
84
|
+
} else if (
|
|
85
|
+
!permitted.has(value) &&
|
|
86
|
+
!(/^\d+(?:\.\d+)?$/.test(value) && permitted.has(String(Number(value))))
|
|
87
|
+
)
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function positiveCompletionText(summary) {
|
|
94
|
+
const expanded = summary.replace(
|
|
95
|
+
/\b(has|have|had|was|were|is|are)n['’]t\b/gi,
|
|
96
|
+
"$1 not"
|
|
97
|
+
);
|
|
98
|
+
const actions =
|
|
99
|
+
/\b(?:sent|paid|refunded|committed|applied|reassigned|transferred|completed|executed|delivered|approved|prepared|drafted|cancelled)\b/gi;
|
|
100
|
+
return expanded.replace(actions, (word, offset) => {
|
|
101
|
+
// Only the action span is masked. A later affirmative clause still gets checked.
|
|
102
|
+
const prefix = expanded
|
|
103
|
+
.slice(Math.max(0, offset - 120), offset)
|
|
104
|
+
.split(/[.!?;,\n]|\b(?:but|however|and)\b/i)
|
|
105
|
+
.at(-1);
|
|
106
|
+
const negated =
|
|
107
|
+
/\b(?:not|never)\s+(?:(?:been|be|already|yet)\s+){0,2}$/i.test(prefix) ||
|
|
108
|
+
/\b(?:no (?:action|refund|payment|adjustment|invitation|invite)|nothing)\s+(?:(?:has|have|had|was|were|is|are)\s+)?(?:been\s+)?$/i.test(
|
|
109
|
+
prefix
|
|
110
|
+
);
|
|
111
|
+
const prospective =
|
|
112
|
+
/\b(?:can|could|may|might|will|would|shall)\s+(?:only\s+)?be\s+$/i.test(prefix);
|
|
113
|
+
return negated || prospective ? " ".repeat(word.length) : word;
|
|
114
|
+
});
|
|
115
|
+
}
|