@kontourai/survey 0.4.24 → 0.5.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 +10 -10
- package/dist/example-data/corrected-document-candidates.d.ts +2 -0
- package/dist/{fixtures → example-data}/corrected-document-candidates.js +3 -3
- package/dist/{fixtures → example-data}/downstream-public-directory-proposal.d.ts +1 -1
- package/dist/{fixtures → example-data}/downstream-public-directory-proposal.js +1 -1
- package/dist/{fixtures → example-data}/public-directory-review-resource.d.ts +2 -2
- package/dist/{fixtures → example-data}/public-directory-review-resource.js +2 -2
- package/dist/example-data/public-field-review.d.ts +2 -0
- package/dist/{fixtures → example-data}/public-field-review.js +2 -2
- package/dist/{fixtures → example-data}/regulated-document-review-resource.d.ts +1 -1
- package/dist/{fixtures → example-data}/regulated-document-review-resource.js +3 -3
- package/dist/examples/public-field-observation.js +4 -4
- package/dist/examples/review-workbench/downstream-public-directory-adapter.d.ts +1 -1
- package/dist/examples/review-workbench/facility-credential-consumer.d.ts +2 -2
- package/dist/examples/review-workbench/facility-credential-consumer.js +8 -8
- package/dist/examples/review-workbench/server-apply-consumer.d.ts +1 -1
- package/dist/examples/review-workbench/server-apply-consumer.js +3 -3
- package/dist/src/agent-utterance.d.ts +166 -0
- package/dist/src/agent-utterance.js +373 -0
- package/dist/src/anthropic.d.ts +104 -0
- package/dist/src/anthropic.js +383 -0
- package/dist/src/index.d.ts +8 -2
- package/dist/src/index.js +4 -1
- package/dist/src/inquiry-mapping.d.ts +256 -0
- package/dist/src/inquiry-mapping.js +385 -0
- package/dist/src/review-workbench/review-queue-session.js +3 -3
- package/dist/src/review-workbench/review-surface-preview.js +1 -1
- package/dist/src/review-workbench/review-workbench-data.d.ts +4 -4
- package/dist/src/review-workbench/review-workbench-data.js +14 -14
- package/dist/src/schema-mapping.d.ts +196 -0
- package/dist/src/schema-mapping.js +486 -0
- package/dist/src/to-surface.d.ts +3 -3
- package/dist/src/to-surface.js +1 -1
- package/dist/src/types.d.ts +2 -2
- package/package.json +20 -7
- package/dist/fixtures/corrected-document-candidates.d.ts +0 -2
- package/dist/fixtures/public-field-review.d.ts +0 -2
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-utterance producer profile — ADR 0003 step 6.
|
|
3
|
+
*
|
|
4
|
+
* This module implements Survey as a producer pointed at agent utterances
|
|
5
|
+
* instead of web sources. Each factual statement in agent prose is extracted
|
|
6
|
+
* as a candidate claim and run through the Inquiry pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Integration point: surveyAgentUtterance is the clean entry point for
|
|
9
|
+
* consumers wanting to "spell-check" an agent's output for evidence. Flow-agent
|
|
10
|
+
* hook wiring (connecting this function to a live agent's output pipeline) is
|
|
11
|
+
* out of scope for this module and lives in the flow-agents repo.
|
|
12
|
+
*
|
|
13
|
+
* Hard constraint (ADR 0003 §4): nothing here silently decides. The
|
|
14
|
+
* UtteranceClaimExtractor is a pluggable interface; implementations may be
|
|
15
|
+
* deterministic or model-backed, but they are always extractors — their output
|
|
16
|
+
* has full provenance (excerpt, span, extractor name, confidence) and is
|
|
17
|
+
* run through the Inquiry pipeline rather than treated as authoritative.
|
|
18
|
+
*/
|
|
19
|
+
import { resolveInquiry } from "@kontourai/surface";
|
|
20
|
+
import { lookupMapping, resolveQuestion } from "./inquiry-mapping.js";
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// SurveyInput projection
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Project an agent utterance and its extracted statements into the standard
|
|
26
|
+
* SurveyInput shape so they can flow into buildSurveyTrustBundle.
|
|
27
|
+
*
|
|
28
|
+
* Each extracted statement lands as:
|
|
29
|
+
* RawSource (agent-utterance) → Extraction (with text-span locator) →
|
|
30
|
+
* Candidate → CandidateSet (needs-review, no review outcome) → ClaimTarget
|
|
31
|
+
*
|
|
32
|
+
* Status discipline (ADR 0003 §4, to-surface.ts producer rules):
|
|
33
|
+
* - All claims project as "proposed" — unreviewed extractions are proposals,
|
|
34
|
+
* never authoritative. assertProducerDiscipline forbids verified/assumed
|
|
35
|
+
* without a review outcome.
|
|
36
|
+
* - agent-utterance is not a manual-entry source, so extraction.locator is
|
|
37
|
+
* required. Span-located statements use text-span:start-end; span-less
|
|
38
|
+
* statements use text-span derived from the excerpt offset in the utterance
|
|
39
|
+
* (best-effort, 0-based).
|
|
40
|
+
*
|
|
41
|
+
* The returned SurveyInput can be passed directly to buildSurveyTrustBundle
|
|
42
|
+
* to produce a TrustBundle with full provenance in the Trust Bundle metadata.
|
|
43
|
+
*
|
|
44
|
+
* @param utterance - The raw agent utterance text.
|
|
45
|
+
* @param extracted - ExtractedStatements produced by a UtteranceClaimExtractor.
|
|
46
|
+
* @param context - agentId, extractor name, optional now timestamp.
|
|
47
|
+
*/
|
|
48
|
+
export function utteranceToSurveyInput(utterance, extracted, context) {
|
|
49
|
+
const { agentId, extractorName, now } = context;
|
|
50
|
+
const observedAt = (now ?? new Date()).toISOString();
|
|
51
|
+
const source = context.source ?? `agent-utterance:${agentId}`;
|
|
52
|
+
// One shared RawSource for the entire utterance
|
|
53
|
+
const sourceId = `agent-utterance:${agentId}:${observedAt}`;
|
|
54
|
+
const rawSource = {
|
|
55
|
+
id: sourceId,
|
|
56
|
+
kind: "agent-utterance",
|
|
57
|
+
sourceRef: `agent-utterance://${agentId}/${observedAt}`,
|
|
58
|
+
observedAt,
|
|
59
|
+
locatorScheme: "text-span",
|
|
60
|
+
inlineText: utterance,
|
|
61
|
+
metadata: { agentId },
|
|
62
|
+
};
|
|
63
|
+
const extractions = [];
|
|
64
|
+
const candidateSets = [];
|
|
65
|
+
const claims = [];
|
|
66
|
+
for (let idx = 0; idx < extracted.length; idx++) {
|
|
67
|
+
const statement = extracted[idx];
|
|
68
|
+
const statementId = `${sourceId}.statement.${idx}`;
|
|
69
|
+
const extractionId = `${statementId}.extraction`;
|
|
70
|
+
const candidateId = `${statementId}.candidate`;
|
|
71
|
+
const candidateSetId = `${statementId}.candidate-set`;
|
|
72
|
+
const claimId = `${statementId}.claim`;
|
|
73
|
+
// Compute locator — required for non-manual-entry sources
|
|
74
|
+
// (assertProducerDiscipline throws without it)
|
|
75
|
+
const locator = spanToLocator(statement.span) ?? excerptLocator(utterance, statement.excerpt);
|
|
76
|
+
const extraction = {
|
|
77
|
+
id: extractionId,
|
|
78
|
+
sourceId,
|
|
79
|
+
target: canonicalTargetKey(statement.target),
|
|
80
|
+
value: statement.value ?? null,
|
|
81
|
+
confidence: statement.confidence,
|
|
82
|
+
locator,
|
|
83
|
+
excerpt: statement.excerpt,
|
|
84
|
+
extractor: extractorName,
|
|
85
|
+
extractedAt: observedAt,
|
|
86
|
+
metadata: {
|
|
87
|
+
agentUtterance: {
|
|
88
|
+
span: statement.span,
|
|
89
|
+
excerpt: statement.excerpt,
|
|
90
|
+
extractorName,
|
|
91
|
+
confidence: statement.confidence,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const candidate = {
|
|
96
|
+
id: candidateId,
|
|
97
|
+
extractionId,
|
|
98
|
+
value: statement.value ?? null,
|
|
99
|
+
confidence: statement.confidence,
|
|
100
|
+
metadata: {
|
|
101
|
+
agentUtterance: {
|
|
102
|
+
span: statement.span,
|
|
103
|
+
excerpt: statement.excerpt,
|
|
104
|
+
extractorName,
|
|
105
|
+
confidence: statement.confidence,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
// needs-review status → statusFor returns "proposed" (no review outcome)
|
|
110
|
+
const candidateSet = {
|
|
111
|
+
id: candidateSetId,
|
|
112
|
+
target: canonicalTargetKey(statement.target),
|
|
113
|
+
candidates: [candidate],
|
|
114
|
+
selectedCandidateId: candidateId,
|
|
115
|
+
status: "needs-review",
|
|
116
|
+
metadata: {
|
|
117
|
+
agentUtterance: {
|
|
118
|
+
subjectType: statement.target.subjectType,
|
|
119
|
+
subjectId: statement.target.subjectId,
|
|
120
|
+
fieldOrBehavior: statement.target.fieldOrBehavior,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
// Unreviewed: status is omitted so statusFor() computes "proposed"
|
|
125
|
+
// assertProducerDiscipline: no verified/assumed without review → compliant
|
|
126
|
+
const claimTarget = {
|
|
127
|
+
id: claimId,
|
|
128
|
+
candidateSetId,
|
|
129
|
+
candidateId,
|
|
130
|
+
subjectType: statement.target.subjectType,
|
|
131
|
+
subjectId: statement.target.subjectId,
|
|
132
|
+
surface: "agent-utterance.profile",
|
|
133
|
+
claimType: "agent-extraction",
|
|
134
|
+
fieldOrBehavior: statement.target.fieldOrBehavior,
|
|
135
|
+
value: statement.value,
|
|
136
|
+
// status intentionally omitted → computed as "proposed" by statusFor
|
|
137
|
+
impactLevel: "low",
|
|
138
|
+
collectedBy: extractorName,
|
|
139
|
+
metadata: {
|
|
140
|
+
survey: {
|
|
141
|
+
agentUtterance: {
|
|
142
|
+
agentId,
|
|
143
|
+
extractorName,
|
|
144
|
+
excerpt: statement.excerpt,
|
|
145
|
+
span: statement.span,
|
|
146
|
+
confidence: statement.confidence,
|
|
147
|
+
locator,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
extractions.push(extraction);
|
|
153
|
+
candidateSets.push(candidateSet);
|
|
154
|
+
claims.push(claimTarget);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
source,
|
|
158
|
+
generatedAt: observedAt,
|
|
159
|
+
rawSources: [rawSource],
|
|
160
|
+
extractions,
|
|
161
|
+
candidateSets,
|
|
162
|
+
reviewOutcomes: [],
|
|
163
|
+
claims,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Main entry point
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
/**
|
|
170
|
+
* Survey an agent utterance, returning a trust report for each extracted claim.
|
|
171
|
+
*
|
|
172
|
+
* Steps:
|
|
173
|
+
* 1. Build a RawSource for the utterance (kind: "agent-utterance").
|
|
174
|
+
* 2. Run the extractor → project each statement into Survey records with full
|
|
175
|
+
* provenance (excerpt, span locator, extractor name, confidence).
|
|
176
|
+
* 3. Resolve each extracted claim against the bundle via resolveInquiry or
|
|
177
|
+
* resolveQuestion (if mappings are provided).
|
|
178
|
+
* 4. Return an UtteranceTrustReport with per-statement badges.
|
|
179
|
+
*
|
|
180
|
+
* This function is the integration point for consumers. Flow-agent hook wiring
|
|
181
|
+
* lives in the flow-agents repo.
|
|
182
|
+
*/
|
|
183
|
+
export async function surveyAgentUtterance(utterance, extractor, context) {
|
|
184
|
+
const { bundle, mappings, rules, now, agentId } = context;
|
|
185
|
+
const observedAt = (now ?? new Date()).toISOString();
|
|
186
|
+
// Step 1: Build a RawSource for this utterance
|
|
187
|
+
const sourceId = `agent-utterance:${agentId}:${observedAt}`;
|
|
188
|
+
const source = {
|
|
189
|
+
id: sourceId,
|
|
190
|
+
kind: "agent-utterance",
|
|
191
|
+
sourceRef: `agent-utterance://${agentId}/${observedAt}`,
|
|
192
|
+
observedAt,
|
|
193
|
+
locatorScheme: "text-span",
|
|
194
|
+
inlineText: utterance,
|
|
195
|
+
metadata: { agentId },
|
|
196
|
+
};
|
|
197
|
+
// Step 2: Extract statements
|
|
198
|
+
const extracted = await Promise.resolve(extractor.extract(utterance));
|
|
199
|
+
// Step 3 & 4: Resolve each statement and build the report
|
|
200
|
+
const statements = [];
|
|
201
|
+
for (const statement of extracted) {
|
|
202
|
+
const statementId = `${sourceId}.statement.${statements.length}`;
|
|
203
|
+
// Build Survey records for provenance
|
|
204
|
+
const extractionId = `${statementId}.extraction`;
|
|
205
|
+
const extraction = {
|
|
206
|
+
id: extractionId,
|
|
207
|
+
sourceId,
|
|
208
|
+
target: canonicalTargetKey(statement.target),
|
|
209
|
+
value: statement.value ?? null,
|
|
210
|
+
confidence: statement.confidence,
|
|
211
|
+
locator: statement.span ? `text-span:${statement.span.start}-${statement.span.end}` : undefined,
|
|
212
|
+
excerpt: statement.excerpt,
|
|
213
|
+
extractor: extractor.name,
|
|
214
|
+
extractedAt: observedAt,
|
|
215
|
+
metadata: {
|
|
216
|
+
agentUtterance: {
|
|
217
|
+
span: statement.span,
|
|
218
|
+
excerpt: statement.excerpt,
|
|
219
|
+
extractorName: extractor.name,
|
|
220
|
+
confidence: statement.confidence,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
// Resolve the claim
|
|
225
|
+
let inquiryRecord;
|
|
226
|
+
if (mappings && mappings.length > 0) {
|
|
227
|
+
// If we have question-level mappings, check them first by building
|
|
228
|
+
// a question from the target
|
|
229
|
+
const syntheticQuestion = targetToQuestion(statement.target);
|
|
230
|
+
const mapping = lookupMapping(mappings, syntheticQuestion);
|
|
231
|
+
if (mapping) {
|
|
232
|
+
inquiryRecord = resolveQuestion(bundle, syntheticQuestion, {
|
|
233
|
+
mappings,
|
|
234
|
+
rules,
|
|
235
|
+
now,
|
|
236
|
+
askedBy: agentId,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// No mapping: resolve directly by canonical target
|
|
241
|
+
inquiryRecord = resolveByTarget(bundle, statement.target, agentId, observedAt, rules, now);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Resolve directly by canonical target
|
|
246
|
+
inquiryRecord = resolveByTarget(bundle, statement.target, agentId, observedAt, rules, now);
|
|
247
|
+
}
|
|
248
|
+
const badge = badgeFromRecord(inquiryRecord);
|
|
249
|
+
statements.push({
|
|
250
|
+
excerpt: statement.excerpt,
|
|
251
|
+
span: statement.span,
|
|
252
|
+
target: statement.target,
|
|
253
|
+
inquiryRecord,
|
|
254
|
+
badge,
|
|
255
|
+
});
|
|
256
|
+
// Suppress unused-variable warning for extraction
|
|
257
|
+
void extraction;
|
|
258
|
+
}
|
|
259
|
+
return { source, statements };
|
|
260
|
+
}
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// Internal helpers
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
function resolveByTarget(bundle, target, askedBy, askedAt, rules, now) {
|
|
265
|
+
const id = `inquiry.direct.${canonicalTargetKey(target)}.${askedAt}`;
|
|
266
|
+
const inquiry = {
|
|
267
|
+
id,
|
|
268
|
+
question: targetToQuestion(target),
|
|
269
|
+
target,
|
|
270
|
+
askedBy,
|
|
271
|
+
askedAt,
|
|
272
|
+
};
|
|
273
|
+
return resolveInquiry(bundle, inquiry, { now, rules });
|
|
274
|
+
}
|
|
275
|
+
function canonicalTargetKey(target) {
|
|
276
|
+
return `${target.subjectType}/${target.subjectId}/${target.fieldOrBehavior}`;
|
|
277
|
+
}
|
|
278
|
+
function targetToQuestion(target) {
|
|
279
|
+
return `${target.subjectId} ${target.fieldOrBehavior}`;
|
|
280
|
+
}
|
|
281
|
+
function badgeFromRecord(record) {
|
|
282
|
+
if (record.outcome === "unsupported")
|
|
283
|
+
return "unsupported";
|
|
284
|
+
const status = record.answer?.status;
|
|
285
|
+
if (!status)
|
|
286
|
+
return "unsupported";
|
|
287
|
+
if (status === "verified")
|
|
288
|
+
return "verified";
|
|
289
|
+
if (status === "assumed")
|
|
290
|
+
return "assumed";
|
|
291
|
+
if (status === "stale")
|
|
292
|
+
return "stale";
|
|
293
|
+
if (status === "disputed")
|
|
294
|
+
return "disputed";
|
|
295
|
+
if (status === "rejected" || status === "superseded")
|
|
296
|
+
return "rejected";
|
|
297
|
+
return "unsupported";
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Convert a text-span to a locator string.
|
|
301
|
+
*/
|
|
302
|
+
function spanToLocator(span) {
|
|
303
|
+
if (!span)
|
|
304
|
+
return undefined;
|
|
305
|
+
return `text-span:${span.start}-${span.end}`;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Best-effort locator from excerpt text — find the first occurrence of the
|
|
309
|
+
* excerpt in the utterance and use that as a text-span locator.
|
|
310
|
+
* Falls back to text-span:0-0 if the excerpt is not found.
|
|
311
|
+
*/
|
|
312
|
+
function excerptLocator(utterance, excerpt) {
|
|
313
|
+
const idx = utterance.indexOf(excerpt);
|
|
314
|
+
if (idx >= 0) {
|
|
315
|
+
return `text-span:${idx}-${idx + excerpt.length}`;
|
|
316
|
+
}
|
|
317
|
+
// Fallback: anchor to start (preserves discipline contract; locator is
|
|
318
|
+
// best-effort for span-less extractors)
|
|
319
|
+
return `text-span:0-${excerpt.length}`;
|
|
320
|
+
}
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// Reference extractor (deterministic, for tests — not for production use)
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
/**
|
|
325
|
+
* Reference UtteranceClaimExtractor for tests.
|
|
326
|
+
*
|
|
327
|
+
* REFERENCE IMPLEMENTATION ONLY — not suitable for production extraction.
|
|
328
|
+
*
|
|
329
|
+
* Parsing strategy: looks for statements matching the pattern:
|
|
330
|
+
* "<subjectId> <fieldOrBehavior> is <value>"
|
|
331
|
+
* or
|
|
332
|
+
* "<subjectId> <fieldOrBehavior>: <value>"
|
|
333
|
+
*
|
|
334
|
+
* where subjectId and fieldOrBehavior are single words. This intentionally
|
|
335
|
+
* simple and transparent pattern lets tests be deterministic.
|
|
336
|
+
*
|
|
337
|
+
* The subjectType is always "unknown" since it cannot be inferred from text
|
|
338
|
+
* alone in this reference implementation.
|
|
339
|
+
*/
|
|
340
|
+
export const referenceUtteranceExtractor = {
|
|
341
|
+
name: "reference-utterance-extractor",
|
|
342
|
+
extract(utterance) {
|
|
343
|
+
const results = [];
|
|
344
|
+
// Pattern: "<word> <word> is <value>" or "<word> <word>: <value>"
|
|
345
|
+
// Value is a single non-whitespace token; trailing punctuation is stripped.
|
|
346
|
+
const isPattern = /\b(\S+)\s+(\S+)\s+is\s+(\S+)/giu;
|
|
347
|
+
const colonPattern = /\b(\S+)\s+(\S+):\s*(\S+)/giu;
|
|
348
|
+
for (const pattern of [isPattern, colonPattern]) {
|
|
349
|
+
let match;
|
|
350
|
+
while ((match = pattern.exec(utterance)) !== null) {
|
|
351
|
+
const [full, subjectId, fieldOrBehavior, rawValue] = match;
|
|
352
|
+
if (!subjectId || !fieldOrBehavior || rawValue === undefined)
|
|
353
|
+
continue;
|
|
354
|
+
const start = match.index;
|
|
355
|
+
const end = start + full.length;
|
|
356
|
+
// Strip trailing punctuation from the captured value
|
|
357
|
+
const value = rawValue.replace(/[.!?,;]+$/u, "");
|
|
358
|
+
results.push({
|
|
359
|
+
target: {
|
|
360
|
+
subjectType: "unknown",
|
|
361
|
+
subjectId: subjectId.toLowerCase(),
|
|
362
|
+
fieldOrBehavior: fieldOrBehavior.toLowerCase(),
|
|
363
|
+
},
|
|
364
|
+
value,
|
|
365
|
+
excerpt: full.replace(/[.!?,;]+$/u, ""),
|
|
366
|
+
span: { start, end },
|
|
367
|
+
confidence: 0.6,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return results;
|
|
372
|
+
},
|
|
373
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic production adapters for Survey's pluggable interfaces.
|
|
3
|
+
*
|
|
4
|
+
* ADR 0003 §4 compliance: these implementations are PROPOSERS only. Every
|
|
5
|
+
* output is a proposal (MappingProposal / ExtractedStatement) that goes
|
|
6
|
+
* through the existing review/auto-accept machinery before counting.
|
|
7
|
+
* Nothing here bypasses review.
|
|
8
|
+
*
|
|
9
|
+
* Subpath export: import from "@kontourai/survey/anthropic" — this module is
|
|
10
|
+
* NOT re-exported from the main index.ts so consumers without @anthropic-ai/sdk
|
|
11
|
+
* pay nothing.
|
|
12
|
+
*
|
|
13
|
+
* Injected client: both factories accept an optional pre-built client so tests
|
|
14
|
+
* can inject a fake without hitting the network. If no client is provided, one
|
|
15
|
+
* is constructed from opts.apiKey (falling back to process.env.ANTHROPIC_API_KEY).
|
|
16
|
+
*/
|
|
17
|
+
import type { MappingProposer } from "./inquiry-mapping.js";
|
|
18
|
+
import type { UtteranceClaimExtractor } from "./agent-utterance.js";
|
|
19
|
+
export interface AnthropicToolResultBlock {
|
|
20
|
+
type: "tool_result";
|
|
21
|
+
tool_use_id: string;
|
|
22
|
+
content: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AnthropicToolUseBlock {
|
|
25
|
+
type: "tool_use";
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
input: unknown;
|
|
29
|
+
}
|
|
30
|
+
export type AnthropicContentBlock = {
|
|
31
|
+
type: "text";
|
|
32
|
+
text: string;
|
|
33
|
+
} | AnthropicToolUseBlock;
|
|
34
|
+
export interface AnthropicMessage {
|
|
35
|
+
id: string;
|
|
36
|
+
type: "message";
|
|
37
|
+
role: "assistant";
|
|
38
|
+
content: AnthropicContentBlock[];
|
|
39
|
+
model: string;
|
|
40
|
+
stop_reason: string | null;
|
|
41
|
+
usage: {
|
|
42
|
+
input_tokens: number;
|
|
43
|
+
output_tokens: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface AnthropicTool {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
input_schema: {
|
|
50
|
+
type: "object";
|
|
51
|
+
properties: Record<string, unknown>;
|
|
52
|
+
required?: string[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface AnthropicMessageCreateParams {
|
|
56
|
+
model: string;
|
|
57
|
+
max_tokens: number;
|
|
58
|
+
messages: Array<{
|
|
59
|
+
role: "user" | "assistant";
|
|
60
|
+
content: string;
|
|
61
|
+
}>;
|
|
62
|
+
tools: AnthropicTool[];
|
|
63
|
+
tool_choice: {
|
|
64
|
+
type: "tool";
|
|
65
|
+
name: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Minimal interface matching @anthropic-ai/sdk Anthropic.messages.create.
|
|
70
|
+
* Accept the real SDK client or a test double.
|
|
71
|
+
*/
|
|
72
|
+
export interface AnthropicMessagesClient {
|
|
73
|
+
create(params: AnthropicMessageCreateParams): Promise<AnthropicMessage>;
|
|
74
|
+
}
|
|
75
|
+
export interface AnthropicAdapterOptions {
|
|
76
|
+
/** Injected client (real or mock). If absent, one is built from apiKey. */
|
|
77
|
+
client?: AnthropicMessagesClient;
|
|
78
|
+
/** API key. Falls back to ANTHROPIC_API_KEY env var. */
|
|
79
|
+
apiKey?: string;
|
|
80
|
+
/** Model to use. Defaults to "claude-sonnet-4-6". */
|
|
81
|
+
model?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create a MappingProposer backed by Anthropic's API using forced tool-use.
|
|
85
|
+
*
|
|
86
|
+
* ADR 0003 §4: returns PROPOSALS only — they flow through the existing
|
|
87
|
+
* review/auto-accept machinery before counting as mappings.
|
|
88
|
+
*
|
|
89
|
+
* Tool output is validated strictly: malformed items (missing required fields,
|
|
90
|
+
* out-of-range confidence, no target and no rule) are filtered out rather than
|
|
91
|
+
* silently accepted.
|
|
92
|
+
*/
|
|
93
|
+
export declare function createAnthropicMappingProposer(opts?: AnthropicAdapterOptions): MappingProposer;
|
|
94
|
+
/**
|
|
95
|
+
* Create a UtteranceClaimExtractor backed by Anthropic's API using forced tool-use.
|
|
96
|
+
*
|
|
97
|
+
* ADR 0003 §4: returns EXTRACTED STATEMENTS only — they carry full provenance
|
|
98
|
+
* (excerpt, span, extractor name, confidence) and flow through the Inquiry
|
|
99
|
+
* pipeline. They are never treated as authoritative.
|
|
100
|
+
*
|
|
101
|
+
* Malformed tool output is rejected/filtered — items missing required fields
|
|
102
|
+
* (subjectId, fieldOrBehavior, excerpt, confidence) are dropped.
|
|
103
|
+
*/
|
|
104
|
+
export declare function createAnthropicUtteranceExtractor(opts?: AnthropicAdapterOptions): UtteranceClaimExtractor;
|