@kontourai/survey 3.0.0 → 5.0.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/README.md +4 -0
- package/dist/examples/calibrated-auto-accept.d.ts +22 -15
- package/dist/examples/calibrated-auto-accept.js +40 -36
- package/dist/examples/review-workbench/server-apply-consumer.js +5 -0
- package/dist/src/calibration.d.ts +48 -21
- package/dist/src/calibration.js +72 -33
- package/dist/src/canonical-reviewed-trust-input.js +73 -34
- package/dist/src/console/review-console-server.d.ts +3 -1
- package/dist/src/console/review-console-server.js +203 -50
- package/dist/src/extraction-envelope.d.ts +81 -3
- package/dist/src/extraction-envelope.js +183 -24
- package/dist/src/index.d.ts +9 -8
- package/dist/src/index.js +3 -3
- package/dist/src/inquiry-mapping.d.ts +15 -1
- package/dist/src/inquiry-mapping.js +10 -2
- package/dist/src/mcp/review-mcp.js +112 -90
- package/dist/src/producer-profile.d.ts +41 -2
- package/dist/src/producer-profile.js +29 -2
- package/dist/src/review-session-file.d.ts +64 -0
- package/dist/src/review-session-file.js +320 -0
- package/dist/src/review-workbench/edited-value.d.ts +70 -0
- package/dist/src/review-workbench/edited-value.js +147 -0
- package/dist/src/review-workbench/extraction-inspector.d.ts +15 -1
- package/dist/src/review-workbench/extraction-inspector.js +55 -19
- package/dist/src/review-workbench/queue-binding.js +1 -1
- package/dist/src/review-workbench/review-presentation.d.ts +46 -1
- package/dist/src/review-workbench/review-presentation.js +72 -1
- package/dist/src/review-workbench/review-queue-session.d.ts +19 -5
- package/dist/src/review-workbench/review-queue-session.js +54 -14
- package/dist/src/review-workbench/review-session-replay.d.ts +35 -1
- package/dist/src/review-workbench/review-session-replay.js +82 -3
- package/dist/src/review-workbench/review-workbench-css.generated.js +2 -0
- package/dist/src/review-workbench/review-workbench.css +2 -0
- package/dist/src/review-workbench/review-workbench.d.ts +22 -11
- package/dist/src/review-workbench/review-workbench.js +91 -26
- package/dist/src/review-workbench/review-workbench.standalone.css +2 -0
- package/dist/src/review-workbench/server-review-session.d.ts +3 -1
- package/dist/src/review-workbench/server-review-session.js +1 -0
- package/dist/src/reviewed-candidate-resolution.js +13 -7
- package/dist/src/schema-mapping.d.ts +23 -0
- package/dist/src/schema-mapping.js +30 -20
- package/dist/src/surface-reviewed-extraction.js +4 -0
- package/dist/src/to-surface.d.ts +30 -6
- package/dist/src/to-surface.js +312 -18
- package/dist/src/types.d.ts +44 -1
- package/package.json +5 -4
|
@@ -17,10 +17,33 @@ export interface PortableExtractionOccurrence {
|
|
|
17
17
|
hintUsed: boolean;
|
|
18
18
|
ambiguous: boolean;
|
|
19
19
|
}
|
|
20
|
+
/** Which model produced one proposal, as the extractor recorded it for that proposal's own request. */
|
|
21
|
+
export interface PortableExtractionProducedBy {
|
|
22
|
+
model: string;
|
|
23
|
+
modelSource: "provider-reported" | "configured";
|
|
24
|
+
/** Content-free digest of the provider request (`sha256:<hex>`). */
|
|
25
|
+
requestDigest: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Deterministic, versioned facts about how a proposal's value relates to the
|
|
29
|
+
* declared schema and to its own excerpt. Annotations only: Survey carries
|
|
30
|
+
* them to review and does not route or block on them.
|
|
31
|
+
*/
|
|
32
|
+
export interface PortableExtractionEvidenceMatch {
|
|
33
|
+
checkerVersion: string;
|
|
34
|
+
schema: "ok" | "type-mismatch" | "enum-mismatch" | "format-invalid";
|
|
35
|
+
valueInExcerpt: "match" | "mismatch" | "not-evaluated" | "not-applicable";
|
|
36
|
+
tokenBoundary?: boolean;
|
|
37
|
+
}
|
|
20
38
|
export interface PortableExtractionProposal {
|
|
21
39
|
fieldPath: string;
|
|
22
40
|
candidateValue: unknown;
|
|
23
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The proposer's own uncalibrated self-report in `0..1`, absent when the
|
|
43
|
+
* proposer reported none. Survey carries it only when present and never
|
|
44
|
+
* substitutes a default.
|
|
45
|
+
*/
|
|
46
|
+
confidence?: number;
|
|
24
47
|
provenance: {
|
|
25
48
|
excerpt: string;
|
|
26
49
|
locator: string;
|
|
@@ -31,6 +54,8 @@ export interface PortableExtractionProposal {
|
|
|
31
54
|
inferenceType?: "explicit" | "inferred";
|
|
32
55
|
valueType?: ReviewValueType;
|
|
33
56
|
enumValues?: string[];
|
|
57
|
+
producedBy?: PortableExtractionProducedBy;
|
|
58
|
+
evidenceMatch?: PortableExtractionEvidenceMatch;
|
|
34
59
|
}
|
|
35
60
|
export type PortablePreparedArtifactState = {
|
|
36
61
|
status: "available" | "unavailable" | "storage-error";
|
|
@@ -51,6 +76,25 @@ export type PortablePreparedArtifactState = {
|
|
|
51
76
|
actualDigest: string;
|
|
52
77
|
actualContentLength: number;
|
|
53
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* Why a run stopped short. The first four are early stops; the last three mean
|
|
81
|
+
* a dispatched chunk was not (fully) read or answered, and such an envelope
|
|
82
|
+
* names the affected ranges in `result.coverage`.
|
|
83
|
+
*/
|
|
84
|
+
export type PortableExtractionPartialReason = "cancelled" | "max-provider-calls" | "max-total-tokens" | "max-chunks" | "provider-failure" | "content-truncated" | "output-truncated";
|
|
85
|
+
/**
|
|
86
|
+
* One prepared-text range (UTF-16 offsets, the space of `chars:` locators) and
|
|
87
|
+
* whether it was read and answered. `reason` is present exactly when `status`
|
|
88
|
+
* is `unread`, and then covers exactly the unread span.
|
|
89
|
+
*/
|
|
90
|
+
export interface PortableExtractionCoverageEntry {
|
|
91
|
+
/** 1-based chunk number. */
|
|
92
|
+
chunk: number;
|
|
93
|
+
start: number;
|
|
94
|
+
end: number;
|
|
95
|
+
status: "complete" | "unread" | "output-truncated";
|
|
96
|
+
reason?: "provider-failure" | "content-truncated" | "missing-tool-call" | "not-dispatched";
|
|
97
|
+
}
|
|
54
98
|
export interface PortableExtractionResultEnvelope {
|
|
55
99
|
format: typeof portableExtractionResultFormat;
|
|
56
100
|
version: typeof portableExtractionResultVersion;
|
|
@@ -70,7 +114,7 @@ export interface PortableExtractionResultEnvelope {
|
|
|
70
114
|
status: "success";
|
|
71
115
|
} | {
|
|
72
116
|
status: "partial";
|
|
73
|
-
reason:
|
|
117
|
+
reason: PortableExtractionPartialReason;
|
|
74
118
|
} | {
|
|
75
119
|
status: "failure";
|
|
76
120
|
category: "invalid-config" | "invalid-task" | "preparation" | "provider" | "unexpected";
|
|
@@ -84,15 +128,19 @@ export interface PortableExtractionResultEnvelope {
|
|
|
84
128
|
providerCalls: number;
|
|
85
129
|
totalTokensUsed: number;
|
|
86
130
|
partial?: {
|
|
87
|
-
reason:
|
|
131
|
+
reason: PortableExtractionPartialReason;
|
|
88
132
|
completedChunks: number;
|
|
89
133
|
remainingChunks: number;
|
|
90
134
|
tokenOvershoot?: number;
|
|
91
135
|
};
|
|
136
|
+
/** Per-chunk read coverage, ordered by `start`; ranges may overlap. Requires `preparedArtifact`. */
|
|
137
|
+
coverage?: PortableExtractionCoverageEntry[];
|
|
138
|
+
/** `code` is the upstream error code, informational only; `kind` stays authoritative. */
|
|
92
139
|
providerFailures?: Array<{
|
|
93
140
|
provider: string;
|
|
94
141
|
kind: "authentication" | "rate-limit" | "timeout" | "invalid-request" | "unavailable" | "unknown";
|
|
95
142
|
retryable: boolean;
|
|
143
|
+
code?: string;
|
|
96
144
|
}>;
|
|
97
145
|
taskDigest?: string;
|
|
98
146
|
exampleDigests?: string[];
|
|
@@ -121,6 +169,13 @@ export interface ExtractionEnvelopeImportOptions {
|
|
|
121
169
|
/** Survey meaning is supplied at the boundary; it is not added to the upstream wire contract. */
|
|
122
170
|
claimTarget: (proposal: PortableExtractionProposal, index: number) => ClaimTargetHint;
|
|
123
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Why an import is `unresolved`: its prepared artifact did not resolve, or the
|
|
174
|
+
* extraction itself produced nothing reviewable. `extraction-failed` is a
|
|
175
|
+
* failure outcome; `extraction-incomplete` is a partial outcome that proposed
|
|
176
|
+
* nothing, so no candidate can carry the reason. Either way the import is not
|
|
177
|
+
* a complete run that found no values.
|
|
178
|
+
*/
|
|
124
179
|
export type ExtractionEnvelopeImportDiagnostic = {
|
|
125
180
|
kind: "artifact-unavailable";
|
|
126
181
|
status: "unavailable" | "storage-error" | "identity-mismatch" | "invalid-artifact";
|
|
@@ -132,6 +187,15 @@ export type ExtractionEnvelopeImportDiagnostic = {
|
|
|
132
187
|
expectedDigest: string;
|
|
133
188
|
actualDigest: string;
|
|
134
189
|
message: string;
|
|
190
|
+
} | {
|
|
191
|
+
kind: "extraction-failed";
|
|
192
|
+
category: string;
|
|
193
|
+
code: string;
|
|
194
|
+
message: string;
|
|
195
|
+
} | {
|
|
196
|
+
kind: "extraction-incomplete";
|
|
197
|
+
reason: PortableExtractionPartialReason;
|
|
198
|
+
message: string;
|
|
135
199
|
};
|
|
136
200
|
export interface ExtractionEnvelopeImport {
|
|
137
201
|
apiVersion: typeof extractionEnvelopeImportApiVersion;
|
|
@@ -160,6 +224,20 @@ export interface ExtractionEnvelopeResolutionIdentity {
|
|
|
160
224
|
}
|
|
161
225
|
/** Parse an untrusted upstream document and create Survey's durable import projection. */
|
|
162
226
|
export declare function importExtractionEnvelope(serialized: string | PortableExtractionResultEnvelope, options: ExtractionEnvelopeImportOptions): ExtractionEnvelopeImportResult;
|
|
227
|
+
/**
|
|
228
|
+
* One ReviewItem per claim slot: every proposal whose claim target names the
|
|
229
|
+
* same claim (subject, facet, claim type, field or behavior, and claim id when
|
|
230
|
+
* one is set) at the same `pathIndices` is one candidate set, so two values for
|
|
231
|
+
* one claim can never be accepted as two separate verified claims. The slot is
|
|
232
|
+
* the claim the proposal would project to, not the upstream `fieldPath` (two
|
|
233
|
+
* field paths mapped to one claim are one slot) and not the value.
|
|
234
|
+
*
|
|
235
|
+
* Within a slot there is one candidate per distinct canonical value; further
|
|
236
|
+
* proposals of an already-seen value are recorded on that candidate as
|
|
237
|
+
* `sameValueProposals`. Two or more distinct values make the set `conflict`.
|
|
238
|
+
* Distinct `pathIndices` (array items) or distinct claim ids are separate slots,
|
|
239
|
+
* which is how a producer declares a multi-valued field.
|
|
240
|
+
*/
|
|
163
241
|
export declare function buildReviewItemsFromExtractionEnvelopeImport(record: ExtractionEnvelopeImport): ReviewItem[];
|
|
164
242
|
export declare function exportExtractionEnvelopeImport(record: ExtractionEnvelopeImport): string;
|
|
165
243
|
export declare function reimportExtractionEnvelope(serialized: string): ExtractionEnvelopeImport;
|
|
@@ -36,11 +36,48 @@ export function importExtractionEnvelope(serialized, options) {
|
|
|
36
36
|
};
|
|
37
37
|
return { record, reviewItems: buildReviewItemsFromExtractionEnvelopeImport(record) };
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* One ReviewItem per claim slot: every proposal whose claim target names the
|
|
41
|
+
* same claim (subject, facet, claim type, field or behavior, and claim id when
|
|
42
|
+
* one is set) at the same `pathIndices` is one candidate set, so two values for
|
|
43
|
+
* one claim can never be accepted as two separate verified claims. The slot is
|
|
44
|
+
* the claim the proposal would project to, not the upstream `fieldPath` (two
|
|
45
|
+
* field paths mapped to one claim are one slot) and not the value.
|
|
46
|
+
*
|
|
47
|
+
* Within a slot there is one candidate per distinct canonical value; further
|
|
48
|
+
* proposals of an already-seen value are recorded on that candidate as
|
|
49
|
+
* `sameValueProposals`. Two or more distinct values make the set `conflict`.
|
|
50
|
+
* Distinct `pathIndices` (array items) or distinct claim ids are separate slots,
|
|
51
|
+
* which is how a producer declares a multi-valued field.
|
|
52
|
+
*/
|
|
39
53
|
export function buildReviewItemsFromExtractionEnvelopeImport(record) {
|
|
40
54
|
validateImport(record);
|
|
41
55
|
if (record.status.state !== "grounded")
|
|
42
56
|
return [];
|
|
43
|
-
return record.
|
|
57
|
+
return claimSlotGroups(record).map((group) => buildReviewItem(record, group));
|
|
58
|
+
}
|
|
59
|
+
function claimSlotGroups(record) {
|
|
60
|
+
const groups = new Map();
|
|
61
|
+
record.spec.envelope.result.proposals.forEach((proposal, index) => {
|
|
62
|
+
const target = record.spec.claimTargets[index];
|
|
63
|
+
const slot = {
|
|
64
|
+
subjectType: target.subjectType, subjectId: target.subjectId, facet: target.facet, claimType: target.claimType,
|
|
65
|
+
fieldOrBehavior: target.fieldOrBehavior, claimId: target.claimId ?? null, pathIndices: proposal.pathIndices ?? null,
|
|
66
|
+
};
|
|
67
|
+
const key = canonicalJson(slot);
|
|
68
|
+
const group = groups.get(key);
|
|
69
|
+
if (!group) {
|
|
70
|
+
groups.set(key, { slot, members: [{ proposal, index }] });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const first = group.members[0].index;
|
|
74
|
+
// One claim cannot carry two impact levels or evidence descriptions.
|
|
75
|
+
if (canonicalJson(record.spec.claimTargets[first]) !== canonicalJson(target)) {
|
|
76
|
+
throw new Error(`Proposals ${first} and ${index} map to the same claim with different claim targets.`);
|
|
77
|
+
}
|
|
78
|
+
group.members.push({ proposal, index });
|
|
79
|
+
});
|
|
80
|
+
return [...groups.values()];
|
|
44
81
|
}
|
|
45
82
|
export function exportExtractionEnvelopeImport(record) {
|
|
46
83
|
validateImport(record);
|
|
@@ -66,15 +103,50 @@ export function createExtractionEnvelopeResolutionIdentity(record, proposalIndex
|
|
|
66
103
|
const nonce = globalThis.crypto.randomUUID();
|
|
67
104
|
return { evidenceId: `survey.extraction.${base}.resolution-evidence.${nonce}`, eventId: `survey.extraction.${base}.resolution-event.${nonce}` };
|
|
68
105
|
}
|
|
69
|
-
function buildReviewItem(record,
|
|
106
|
+
function buildReviewItem(record, group) {
|
|
107
|
+
const envelope = record.spec.envelope;
|
|
108
|
+
const byValue = new Map();
|
|
109
|
+
for (const member of group.members) {
|
|
110
|
+
const key = canonicalJson(member.proposal.candidateValue);
|
|
111
|
+
byValue.set(key, [...(byValue.get(key) ?? []), member]);
|
|
112
|
+
}
|
|
113
|
+
const candidates = [...byValue.values()].map((members) => buildCandidate(record, members));
|
|
114
|
+
const lead = group.members[0].proposal;
|
|
115
|
+
const valueType = lead.valueType ?? inferValueType(lead.candidateValue);
|
|
116
|
+
const identity = identityHash({ producerNamespace: record.metadata.producerNamespace, importName: record.metadata.name, source: envelope.source,
|
|
117
|
+
preparedArtifact: envelope.result.preparedArtifact, pdfLayout: envelope.result.pdfLayout, runId: envelope.result.runId, claimSlot: group.slot });
|
|
118
|
+
return {
|
|
119
|
+
apiVersion: reviewResourceApiVersion, kind: "ReviewItem",
|
|
120
|
+
metadata: { name: `extraction-envelope.${identity}`, producer: { "survey.kontourai.io/extraction-envelope": {
|
|
121
|
+
importName: record.metadata.name,
|
|
122
|
+
evidenceId: `survey.extraction.${identityHash(evidenceInputs(record, lead))}.source-evidence`,
|
|
123
|
+
proposalIndices: group.members.map((member) => member.index),
|
|
124
|
+
source: envelope.source,
|
|
125
|
+
...(envelope.result.preparedArtifact ? { preparedArtifact: envelope.result.preparedArtifact } : {}),
|
|
126
|
+
} } },
|
|
127
|
+
spec: {
|
|
128
|
+
target: lead.fieldPath, candidates,
|
|
129
|
+
candidateSetStatus: candidates.length > 1 ? "conflict" : "needs-review",
|
|
130
|
+
valueDescriptor: { type: valueType }, editable: false,
|
|
131
|
+
},
|
|
132
|
+
status: { observedCandidateCount: candidates.length },
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** One candidate for one distinct value; `members` are that value's proposals in envelope order. */
|
|
136
|
+
function buildCandidate(record, members) {
|
|
70
137
|
const envelope = record.spec.envelope;
|
|
71
|
-
const
|
|
138
|
+
const { proposal, index } = members[0];
|
|
139
|
+
const others = members.slice(1);
|
|
140
|
+
// A candidate commits to every proposal it stands for; a single-proposal
|
|
141
|
+
// candidate keeps exactly the identity it had before grouping.
|
|
142
|
+
const identity = identityHash(others.length === 0 ? identityInputs(record, proposal, index)
|
|
143
|
+
: { ...identityInputs(record, proposal, index), sameValueProposals: others.map((other) => ({ proposalIndex: other.index, proposal: other.proposal })) });
|
|
72
144
|
const evidence = identityHash(evidenceInputs(record, proposal));
|
|
73
145
|
const target = record.spec.claimTargets[index];
|
|
74
146
|
const valueType = proposal.valueType ?? inferValueType(proposal.candidateValue);
|
|
75
|
-
|
|
147
|
+
return {
|
|
76
148
|
id: `extraction-envelope.${identity}.proposed`, role: "proposed", value: proposal.candidateValue,
|
|
77
|
-
confidence: proposal.confidence,
|
|
149
|
+
...(proposal.confidence !== undefined ? { confidence: proposal.confidence } : {}),
|
|
78
150
|
source: {
|
|
79
151
|
sourceRef: envelope.source.ref,
|
|
80
152
|
sourceId: envelope.source.snapshotRef ?? envelope.source.ref,
|
|
@@ -87,9 +159,12 @@ function buildReviewItem(record, proposal, index) {
|
|
|
87
159
|
extraction: {
|
|
88
160
|
extractionId: `extraction-envelope.${identity}`,
|
|
89
161
|
target: proposal.fieldPath,
|
|
90
|
-
confidence: proposal.confidence,
|
|
162
|
+
...(proposal.confidence !== undefined ? { confidence: proposal.confidence } : {}),
|
|
91
163
|
extractor: proposal.extractor,
|
|
92
|
-
|
|
164
|
+
extractedAt: envelope.result.extractedAt,
|
|
165
|
+
// The proposal's own served model when recorded; in a multi-chunk run
|
|
166
|
+
// `result.model` names only the last chunk's model.
|
|
167
|
+
...(proposal.producedBy ? { model: proposal.producedBy.model } : envelope.result.model ? { model: envelope.result.model } : {}),
|
|
93
168
|
},
|
|
94
169
|
claimTarget: target,
|
|
95
170
|
producer: { "survey.kontourai.io/extraction-envelope": {
|
|
@@ -100,23 +175,24 @@ function buildReviewItem(record, proposal, index) {
|
|
|
100
175
|
...(envelope.result.taskDigest ? { taskDigest: envelope.result.taskDigest } : {}),
|
|
101
176
|
...(envelope.result.exampleDigests ? { exampleDigests: envelope.result.exampleDigests } : {}),
|
|
102
177
|
valueType: { type: valueType, origin: proposal.inferenceType ?? "inferred" },
|
|
178
|
+
...(proposal.producedBy ? { producedBy: proposal.producedBy } : {}),
|
|
179
|
+
...(proposal.evidenceMatch ? { evidenceMatch: proposal.evidenceMatch } : {}),
|
|
103
180
|
occurrence: proposal.provenance.occurrence,
|
|
181
|
+
...(others.length ? { sameValueProposals: others.map((other) => ({
|
|
182
|
+
proposalIndex: other.index,
|
|
183
|
+
evidenceId: `survey.extraction.${identityHash(evidenceInputs(record, other.proposal))}.source-evidence`,
|
|
184
|
+
locator: other.proposal.provenance.locator,
|
|
185
|
+
excerpt: other.proposal.provenance.excerpt,
|
|
186
|
+
})) } : {}),
|
|
104
187
|
attempt: { id: envelope.result.runId, providerCalls: envelope.result.providerCalls },
|
|
105
188
|
...(envelope.result.warningClassifications ? { warnings: envelope.result.warningClassifications } : {}),
|
|
106
189
|
outcome: envelope.result.outcome,
|
|
190
|
+
// A partial run may have left this field's other values unread: the
|
|
191
|
+
// reason and the per-chunk coverage travel with every candidate.
|
|
192
|
+
...(envelope.result.partial ? { partial: envelope.result.partial } : {}),
|
|
193
|
+
...(envelope.result.coverage ? { coverage: envelope.result.coverage } : {}),
|
|
107
194
|
} },
|
|
108
195
|
};
|
|
109
|
-
return {
|
|
110
|
-
apiVersion: reviewResourceApiVersion, kind: "ReviewItem",
|
|
111
|
-
metadata: { name: `extraction-envelope.${identity}`, producer: { "survey.kontourai.io/extraction-envelope": {
|
|
112
|
-
importName: record.metadata.name,
|
|
113
|
-
evidenceId: `survey.extraction.${evidence}.source-evidence`,
|
|
114
|
-
source: envelope.source,
|
|
115
|
-
...(envelope.result.preparedArtifact ? { preparedArtifact: envelope.result.preparedArtifact } : {}),
|
|
116
|
-
} } },
|
|
117
|
-
spec: { target: proposal.fieldPath, candidates: [candidate], candidateSetStatus: "needs-review", valueDescriptor: { type: valueType }, editable: false },
|
|
118
|
-
status: { observedCandidateCount: 1 },
|
|
119
|
-
};
|
|
120
196
|
}
|
|
121
197
|
function identityInputs(record, proposal, index) {
|
|
122
198
|
return { producerNamespace: record.metadata.producerNamespace, importName: record.metadata.name, source: record.spec.envelope.source,
|
|
@@ -132,6 +208,9 @@ function evidenceInputs(record, proposal) {
|
|
|
132
208
|
provenance: proposal.provenance };
|
|
133
209
|
}
|
|
134
210
|
function diagnosticsFor(envelope) {
|
|
211
|
+
return [...artifactDiagnostics(envelope), ...outcomeDiagnostics(envelope)];
|
|
212
|
+
}
|
|
213
|
+
function artifactDiagnostics(envelope) {
|
|
135
214
|
const state = envelope.result.preparedArtifactState;
|
|
136
215
|
if (!state || state.status === "available")
|
|
137
216
|
return [];
|
|
@@ -143,6 +222,21 @@ function diagnosticsFor(envelope) {
|
|
|
143
222
|
...(state.status === "invalid-artifact" ? { artifactRef: state.canonicalRef } : { artifactRef: state.requestedRef }),
|
|
144
223
|
message: `Prepared artifact resolution is ${state.status}.` }];
|
|
145
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* A failed run, or a partial run with no proposal, must not look like a
|
|
227
|
+
* complete run that found nothing. A partial run with proposals stays
|
|
228
|
+
* grounded: its reason and coverage travel on every candidate.
|
|
229
|
+
*/
|
|
230
|
+
function outcomeDiagnostics(envelope) {
|
|
231
|
+
const outcome = envelope.result.outcome;
|
|
232
|
+
if (outcome.status === "failure")
|
|
233
|
+
return [{ kind: "extraction-failed", category: outcome.category, code: outcome.code,
|
|
234
|
+
message: `Extraction failed (${outcome.category}/${outcome.code}); no usable answer was recorded for this source, so the import has no candidates.` }];
|
|
235
|
+
if (outcome.status === "partial" && envelope.result.proposals.length === 0)
|
|
236
|
+
return [{ kind: "extraction-incomplete", reason: outcome.reason,
|
|
237
|
+
message: `Extraction stopped short (${outcome.reason}) without proposing any value; unread text may hold values.` }];
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
146
240
|
function validateImport(value) {
|
|
147
241
|
jsonSafe(value, "Extraction envelope import");
|
|
148
242
|
const record = obj(value, "Extraction envelope import");
|
|
@@ -185,7 +279,7 @@ function validateEnvelope(input) {
|
|
|
185
279
|
if (source.snapshotRef !== undefined)
|
|
186
280
|
safeReference(source.snapshotRef, "source.snapshotRef");
|
|
187
281
|
const r = obj(e.result, "result");
|
|
188
|
-
exact(r, ["proposals", "provider", "runId", "raw", "outcome", "extractedAt", "providerCalls", "totalTokensUsed"], "result", ["model", "warningClassifications", "partial", "providerFailures", "taskDigest", "exampleDigests", "pdfPageOffsets", "pdfLayout", "ocrDerived", "preparedArtifact", "preparedArtifactState"]);
|
|
282
|
+
exact(r, ["proposals", "provider", "runId", "raw", "outcome", "extractedAt", "providerCalls", "totalTokensUsed"], "result", ["model", "warningClassifications", "partial", "coverage", "providerFailures", "taskDigest", "exampleDigests", "pdfPageOffsets", "pdfLayout", "ocrDerived", "preparedArtifact", "preparedArtifactState"]);
|
|
189
283
|
stableIdentity(r.provider, "result.provider");
|
|
190
284
|
if (r.model !== undefined)
|
|
191
285
|
stableIdentity(r.model, "result.model");
|
|
@@ -220,6 +314,9 @@ function validateEnvelope(input) {
|
|
|
220
314
|
: artifact === undefined
|
|
221
315
|
? (() => { throw new Error("result.pdfLayout requires result.preparedArtifact."); })()
|
|
222
316
|
: validatePortablePdfLayout(r.pdfLayout, artifact.contentLength);
|
|
317
|
+
if (r.coverage !== undefined)
|
|
318
|
+
validateCoverage(r.coverage, artifact);
|
|
319
|
+
validateCoverageAgreement(r.outcome, r.coverage);
|
|
223
320
|
const state = r.preparedArtifactState === undefined ? undefined : validateArtifactState(r.preparedArtifactState, artifact);
|
|
224
321
|
if (source.snapshotRef !== undefined && artifact?.sourceSnapshotRef !== undefined && source.snapshotRef !== artifact.sourceSnapshotRef)
|
|
225
322
|
throw new Error("Source snapshot identity mismatch.");
|
|
@@ -228,10 +325,12 @@ function validateEnvelope(input) {
|
|
|
228
325
|
}
|
|
229
326
|
function validateProposal(input, index, contentLength) {
|
|
230
327
|
const p = obj(input, `proposal[${index}]`);
|
|
231
|
-
exact(p, ["fieldPath", "candidateValue", "
|
|
328
|
+
exact(p, ["fieldPath", "candidateValue", "provenance", "extractor"], `proposal[${index}]`, ["confidence", "pathIndices", "inferenceType", "valueType", "enumValues", "producedBy", "evidenceMatch"]);
|
|
232
329
|
wireNonEmpty(p.fieldPath, "proposal.fieldPath");
|
|
233
330
|
stableIdentity(p.extractor, "proposal.extractor");
|
|
234
|
-
|
|
331
|
+
// Absent means the proposer reported none; `null` is not absent and is rejected.
|
|
332
|
+
if (Object.hasOwn(p, "confidence"))
|
|
333
|
+
finite(p.confidence, "proposal.confidence", 0, 1);
|
|
235
334
|
const provenance = obj(p.provenance, "proposal.provenance");
|
|
236
335
|
exact(provenance, ["excerpt", "locator", "occurrence"], "proposal.provenance");
|
|
237
336
|
wireNonEmpty(provenance.excerpt, "proposal.provenance.excerpt");
|
|
@@ -252,6 +351,10 @@ function validateProposal(input, index, contentLength) {
|
|
|
252
351
|
throw new Error("proposal valueType is invalid.");
|
|
253
352
|
if (p.enumValues !== undefined)
|
|
254
353
|
array(p.enumValues, "enumValues").forEach((v) => wellFormedString(v, "enumValue"));
|
|
354
|
+
if (p.producedBy !== undefined)
|
|
355
|
+
validateProducedBy(p.producedBy);
|
|
356
|
+
if (p.evidenceMatch !== undefined)
|
|
357
|
+
validateEvidenceMatch(p.evidenceMatch);
|
|
255
358
|
return cloneJson(p);
|
|
256
359
|
}
|
|
257
360
|
function validateOccurrence(input, start, end) { const o = obj(input, "occurrence"); exact(o, ["resolverVersion", "count", "selected", "selection", "hintUsed", "ambiguous"], "occurrence"); if (o.resolverVersion !== "exact-occurrence-v1")
|
|
@@ -315,10 +418,59 @@ else if (o.status === "failure") {
|
|
|
315
418
|
else
|
|
316
419
|
throw new Error("outcome status is invalid."); if (o.status !== "partial" && partial !== undefined)
|
|
317
420
|
throw new Error("partial requires partial outcome."); }
|
|
421
|
+
function validateCoverage(input, artifact) {
|
|
422
|
+
if (!artifact)
|
|
423
|
+
throw new Error("result.coverage requires result.preparedArtifact.");
|
|
424
|
+
const entries = array(input, "result.coverage");
|
|
425
|
+
entries.forEach((value, index) => {
|
|
426
|
+
const subject = `result.coverage[${index}]`;
|
|
427
|
+
const c = obj(value, subject);
|
|
428
|
+
exact(c, ["chunk", "start", "end", "status"], subject, ["reason"]);
|
|
429
|
+
integer(c.chunk, `${subject}.chunk`);
|
|
430
|
+
if (c.chunk === 0)
|
|
431
|
+
throw new Error(`${subject}.chunk must be positive.`);
|
|
432
|
+
integer(c.start, `${subject}.start`);
|
|
433
|
+
integer(c.end, `${subject}.end`);
|
|
434
|
+
if (c.end <= c.start)
|
|
435
|
+
throw new Error(`${subject} must have start < end.`);
|
|
436
|
+
if (c.end > artifact.contentLength)
|
|
437
|
+
throw new Error(`${subject}.end exceeds the prepared artifact contentLength.`);
|
|
438
|
+
if (!COVERAGE_STATUSES.has(c.status))
|
|
439
|
+
throw new Error(`${subject}.status is invalid.`);
|
|
440
|
+
if ((c.status === "unread") !== (c.reason !== undefined))
|
|
441
|
+
throw new Error(`${subject}.reason is required exactly when status is unread.`);
|
|
442
|
+
if (c.reason !== undefined && !COVERAGE_REASONS.has(c.reason))
|
|
443
|
+
throw new Error(`${subject}.reason is invalid.`);
|
|
444
|
+
// Chunks overlap by design, so ranges may overlap; only the order is fixed.
|
|
445
|
+
if (index > 0 && c.start < (entries[index - 1].start))
|
|
446
|
+
throw new Error("result.coverage entries must be ordered by start.");
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
/** The outcome and the coverage must tell the same story about unread text. */
|
|
450
|
+
function validateCoverageAgreement(outcome, coverage) {
|
|
451
|
+
const lost = coverage?.some((entry) => entry.status !== "complete") ?? false;
|
|
452
|
+
if (outcome.status === "success" && lost)
|
|
453
|
+
throw new Error("result.coverage names unread or unanswered text, but the outcome is success.");
|
|
454
|
+
// A loss reason means a dispatched chunk lost text; a never-dispatched range
|
|
455
|
+
// is an early stop, not that loss.
|
|
456
|
+
const dispatchedLoss = coverage?.some((entry) => entry.status !== "complete" && entry.reason !== "not-dispatched") ?? false;
|
|
457
|
+
if (outcome.status === "partial" && LOSS_PARTIAL.has(outcome.reason) && !dispatchedLoss)
|
|
458
|
+
throw new Error(`partial reason ${outcome.reason} requires a result.coverage entry for a dispatched chunk that was not read or answered.`);
|
|
459
|
+
}
|
|
318
460
|
function validateWarning(v) { const w = obj(v, "warning"); exact(w, ["category", "code"], "warning"); if (!WARNING_CATEGORIES.has(w.category))
|
|
319
461
|
throw new Error("warning category invalid."); stableIdentity(w.code, "warning.code"); }
|
|
320
|
-
function validateFailure(v) { const f = obj(v, "providerFailure"); exact(f, ["provider", "kind", "retryable"], "providerFailure"); stableIdentity(f.provider, "failure.provider"); if (!FAILURE_KINDS.has(f.kind) || typeof f.retryable !== "boolean")
|
|
321
|
-
throw new Error("provider failure invalid.");
|
|
462
|
+
function validateFailure(v) { const f = obj(v, "providerFailure"); exact(f, ["provider", "kind", "retryable"], "providerFailure", ["code"]); stableIdentity(f.provider, "failure.provider"); if (!FAILURE_KINDS.has(f.kind) || typeof f.retryable !== "boolean")
|
|
463
|
+
throw new Error("provider failure invalid."); if (f.code !== undefined) {
|
|
464
|
+
stableIdentity(f.code, "failure.code");
|
|
465
|
+
if (f.code.length > 128)
|
|
466
|
+
throw new Error("failure.code must be at most 128 characters.");
|
|
467
|
+
} }
|
|
468
|
+
function validateProducedBy(v) { const b = obj(v, "proposal.producedBy"); exact(b, ["model", "modelSource", "requestDigest"], "proposal.producedBy"); stableIdentity(b.model, "proposal.producedBy.model"); if (!MODEL_SOURCES.has(b.modelSource))
|
|
469
|
+
throw new Error("proposal.producedBy.modelSource is invalid."); digest(b.requestDigest, "proposal.producedBy.requestDigest"); }
|
|
470
|
+
function validateEvidenceMatch(v) { const m = obj(v, "proposal.evidenceMatch"); exact(m, ["checkerVersion", "schema", "valueInExcerpt"], "proposal.evidenceMatch", ["tokenBoundary"]); stableIdentity(m.checkerVersion, "proposal.evidenceMatch.checkerVersion"); if (!SCHEMA_MATCHES.has(m.schema))
|
|
471
|
+
throw new Error("proposal.evidenceMatch.schema is invalid."); if (!VALUE_IN_EXCERPT_MATCHES.has(m.valueInExcerpt))
|
|
472
|
+
throw new Error("proposal.evidenceMatch.valueInExcerpt is invalid."); if (m.tokenBoundary !== undefined && typeof m.tokenBoundary !== "boolean")
|
|
473
|
+
throw new Error("proposal.evidenceMatch.tokenBoundary must be a boolean."); }
|
|
322
474
|
function validateClaimTarget(v) { const t = obj(v, "claimTarget"); exact(t, ["subjectType", "subjectId", "facet", "claimType", "fieldOrBehavior", "impactLevel"], "claimTarget", ["claimId", "evidenceType", "evidenceMethod", "collectedBy", "derivedFrom"]); for (const key of ["subjectType", "subjectId", "facet", "claimType", "fieldOrBehavior"])
|
|
323
475
|
nonEmpty(t[key], `claimTarget.${key}`); if (!["low", "medium", "high", "critical"].includes(t.impactLevel))
|
|
324
476
|
throw new Error("claimTarget.impactLevel invalid."); for (const key of ["claimId", "evidenceType", "evidenceMethod", "collectedBy"])
|
|
@@ -429,10 +581,17 @@ function isWellFormedUnicode(value) { for (let index = 0; index < value.length;
|
|
|
429
581
|
} return true; }
|
|
430
582
|
const RAW_SOURCE_KINDS = new Set(["uploaded-document", "web-page", "api-record", "manual-entry", "policy-standard", "inquiry-question", "agent-utterance", "system-schema"]);
|
|
431
583
|
const VALUE_TYPES = new Set(["string", "number", "boolean", "date", "enum", "array", "object"]);
|
|
432
|
-
|
|
584
|
+
/** Partial reasons meaning a dispatched chunk was not (fully) read or answered. */
|
|
585
|
+
const LOSS_PARTIAL = new Set(["provider-failure", "content-truncated", "output-truncated"]);
|
|
586
|
+
const PARTIAL = new Set(["cancelled", "max-provider-calls", "max-total-tokens", "max-chunks", ...LOSS_PARTIAL]);
|
|
587
|
+
const COVERAGE_STATUSES = new Set(["complete", "unread", "output-truncated"]);
|
|
588
|
+
const COVERAGE_REASONS = new Set(["provider-failure", "content-truncated", "missing-tool-call", "not-dispatched"]);
|
|
433
589
|
const FAILURE_CATEGORIES = new Set(["invalid-config", "invalid-task", "preparation", "provider", "unexpected"]);
|
|
434
590
|
const WARNING_CATEGORIES = new Set(["provider", "normalization", "preparation", "limit", "storage", "content", "other"]);
|
|
435
591
|
const FAILURE_KINDS = new Set(["authentication", "rate-limit", "timeout", "invalid-request", "unavailable", "unknown"]);
|
|
592
|
+
const MODEL_SOURCES = new Set(["provider-reported", "configured"]);
|
|
593
|
+
const SCHEMA_MATCHES = new Set(["ok", "type-mismatch", "enum-mismatch", "format-invalid"]);
|
|
594
|
+
const VALUE_IN_EXCERPT_MATCHES = new Set(["match", "mismatch", "not-evaluated", "not-applicable"]);
|
|
436
595
|
const PREPARATION_MODES = new Set(["text", "markdown", "transcript", "pdf-text", "image-ocr"]);
|
|
437
596
|
const ARTIFACT_INVALID_REASONS = new Set(["not-an-object", "invalid-format", "invalid-version", "invalid-digest", "invalid-ref", "invalid-preparation-mode", "invalid-preparation-version", "invalid-content-length", "invalid-source-snapshot-ref", "ill-formed-unicode", "invalid-resolved-text"]);
|
|
438
597
|
const STABLE_IDENTITY = /^[A-Za-z0-9][A-Za-z0-9._:@/+~-]{0,255}$/;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export type { CandidateSetStatus, Candidate, CandidateSet, ClaimTarget, EscalationDimension, EscalationRecord, Extraction, Interpretation, LocatorScheme, ProvenanceResolution, RawSource, RawSourceKind, ReviewAuthorizing, ReviewAuthorizingAuthorizedAction, ReviewAuthorizingExchange, ReviewAuthorizingExplicitStatement, ReviewAuthorizingKind, ReviewOutcome, ReviewResolution, ReviewStatus, SurveyInput, } from "./types.js";
|
|
1
|
+
export type { CandidateSetStatus, Candidate, CandidateSet, ClaimTarget, EscalationDimension, EscalationRecord, Extraction, Interpretation, InterpretationAnswerImpact, InterpretationReadingKind, LocatorScheme, ProvenanceResolution, RawSource, RawSourceKind, ReviewAuthorizing, ReviewAuthorizingAuthorizedAction, ReviewAuthorizingExchange, ReviewAuthorizingExplicitStatement, ReviewAuthorizingKind, ReviewOutcome, ReviewResolution, ReviewStatus, SurveyInput, } from "./types.js";
|
|
2
2
|
export { SURVEY_INPUT_CONTRACT_VERSION } from "./types.js";
|
|
3
3
|
export { reviewResourceApiVersion } from "./review-resource.js";
|
|
4
4
|
export { buildReviewItemsFromExtractionEnvelopeImport, createExtractionEnvelopeResolutionIdentity, exportExtractionEnvelopeImport, extractionEnvelopeImportApiVersion, importExtractionEnvelope, portableExtractionResultFormat, portableExtractionResultVersion, reimportExtractionEnvelope, validateExtractionEnvelopeImport, } from "./extraction-envelope.js";
|
|
5
|
-
export { buildExtractionInspectorModel, exportExtractionInspector, filterExtractionInspectorCandidates, } from "./review-workbench/extraction-inspector.js";
|
|
5
|
+
export { buildExtractionInspectorModel, exportExtractionInspector, inspectorSourcePosture, filterExtractionInspectorCandidates, } from "./review-workbench/extraction-inspector.js";
|
|
6
6
|
export { assertReviewQueueAgainstExtractionImport, assertReviewQueueBinding, bindReviewQueue, hashReviewQueueSnapshot, UnattestedExtractionQueueError, UnattestedReviewQueueError, validateReviewQueueAgainstExtractionImport, validateReviewQueueBinding, } from "./review-workbench/queue-binding.js";
|
|
7
7
|
export type { BindReviewQueueOptions, ReviewQueueBinding, ReviewQueueBindingIssue, ReviewQueueBindingIssueCode, ReviewQueueExtractionIssue, ReviewQueueExtractionIssueCode, ValidateReviewQueueBindingOptions, } from "./review-workbench/queue-binding.js";
|
|
8
8
|
export { resolvePortablePdfRegion } from "./pdf-layout.js";
|
|
9
9
|
export type { PortablePdfBoundingBox, PortablePdfLayout, PortablePdfPageGeometry, PortablePdfRegionContext, PortablePdfTable, PortablePdfTableCell, PortablePdfTextElement, PortablePdfTextRange, } from "./pdf-layout.js";
|
|
10
10
|
export type { ExtractionAlignmentState, ArtifactUnavailableCode, BuiltExtractionInspectorCandidate, BuiltExtractionInspectorModel, ExtractionInspectorCandidate, ExtractionInspectorEntry, ExtractionInspectorExportOptions, ExtractionInspectorFilters, ExtractionInspectorInput, ExtractionInspectorModel, ExtractionInspectorSource, ResolvedExtractionArtifact, } from "./review-workbench/extraction-inspector.js";
|
|
11
|
-
export type { ExtractionEnvelopeImport, ExtractionEnvelopeImportDiagnostic, ExtractionEnvelopeImportOptions, ExtractionEnvelopeImportResult, ExtractionEnvelopeResolutionIdentity, PortableExtractionOccurrence, PortableExtractionProposal, PortableExtractionResultEnvelope, PortablePreparedArtifactState, } from "./extraction-envelope.js";
|
|
11
|
+
export type { ExtractionEnvelopeImport, ExtractionEnvelopeImportDiagnostic, ExtractionEnvelopeImportOptions, ExtractionEnvelopeImportResult, ExtractionEnvelopeResolutionIdentity, PortableExtractionCoverageEntry, PortableExtractionEvidenceMatch, PortableExtractionPartialReason, PortableExtractionOccurrence, PortableExtractionProducedBy, PortableExtractionProposal, PortableExtractionResultEnvelope, PortablePreparedArtifactState, } from "./extraction-envelope.js";
|
|
12
12
|
export type { CandidateRole, ClaimTargetHint, ProducerPolicy, ExtractionReference, ResourceEnvelope, ResourceMetadata, ReviewActor, ReviewCandidate, ReviewDecision, ReviewDecisionMode, ReviewDecisionSpec, ReviewDecisionStatus, ReviewItem, ReviewItemSpec, ReviewItemStatus, ReviewLocator, ReviewResource, ReviewResourceApiVersion, ReviewResourceKind, ReviewSession, ReviewSessionEvent, ReviewSessionEventSpec, ReviewSessionEventStatus, ReviewSessionEventType, ReviewSessionSpec, ReviewSessionStatus, ReviewValueDescriptor, ReviewValueType, SourceReference, SurveyRecordProjectionHint, } from "./review-resource.js";
|
|
13
13
|
export { toSurfaceReviewedExtractionDecision, toSurfaceReviewedExtractionImport, toSurfaceReviewedExtractionItem, } from "./surface-reviewed-extraction.js";
|
|
14
14
|
export { candidateReviewRecord, candidateSetStatusFor, SurveyInputBuilder } from "./builder.js";
|
|
@@ -17,7 +17,7 @@ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js"
|
|
|
17
17
|
export type { ReviewedCandidateResolutionInput } from "./reviewed-candidate-resolution.js";
|
|
18
18
|
export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
|
|
19
19
|
export type { CurrentProposedCandidateRole, ReviewedCurrentProposedResolutionInput, } from "./reviewed-current-proposed-resolution.js";
|
|
20
|
-
export { buildSurveyTrustBundle } from "./to-surface.js";
|
|
20
|
+
export { buildSurveyTrustBundle, ReviewAgreementError } from "./to-surface.js";
|
|
21
21
|
export type { BuildSurveyTrustBundleOptions } from "./to-surface.js";
|
|
22
22
|
export { buildCanonicalReviewedTrustInput } from "./canonical-reviewed-trust-input.js";
|
|
23
23
|
export type { BuildCanonicalReviewedTrustInputOptions, CanonicalReviewedTrustInput, } from "./canonical-reviewed-trust-input.js";
|
|
@@ -35,16 +35,17 @@ export { repeatedObservation } from "./repeated-observation.js";
|
|
|
35
35
|
export type { RepeatedObservationInput } from "./repeated-observation.js";
|
|
36
36
|
export { sourceOfAuthorityObservation, sourceOfAuthorityObservationBuilder, SourceOfAuthorityObservationBuilder, } from "./source-of-authority-observation.js";
|
|
37
37
|
export type { SourceAuthorityClass, SourceAuthorityMetadata, SourceOfAuthorityObservationBuilderArgs, SourceOfAuthorityObservationInput, } from "./source-of-authority-observation.js";
|
|
38
|
-
export { buildReviewCandidatePresentation, buildReviewItemPresentation, buildReviewResultPresentation, humanizeIdentifier, } from "./review-workbench/review-presentation.js";
|
|
39
|
-
export type { ReviewCandidatePresentation, ReviewCandidatePresentationContext, ReviewItemPresentation, ReviewItemPresentationContext, ReviewPresentationAdapter, ReviewPresentationLink, ReviewResultPresentation, ReviewTracePresentationContext, ReviewTraceRef, ReviewValuePresentationContext, } from "./review-workbench/review-presentation.js";
|
|
38
|
+
export { buildInterpretationReadingPresentation, buildReviewCandidatePresentation, buildReviewItemPresentation, buildReviewResultPresentation, humanizeIdentifier, } from "./review-workbench/review-presentation.js";
|
|
39
|
+
export type { InterpretationReadingPresentation, InterpretationReadingSource, ReviewCandidatePresentation, ReviewCandidatePresentationContext, ReviewItemPresentation, ReviewItemPresentationContext, ReviewPresentationAdapter, ReviewPresentationLink, ReviewResultPresentation, ReviewTracePresentationContext, ReviewTraceRef, ReviewValuePresentationContext, } from "./review-workbench/review-presentation.js";
|
|
40
40
|
export { apiRecordSource, manualEntrySource, policyStandardSource, uploadedDocumentSource, webPageSource, } from "./raw-source.js";
|
|
41
41
|
export type { ApiRecordSourceInput, ChecksumInput, ManualEntrySourceInput, PolicyStandardMetadata, PolicyStandardSourceInput, RawSourceInput, UploadedDocumentSourceInput, WebPageSourceInput, } from "./raw-source.js";
|
|
42
42
|
export { applyAutoAcceptPolicy, applyMappingReview, buildMappingReviewItems, lookupMapping, lookupRejectedMapping, normalizeQuestion, proposalsToCandidateSet, referenceMappingProposer, resolveQuestion, } from "./inquiry-mapping.js";
|
|
43
|
-
export type {
|
|
43
|
+
export type { AutoAcceptWarning } from "./producer-profile.js";
|
|
44
|
+
export type { AutoAcceptPolicy, AutoAcceptPolicyOptions, InquiryMapping, MappingProposal, MappingProposer, } from "./inquiry-mapping.js";
|
|
44
45
|
export { referenceUtteranceExtractor, surveyAgentUtterance, utteranceToSurveyInput, } from "./agent-utterance.js";
|
|
45
46
|
export type { ExtractedStatement, LocatorResolution, StatementBadge, StatementValueComparison, UtteranceClaimExtractor, UtteranceStatement, UtteranceStatementRecords, UtteranceTrustReport, } from "./agent-utterance.js";
|
|
46
47
|
export { mappingReviewToSurface, referenceSchemaExtractor, surveySchemaMapping, } from "./schema-mapping.js";
|
|
47
|
-
export type { MappingProposalRecord, ReviewedMapping, SchemaMappingExtractor, SchemaMappingOptions, SystemFieldRef, } from "./schema-mapping.js";
|
|
48
|
+
export type { MappingProposalRecord, ReviewedMapping, SchemaMappingExtractor, SchemaMappingOptions, SchemaMappingValue, SystemFieldRef, } from "./schema-mapping.js";
|
|
48
49
|
export { buildAuthorizedActionAuthorizing, buildPromptRef, isValidAuthorizing, validateAuthorizing } from "./review-authorizing.js";
|
|
49
50
|
export type { BuildAuthorizedActionAuthorizingInput, BuildPromptRefInput, ReviewAuthorizingIssue, ReviewAuthorizingIssueCode, } from "./review-authorizing.js";
|
|
50
51
|
export { confidenceBasisForReview, defineProductVocabulary, stableId } from "./vocabulary.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { SURVEY_INPUT_CONTRACT_VERSION } from "./types.js";
|
|
2
2
|
export { reviewResourceApiVersion } from "./review-resource.js";
|
|
3
3
|
export { buildReviewItemsFromExtractionEnvelopeImport, createExtractionEnvelopeResolutionIdentity, exportExtractionEnvelopeImport, extractionEnvelopeImportApiVersion, importExtractionEnvelope, portableExtractionResultFormat, portableExtractionResultVersion, reimportExtractionEnvelope, validateExtractionEnvelopeImport, } from "./extraction-envelope.js";
|
|
4
|
-
export { buildExtractionInspectorModel, exportExtractionInspector, filterExtractionInspectorCandidates, } from "./review-workbench/extraction-inspector.js";
|
|
4
|
+
export { buildExtractionInspectorModel, exportExtractionInspector, inspectorSourcePosture, filterExtractionInspectorCandidates, } from "./review-workbench/extraction-inspector.js";
|
|
5
5
|
export { assertReviewQueueAgainstExtractionImport, assertReviewQueueBinding, bindReviewQueue, hashReviewQueueSnapshot, UnattestedExtractionQueueError, UnattestedReviewQueueError, validateReviewQueueAgainstExtractionImport, validateReviewQueueBinding, } from "./review-workbench/queue-binding.js";
|
|
6
6
|
export { resolvePortablePdfRegion } from "./pdf-layout.js";
|
|
7
7
|
export { toSurfaceReviewedExtractionDecision, toSurfaceReviewedExtractionImport, toSurfaceReviewedExtractionItem, } from "./surface-reviewed-extraction.js";
|
|
8
8
|
export { candidateReviewRecord, candidateSetStatusFor, SurveyInputBuilder } from "./builder.js";
|
|
9
9
|
export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js";
|
|
10
10
|
export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
|
|
11
|
-
export { buildSurveyTrustBundle } from "./to-surface.js";
|
|
11
|
+
export { buildSurveyTrustBundle, ReviewAgreementError } from "./to-surface.js";
|
|
12
12
|
export { buildCanonicalReviewedTrustInput } from "./canonical-reviewed-trust-input.js";
|
|
13
13
|
export { buildSurveyLearningProjections } from "./learning-projections.js";
|
|
14
14
|
export { buildReviewedLearningUpdateProposal } from "./learning-update-proposal.js";
|
|
@@ -17,7 +17,7 @@ export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalRevi
|
|
|
17
17
|
export { fieldObservation } from "./field-observation.js";
|
|
18
18
|
export { repeatedObservation } from "./repeated-observation.js";
|
|
19
19
|
export { sourceOfAuthorityObservation, sourceOfAuthorityObservationBuilder, SourceOfAuthorityObservationBuilder, } from "./source-of-authority-observation.js";
|
|
20
|
-
export { buildReviewCandidatePresentation, buildReviewItemPresentation, buildReviewResultPresentation, humanizeIdentifier, } from "./review-workbench/review-presentation.js";
|
|
20
|
+
export { buildInterpretationReadingPresentation, buildReviewCandidatePresentation, buildReviewItemPresentation, buildReviewResultPresentation, humanizeIdentifier, } from "./review-workbench/review-presentation.js";
|
|
21
21
|
export { apiRecordSource, manualEntrySource, policyStandardSource, uploadedDocumentSource, webPageSource, } from "./raw-source.js";
|
|
22
22
|
export { applyAutoAcceptPolicy, applyMappingReview, buildMappingReviewItems, lookupMapping, lookupRejectedMapping, normalizeQuestion, proposalsToCandidateSet, referenceMappingProposer, resolveQuestion, } from "./inquiry-mapping.js";
|
|
23
23
|
export { referenceUtteranceExtractor, surveyAgentUtterance, utteranceToSurveyInput, } from "./agent-utterance.js";
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import type { DerivationRule, InquiryRecord, TrustBundle } from "@kontourai/surface";
|
|
21
21
|
import type { CanonicalClaimTarget } from "@kontourai/surface";
|
|
22
22
|
import type { Candidate, CandidateSet, ReviewOutcome } from "./types.js";
|
|
23
|
+
import type { AutoAcceptWarning } from "./producer-profile.js";
|
|
23
24
|
import type { ReviewItem } from "./review-resource.js";
|
|
24
25
|
/**
|
|
25
26
|
* A single machine- or human-generated suggestion that a natural-language
|
|
@@ -140,18 +141,31 @@ export declare function applyMappingReview(candidateSet: CandidateSet, reviewOut
|
|
|
140
141
|
export interface AutoAcceptPolicy {
|
|
141
142
|
minConfidence: number;
|
|
142
143
|
}
|
|
144
|
+
/** Optional hooks for {@link applyAutoAcceptPolicy}. */
|
|
145
|
+
export interface AutoAcceptPolicyOptions {
|
|
146
|
+
/**
|
|
147
|
+
* Called once for each proposal the policy refused because its confidence
|
|
148
|
+
* is not a finite number in [0, 1]. The proposal gets no mapping and stays
|
|
149
|
+
* in human review.
|
|
150
|
+
*/
|
|
151
|
+
onWarning?: (warning: AutoAcceptWarning) => void;
|
|
152
|
+
}
|
|
143
153
|
/**
|
|
144
154
|
* Apply an auto-accept policy to a list of proposals, returning InquiryMappings.
|
|
145
155
|
*
|
|
146
156
|
* Proposals at or above minConfidence → status "assumed", withinComfortZone: true
|
|
147
157
|
* Proposals below minConfidence → return a "needs-review" mapping (not yet durable)
|
|
158
|
+
* Proposals whose confidence is not a finite number in [0, 1] are never
|
|
159
|
+
* auto-accepted. Throws `RangeError` unless `policy.minConfidence` is a finite
|
|
160
|
+
* number in (0, 1]. Refused out-of-range proposals are reported through
|
|
161
|
+
* `options.onWarning`.
|
|
148
162
|
*
|
|
149
163
|
* Only non-conflicting proposals are auto-accepted. If proposals disagree, they
|
|
150
164
|
* need human review regardless of confidence.
|
|
151
165
|
*
|
|
152
166
|
* Returns an array of InquiryMappings (only for accepted proposals).
|
|
153
167
|
*/
|
|
154
|
-
export declare function applyAutoAcceptPolicy(proposals: MappingProposal[], policy: AutoAcceptPolicy): InquiryMapping[];
|
|
168
|
+
export declare function applyAutoAcceptPolicy(proposals: MappingProposal[], policy: AutoAcceptPolicy, options?: AutoAcceptPolicyOptions): InquiryMapping[];
|
|
155
169
|
/**
|
|
156
170
|
* Look up an InquiryMapping for a question by exact normalized-text match.
|
|
157
171
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* and lives in the flow-agents repo.
|
|
19
19
|
*/
|
|
20
20
|
import { resolveInquiry } from "@kontourai/surface";
|
|
21
|
-
import { evaluateAutoAccept, getProducerProposal, hasCandidateConflict, projectProposalsToCandidateSet, } from "./producer-profile.js";
|
|
21
|
+
import { assertValidAutoAcceptThreshold, evaluateAutoAccept, getProducerProposal, hasCandidateConflict, projectProposalsToCandidateSet, } from "./producer-profile.js";
|
|
22
22
|
import { reviewResourceApiVersion } from "./review-resource.js";
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
// Question normalization
|
|
@@ -140,13 +140,18 @@ export function applyMappingReview(candidateSet, reviewOutcome) {
|
|
|
140
140
|
*
|
|
141
141
|
* Proposals at or above minConfidence → status "assumed", withinComfortZone: true
|
|
142
142
|
* Proposals below minConfidence → return a "needs-review" mapping (not yet durable)
|
|
143
|
+
* Proposals whose confidence is not a finite number in [0, 1] are never
|
|
144
|
+
* auto-accepted. Throws `RangeError` unless `policy.minConfidence` is a finite
|
|
145
|
+
* number in (0, 1]. Refused out-of-range proposals are reported through
|
|
146
|
+
* `options.onWarning`.
|
|
143
147
|
*
|
|
144
148
|
* Only non-conflicting proposals are auto-accepted. If proposals disagree, they
|
|
145
149
|
* need human review regardless of confidence.
|
|
146
150
|
*
|
|
147
151
|
* Returns an array of InquiryMappings (only for accepted proposals).
|
|
148
152
|
*/
|
|
149
|
-
export function applyAutoAcceptPolicy(proposals, policy) {
|
|
153
|
+
export function applyAutoAcceptPolicy(proposals, policy, options = {}) {
|
|
154
|
+
assertValidAutoAcceptThreshold(policy.minConfidence);
|
|
150
155
|
if (proposals.length === 0)
|
|
151
156
|
return [];
|
|
152
157
|
// If proposals disagree, none can be auto-accepted
|
|
@@ -154,6 +159,9 @@ export function applyAutoAcceptPolicy(proposals, policy) {
|
|
|
154
159
|
return [];
|
|
155
160
|
return proposals.flatMap((proposal) => {
|
|
156
161
|
const decision = evaluateAutoAccept({ confidence: proposal.confidence, rationale: proposal.rationale, proposedAt: proposal.proposedAt }, false, policy, proposal.proposedAt);
|
|
162
|
+
if (decision.warning) {
|
|
163
|
+
options.onWarning?.({ code: decision.warning, proposalId: proposal.id, confidence: decision.confidence });
|
|
164
|
+
}
|
|
157
165
|
if (!decision.accepted)
|
|
158
166
|
return [];
|
|
159
167
|
return [
|