@openapi-flow/core 0.3.10 → 0.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arguments/clarification/api-input-requirement-dialect.d.ts +4 -0
- package/dist/arguments/clarification/api-input-requirement-dialect.js +8 -0
- package/dist/arguments/clarification/extraction/capture-api-input-answer-context.js +2 -3
- package/dist/arguments/clarification/extraction/create-api-input-answer-extraction-record.js +9 -2
- package/dist/arguments/clarification/extraction/resolve-api-input-answer-interpretations.d.ts +3 -0
- package/dist/arguments/clarification/extraction/resolve-api-input-answer-interpretations.js +52 -0
- package/dist/arguments/clarification/extraction/resolve-api-input-answer-sources.js +3 -3
- package/dist/arguments/clarification/extraction/resolve-api-input-source-selections.js +1 -0
- package/dist/arguments/clarification/extraction/validate-api-input-answer-analysis.d.ts +3 -0
- package/dist/arguments/clarification/extraction/validate-api-input-answer-analysis.js +56 -0
- package/dist/arguments/clarification/extraction/validate-api-input-answer-extraction-record.js +4 -0
- package/dist/arguments/enum/describe-api-argument-enum-context.d.ts +4 -0
- package/dist/arguments/enum/describe-api-argument-enum-context.js +41 -0
- package/dist/arguments/enum/describe-api-input-enum-answer-choices.d.ts +4 -0
- package/dist/arguments/enum/describe-api-input-enum-answer-choices.js +23 -0
- package/dist/arguments/enum/expand-requested-enum-requirements.d.ts +4 -0
- package/dist/arguments/enum/expand-requested-enum-requirements.js +45 -0
- package/dist/arguments/enum/list-api-input-enum-candidates.d.ts +3 -0
- package/dist/arguments/enum/list-api-input-enum-candidates.js +73 -0
- package/dist/arguments/enum/resolve-api-argument-enum-interpretation.d.ts +3 -0
- package/dist/arguments/enum/resolve-api-argument-enum-interpretation.js +26 -0
- package/dist/arguments/sources/resolve-api-literal-proposal-sources.d.ts +3 -0
- package/dist/arguments/sources/resolve-api-literal-proposal-sources.js +58 -0
- package/dist/draft/edits/api/describe-workflow-draft-api-enum-choices.d.ts +3 -0
- package/dist/draft/edits/api/describe-workflow-draft-api-enum-choices.js +70 -0
- package/dist/draft/edits/api/resolve-workflow-draft-api-call-arguments.js +9 -5
- package/dist/draft/edits/api/resolve-workflow-draft-api-enum-selections.d.ts +4 -0
- package/dist/draft/edits/api/resolve-workflow-draft-api-enum-selections.js +38 -0
- package/dist/draft/edits/api/resolve-workflow-draft-api-literal-sources.js +9 -56
- package/dist/draft/edits/enum/verify-workflow-draft-api-enum-selection.d.ts +3 -0
- package/dist/draft/edits/enum/verify-workflow-draft-api-enum-selection.js +23 -0
- package/dist/draft/edits/inputs/describe-workflow-draft-api-input-edit-answer-requirements.d.ts +4 -0
- package/dist/draft/edits/inputs/describe-workflow-draft-api-input-edit-answer-requirements.js +30 -0
- package/dist/draft/edits/inputs/describe-workflow-draft-api-input-edit-enum-choices.d.ts +4 -0
- package/dist/draft/edits/inputs/describe-workflow-draft-api-input-edit-enum-choices.js +42 -0
- package/dist/draft/edits/inputs/resolve-workflow-draft-api-input-edit-reply.d.ts +3 -0
- package/dist/draft/edits/inputs/resolve-workflow-draft-api-input-edit-reply.js +24 -0
- package/dist/draft/edits/inputs/resolve-workflow-draft-api-input-edits.js +20 -0
- package/dist/public-api.d.ts +18 -0
- package/dist/public-api.js +13 -0
- package/dist/schemas/api-input-answer-extraction-schema.d.ts +16 -0
- package/dist/schemas/api-input-answer-extraction-schema.js +9 -0
- package/dist/schemas/api-input-enum-answer-selection-schema.d.ts +25 -0
- package/dist/schemas/api-input-enum-answer-selection-schema.js +11 -0
- package/dist/schemas/create-workflow-draft-api-input-edit-values-schema.js +11 -2
- package/dist/schemas/workflow-draft-api-enum-choice-schema.d.ts +5 -0
- package/dist/schemas/workflow-draft-api-enum-choice-schema.js +25 -0
- package/dist/schemas/workflow-draft-api-enum-selection-schema.d.ts +4 -0
- package/dist/schemas/workflow-draft-api-enum-selection-schema.js +14 -0
- package/dist/schemas/workflow-draft-api-input-edit-proposal-schema.js +3 -1
- package/dist/types/api-argument-enum.d.ts +23 -0
- package/dist/types/api-argument-enum.js +1 -0
- package/dist/types/api-arguments.d.ts +6 -0
- package/dist/types/api-clarification.d.ts +2 -0
- package/dist/types/api-input-answer-extraction.d.ts +11 -2
- package/dist/types/api-input-enum-answers.d.ts +25 -0
- package/dist/types/api-input-enum-answers.js +1 -0
- package/dist/types/api-input-enum.d.ts +26 -0
- package/dist/types/api-input-enum.js +1 -0
- package/dist/types/api-input-literal-source.d.ts +15 -0
- package/dist/types/workflow-draft-api-call-planning.d.ts +4 -0
- package/dist/types/workflow-draft-api-enum-selection.d.ts +39 -0
- package/dist/types/workflow-draft-api-enum-selection.js +1 -0
- package/dist/types/workflow-draft-api-input-edit.d.ts +15 -2
- package/dist/types/workflow-draft-api-literal-sources.d.ts +3 -9
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiInputRequirement } from '../../types/api-clarification.js';
|
|
2
|
+
import type { SchemaDialect } from '../../types/schema-dialect.js';
|
|
3
|
+
/** A compiled projection declares its actual dialect; the source OAS version remains unchanged. */
|
|
4
|
+
export declare function apiInputRequirementDialect(requirement: ApiInputRequirement): SchemaDialect;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** A compiled projection declares its actual dialect; the source OAS version remains unchanged. */
|
|
2
|
+
export function apiInputRequirementDialect(requirement) {
|
|
3
|
+
if (requirement.schemaDialect === 'draft-2020-12')
|
|
4
|
+
return 'draft-2020-12';
|
|
5
|
+
return requirement.openapiVersion.startsWith('3.0.')
|
|
6
|
+
? 'openapi-3.0'
|
|
7
|
+
: 'draft-2020-12';
|
|
8
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { apiInputAnswerContextSchema } from '../../../schemas/api-input-answer-extraction-schema.js';
|
|
2
2
|
import { createOasValueValidator } from '../../../openapi/common/create-oas-value-validator.js';
|
|
3
|
+
import { apiInputRequirementDialect } from '../api-input-requirement-dialect.js';
|
|
3
4
|
/** Capture supplied pending contracts before any asynchronous model work. */
|
|
4
5
|
export function captureApiInputAnswerContext(input) {
|
|
5
6
|
apiInputAnswerContextSchema.parse(input);
|
|
@@ -9,9 +10,7 @@ export function captureApiInputAnswerContext(input) {
|
|
|
9
10
|
if (ids.has(requirement.id))
|
|
10
11
|
throw new Error(`Duplicate API input requirementId: ${requirement.id}`);
|
|
11
12
|
ids.add(requirement.id);
|
|
12
|
-
createOasValueValidator(requirement.schema, requirement.
|
|
13
|
-
? 'openapi-3.0'
|
|
14
|
-
: 'draft-2020-12', `API input requirement ${requirement.id} ${requirement.targetPointer}`);
|
|
13
|
+
createOasValueValidator(requirement.schema, apiInputRequirementDialect(requirement), `API input requirement ${requirement.id} ${requirement.targetPointer}`);
|
|
15
14
|
}
|
|
16
15
|
return snapshot;
|
|
17
16
|
}
|
package/dist/arguments/clarification/extraction/create-api-input-answer-extraction-record.js
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { apiInputAnswerExtractionDecisionSchema } from '../../../schemas/api-input-answer-extraction-schema.js';
|
|
2
2
|
import { captureApiInputAnswerContext } from './capture-api-input-answer-context.js';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveApiInputAnswerInterpretations } from './resolve-api-input-answer-interpretations.js';
|
|
4
4
|
export function createApiInputAnswerExtractionRecord(input) {
|
|
5
5
|
const evidence = captureApiInputAnswerContext({
|
|
6
6
|
reply: input.reply,
|
|
7
7
|
requirements: input.requirements,
|
|
8
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
9
|
+
...(input.selectOasEnums === undefined
|
|
10
|
+
? {}
|
|
11
|
+
: { selectOasEnums: input.selectOasEnums }),
|
|
8
12
|
});
|
|
9
13
|
const decision = structuredClone(apiInputAnswerExtractionDecisionSchema.parse(input.decision));
|
|
10
|
-
const verification =
|
|
14
|
+
const verification = resolveApiInputAnswerInterpretations({
|
|
11
15
|
...evidence,
|
|
12
16
|
selections: decision.answers.map((source) => ({
|
|
13
17
|
requirementId: source.requirementId,
|
|
14
18
|
sourceFormat: source.sourceFormat,
|
|
15
19
|
sourceText: source.sourceText,
|
|
16
20
|
})),
|
|
21
|
+
...(decision.enumSelections === undefined
|
|
22
|
+
? {}
|
|
23
|
+
: { enumSelections: decision.enumSelections }),
|
|
17
24
|
});
|
|
18
25
|
return {
|
|
19
26
|
kind: 'model-extracted-api-input-answers',
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ResolveApiInputAnswerInterpretationsInput, ResolvedApiInputAnswerInterpretations } from '../../../types/api-input-enum-answers.js';
|
|
2
|
+
/** Literal values and semantic intent have different sources and must never be conflated. */
|
|
3
|
+
export declare function resolveApiInputAnswerInterpretations(input: ResolveApiInputAnswerInterpretationsInput): ResolvedApiInputAnswerInterpretations;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { apiInputEnumAnswerSelectionsSchema } from '../../../schemas/api-input-enum-answer-selection-schema.js';
|
|
2
|
+
import { captureApiInputAnswerContext } from './capture-api-input-answer-context.js';
|
|
3
|
+
import { resolveApiInputAnswerSources } from './resolve-api-input-answer-sources.js';
|
|
4
|
+
import { describeApiInputEnumAnswerChoices } from '../../enum/describe-api-input-enum-answer-choices.js';
|
|
5
|
+
import { readApiInputLiteralSource } from '../../sources/read-api-input-literal-source.js';
|
|
6
|
+
/** Literal values and semantic intent have different sources and must never be conflated. */
|
|
7
|
+
export function resolveApiInputAnswerInterpretations(input) {
|
|
8
|
+
const context = captureApiInputAnswerContext({
|
|
9
|
+
reply: input.reply,
|
|
10
|
+
requirements: input.requirements,
|
|
11
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
12
|
+
...(input.selectOasEnums === undefined
|
|
13
|
+
? {}
|
|
14
|
+
: { selectOasEnums: input.selectOasEnums }),
|
|
15
|
+
});
|
|
16
|
+
const result = resolveApiInputAnswerSources({
|
|
17
|
+
reply: context.reply,
|
|
18
|
+
requirements: context.requirements,
|
|
19
|
+
selections: input.selections,
|
|
20
|
+
});
|
|
21
|
+
if (input.enumSelections === undefined)
|
|
22
|
+
return result;
|
|
23
|
+
const selections = apiInputEnumAnswerSelectionsSchema.parse(input.enumSelections);
|
|
24
|
+
if (context.selectOasEnums !== true)
|
|
25
|
+
throw new Error('API enum answer selection requires selectOasEnums: true in the original context');
|
|
26
|
+
const choices = describeApiInputEnumAnswerChoices(context);
|
|
27
|
+
const answered = new Set(result.answers.map((answer) => answer.requirementId));
|
|
28
|
+
result.enumSelections = selections.map((selection) => {
|
|
29
|
+
if (answered.has(selection.requirementId))
|
|
30
|
+
throw new Error(`Duplicate requirementId: ${selection.requirementId}`);
|
|
31
|
+
const candidate = choices
|
|
32
|
+
.find((choice) => choice.requirementId === selection.requirementId)
|
|
33
|
+
?.candidates.find((item) => item.id === selection.candidateId);
|
|
34
|
+
if (!candidate)
|
|
35
|
+
throw new Error(`API enum answer ${selection.requirementId} has an unknown or stale OAS candidate: ${selection.candidateId}`);
|
|
36
|
+
const { occurrences } = readApiInputLiteralSource({
|
|
37
|
+
...selection.intent,
|
|
38
|
+
text: context.reply,
|
|
39
|
+
label: `Enum answer intent ${selection.requirementId}`,
|
|
40
|
+
});
|
|
41
|
+
answered.add(selection.requirementId);
|
|
42
|
+
result.answers.push({
|
|
43
|
+
requirementId: selection.requirementId,
|
|
44
|
+
value: structuredClone(candidate.value),
|
|
45
|
+
});
|
|
46
|
+
return { ...selection, candidate: structuredClone(candidate), occurrences };
|
|
47
|
+
});
|
|
48
|
+
result.unansweredRequirementIds = context.requirements
|
|
49
|
+
.filter((item) => !answered.has(item.id))
|
|
50
|
+
.map((item) => item.id);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
@@ -2,11 +2,13 @@ import { apiInputAnswerSourcesSchema } from '../../../schemas/api-input-answer-e
|
|
|
2
2
|
import { captureApiInputAnswerContext } from './capture-api-input-answer-context.js';
|
|
3
3
|
import { readApiInputLiteralSource } from '../../sources/read-api-input-literal-source.js';
|
|
4
4
|
import { createOasValueValidator } from '../../../openapi/common/create-oas-value-validator.js';
|
|
5
|
+
import { apiInputRequirementDialect } from '../api-input-requirement-dialect.js';
|
|
5
6
|
/** Model-selected source text is the only value source, never a model-generated value. */
|
|
6
7
|
export function resolveApiInputAnswerSources(input) {
|
|
7
8
|
const context = captureApiInputAnswerContext({
|
|
8
9
|
reply: input.reply,
|
|
9
10
|
requirements: input.requirements,
|
|
11
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
10
12
|
});
|
|
11
13
|
const selections = apiInputAnswerSourcesSchema.parse(input.selections);
|
|
12
14
|
const answered = new Set();
|
|
@@ -24,9 +26,7 @@ export function resolveApiInputAnswerSources(input) {
|
|
|
24
26
|
text: context.reply,
|
|
25
27
|
label: `Answer ${requirement.id}`,
|
|
26
28
|
});
|
|
27
|
-
const checked = createOasValueValidator(requirement.schema, requirement.
|
|
28
|
-
? 'openapi-3.0'
|
|
29
|
-
: 'draft-2020-12', `answer ${requirement.targetPointer}`).validate(value);
|
|
29
|
+
const checked = createOasValueValidator(requirement.schema, apiInputRequirementDialect(requirement), `answer ${requirement.targetPointer}`).validate(value);
|
|
30
30
|
if (!checked.valid)
|
|
31
31
|
throw new Error(`Answer ${requirement.id} violates its supplied OAS schema: ${checked.errors.map((error) => error.message).join('; ')}`);
|
|
32
32
|
answered.add(requirement.id);
|
|
@@ -6,6 +6,7 @@ export function resolveApiInputSourceSelections(input) {
|
|
|
6
6
|
const context = captureApiInputAnswerContext({
|
|
7
7
|
reply: input.reply,
|
|
8
8
|
requirements: input.requirements,
|
|
9
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
9
10
|
});
|
|
10
11
|
const catalog = describeApiInputSources(context.reply);
|
|
11
12
|
const selections = apiInputAnswerSelectionsSchema.parse(input.selections);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ValidateApiInputAnswerAnalysisInput } from '../../../types/api-input-answer-extraction.js';
|
|
2
|
+
/** Replay model choices against independently supplied original questions and reply. */
|
|
3
|
+
export declare function validateApiInputAnswerAnalysis(input: ValidateApiInputAnswerAnalysisInput): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
import { resolveApiInputAnswerInterpretations } from './resolve-api-input-answer-interpretations.js';
|
|
3
|
+
import { validateApiInputAnswerExtractionRecord } from './validate-api-input-answer-extraction-record.js';
|
|
4
|
+
/** Replay model choices against independently supplied original questions and reply. */
|
|
5
|
+
export function validateApiInputAnswerAnalysis(input) {
|
|
6
|
+
const context = {
|
|
7
|
+
reply: input.reply,
|
|
8
|
+
requirements: input.requirements,
|
|
9
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
10
|
+
...(input.selectOasEnums === undefined
|
|
11
|
+
? {}
|
|
12
|
+
: { selectOasEnums: input.selectOasEnums }),
|
|
13
|
+
};
|
|
14
|
+
const { analysis } = input;
|
|
15
|
+
if (!Array.isArray(analysis.sources))
|
|
16
|
+
throw new Error('API input answer analysis.sources must be an array');
|
|
17
|
+
const resolved = resolveApiInputAnswerInterpretations({
|
|
18
|
+
...context,
|
|
19
|
+
selections: analysis.sources.map((source) => ({
|
|
20
|
+
requirementId: source.requirementId,
|
|
21
|
+
sourceFormat: source.sourceFormat,
|
|
22
|
+
sourceText: source.sourceText,
|
|
23
|
+
})),
|
|
24
|
+
...(analysis.enumSelections === undefined
|
|
25
|
+
? {}
|
|
26
|
+
: {
|
|
27
|
+
enumSelections: analysis.enumSelections.map((selection) => ({
|
|
28
|
+
requirementId: selection.requirementId,
|
|
29
|
+
candidateId: selection.candidateId,
|
|
30
|
+
intent: selection.intent,
|
|
31
|
+
reason: selection.reason,
|
|
32
|
+
})),
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
if (analysis.extraction !== undefined) {
|
|
36
|
+
validateApiInputAnswerExtractionRecord({
|
|
37
|
+
...context,
|
|
38
|
+
record: analysis.extraction,
|
|
39
|
+
});
|
|
40
|
+
if (!isDeepStrictEqual(analysis.extraction.verification, resolved))
|
|
41
|
+
throw new Error('API input explanation differs from its code-resolved answer selection');
|
|
42
|
+
}
|
|
43
|
+
const status = !input.requirements.length
|
|
44
|
+
? 'no-pending-inputs'
|
|
45
|
+
: input.reply === ''
|
|
46
|
+
? 'empty-reply'
|
|
47
|
+
: 'extracted';
|
|
48
|
+
if (!isDeepStrictEqual(analysis, {
|
|
49
|
+
status,
|
|
50
|
+
...resolved,
|
|
51
|
+
...(analysis.extraction === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { extraction: analysis.extraction }),
|
|
54
|
+
}))
|
|
55
|
+
throw new Error('API input analysis differs from its original reply, contracts or code-resolved values');
|
|
56
|
+
}
|
package/dist/arguments/clarification/extraction/validate-api-input-answer-extraction-record.js
CHANGED
|
@@ -5,6 +5,10 @@ export function validateApiInputAnswerExtractionRecord(input) {
|
|
|
5
5
|
const expected = createApiInputAnswerExtractionRecord({
|
|
6
6
|
reply: input.reply,
|
|
7
7
|
requirements: input.requirements,
|
|
8
|
+
...(input.purpose === undefined ? {} : { purpose: input.purpose }),
|
|
9
|
+
...(input.selectOasEnums === undefined
|
|
10
|
+
? {}
|
|
11
|
+
: { selectOasEnums: input.selectOasEnums }),
|
|
8
12
|
decision: input.record.decision,
|
|
9
13
|
});
|
|
10
14
|
if (!isDeepStrictEqual(expected, input.record))
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiArgumentEnumContextInput } from '../../types/api-argument-enum.js';
|
|
2
|
+
import type { ApiInputAnswerContext } from '../../types/api-input-answer-extraction.js';
|
|
3
|
+
/** Derive real unanswered OAS enum questions, not a synthetic draft/edit plan. */
|
|
4
|
+
export declare function describeApiArgumentEnumContext(input: ApiArgumentEnumContextInput): ApiInputAnswerContext;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describeApiLiteralGeneration } from '../generation/describe-api-literal-generation.js';
|
|
2
|
+
import { inspectApiArgumentRequirements } from '../clarification/inspect-api-argument-requirements.js';
|
|
3
|
+
import { describeApiInputEnumAnswerChoices } from './describe-api-input-enum-answer-choices.js';
|
|
4
|
+
import { applyConfirmedApiInputs } from '../clarification/apply-confirmed-api-inputs.js';
|
|
5
|
+
import { resolveApiLiteralProposalSources } from '../sources/resolve-api-literal-proposal-sources.js';
|
|
6
|
+
import { expandRequestedEnumRequirements } from './expand-requested-enum-requirements.js';
|
|
7
|
+
/** Derive real unanswered OAS enum questions, not a synthetic draft/edit plan. */
|
|
8
|
+
export function describeApiArgumentEnumContext(input) {
|
|
9
|
+
const { input: snapshot, contract } = describeApiLiteralGeneration(input);
|
|
10
|
+
const proposal = contract.schema.parse(input.proposal);
|
|
11
|
+
resolveApiLiteralProposalSources({
|
|
12
|
+
callId: snapshot.callId,
|
|
13
|
+
values: proposal.values,
|
|
14
|
+
sources: input.literalSources,
|
|
15
|
+
messages: [{ id: 'scenario', text: snapshot.scenario }],
|
|
16
|
+
});
|
|
17
|
+
const values = applyConfirmedApiInputs({
|
|
18
|
+
...snapshot,
|
|
19
|
+
values: proposal.values,
|
|
20
|
+
});
|
|
21
|
+
const inspected = inspectApiArgumentRequirements({
|
|
22
|
+
...snapshot,
|
|
23
|
+
values,
|
|
24
|
+
requestedInputs: input.requestedInputs,
|
|
25
|
+
});
|
|
26
|
+
const context = {
|
|
27
|
+
reply: snapshot.scenario,
|
|
28
|
+
requirements: expandRequestedEnumRequirements({
|
|
29
|
+
...input,
|
|
30
|
+
values,
|
|
31
|
+
requirements: inspected.requirements,
|
|
32
|
+
}),
|
|
33
|
+
selectOasEnums: true,
|
|
34
|
+
...(snapshot.purpose === undefined ? {} : { purpose: snapshot.purpose }),
|
|
35
|
+
};
|
|
36
|
+
const ids = new Set(describeApiInputEnumAnswerChoices(context).map((choice) => choice.requirementId));
|
|
37
|
+
return {
|
|
38
|
+
...context,
|
|
39
|
+
requirements: context.requirements.filter((item) => ids.has(item.id)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiInputAnswerContext } from '../../types/api-input-answer-extraction.js';
|
|
2
|
+
import type { ApiInputEnumAnswerChoice } from '../../types/api-input-enum-answers.js';
|
|
3
|
+
/** Pending question contracts are the only value authority; defaults/examples are not candidates. */
|
|
4
|
+
export declare function describeApiInputEnumAnswerChoices(input: ApiInputAnswerContext): ApiInputEnumAnswerChoice[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { captureApiInputAnswerContext } from '../clarification/extraction/capture-api-input-answer-context.js';
|
|
2
|
+
import { createOasValueValidator } from '../../openapi/common/create-oas-value-validator.js';
|
|
3
|
+
import { createSchemaResourceContext } from '../../bindings/resources/create-schema-resource-context.js';
|
|
4
|
+
import { listApiInputEnumCandidates } from './list-api-input-enum-candidates.js';
|
|
5
|
+
import { apiInputRequirementDialect } from '../clarification/api-input-requirement-dialect.js';
|
|
6
|
+
/** Pending question contracts are the only value authority; defaults/examples are not candidates. */
|
|
7
|
+
export function describeApiInputEnumAnswerChoices(input) {
|
|
8
|
+
const context = captureApiInputAnswerContext(input);
|
|
9
|
+
if (context.selectOasEnums !== true)
|
|
10
|
+
return [];
|
|
11
|
+
return context.requirements.flatMap((requirement) => {
|
|
12
|
+
const normalized = createOasValueValidator(requirement.schema, apiInputRequirementDialect(requirement), `API enum answer ${requirement.id}`).schema;
|
|
13
|
+
const resources = createSchemaResourceContext(normalized);
|
|
14
|
+
const candidates = listApiInputEnumCandidates({
|
|
15
|
+
source: { kind: 'requirement', requirement },
|
|
16
|
+
schemas: [resources.reference(resources.root)],
|
|
17
|
+
resources,
|
|
18
|
+
});
|
|
19
|
+
return candidates.length
|
|
20
|
+
? [{ requirementId: requirement.id, candidates }]
|
|
21
|
+
: [];
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiArgumentEnumRequirementsInput } from '../../types/api-argument-enum.js';
|
|
2
|
+
import type { ApiInputRequirement } from '../../types/api-clarification.js';
|
|
3
|
+
/** Missing containers need no fabricated value to expose their explicitly requested OAS descendants. */
|
|
4
|
+
export declare function expandRequestedEnumRequirements(input: ApiArgumentEnumRequirementsInput): ApiInputRequirement[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { requestBindingSchemas } from '../../bindings/request-binding-schema.js';
|
|
3
|
+
import { unionBindingSchemas } from '../../bindings/combine-binding-schemas.js';
|
|
4
|
+
import { hasPointer } from '../../bindings/json-pointer.js';
|
|
5
|
+
/** Missing containers need no fabricated value to expose their explicitly requested OAS descendants. */
|
|
6
|
+
export function expandRequestedEnumRequirements(input) {
|
|
7
|
+
const requirements = structuredClone(input.requirements);
|
|
8
|
+
const owned = [
|
|
9
|
+
...input.bindings.map((binding) => binding.targetPointer),
|
|
10
|
+
...(input.confirmedInputs === undefined
|
|
11
|
+
? []
|
|
12
|
+
: input.confirmedInputs.map((value) => value.targetPointer)),
|
|
13
|
+
];
|
|
14
|
+
for (const parent of input.requirements) {
|
|
15
|
+
if (parent.requestedInputs === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
for (const targetPointer of parent.requestedInputs) {
|
|
18
|
+
if (requirements.some((item) => item.targetPointer === targetPointer) ||
|
|
19
|
+
hasPointer(input.values, targetPointer) ||
|
|
20
|
+
owned.some((pointer) => overlaps(pointer, targetPointer)))
|
|
21
|
+
continue;
|
|
22
|
+
const schemas = requestBindingSchemas(input, targetPointer);
|
|
23
|
+
if (!schemas.length)
|
|
24
|
+
throw new Error(`Requested enum input ${targetPointer} has no original OAS contract`);
|
|
25
|
+
const schema = unionBindingSchemas(schemas);
|
|
26
|
+
requirements.push({
|
|
27
|
+
...parent,
|
|
28
|
+
kind: 'requested-input',
|
|
29
|
+
id: createHash('sha256')
|
|
30
|
+
.update(JSON.stringify([parent.id, targetPointer, schema]))
|
|
31
|
+
.digest('hex'),
|
|
32
|
+
targetPointer,
|
|
33
|
+
schema,
|
|
34
|
+
requestedInputs: [targetPointer],
|
|
35
|
+
message: `Provide scenario-requested value ${targetPointer} for ${input.operation.method} ${input.operation.path}.`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return requirements;
|
|
40
|
+
}
|
|
41
|
+
function overlaps(left, right) {
|
|
42
|
+
return (left === right ||
|
|
43
|
+
left.startsWith(right + '/') ||
|
|
44
|
+
right.startsWith(left + '/'));
|
|
45
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ApiInputEnumCandidatesInput, ApiInputEnumCandidate } from '../../types/api-input-enum.js';
|
|
2
|
+
/** Enumerate declared values only. A source resource retains references and all sibling assertions. */
|
|
3
|
+
export declare function listApiInputEnumCandidates(input: ApiInputEnumCandidatesInput): ApiInputEnumCandidate[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createOasValueValidator } from '../../openapi/common/create-oas-value-validator.js';
|
|
3
|
+
/** Enumerate declared values only. A source resource retains references and all sibling assertions. */
|
|
4
|
+
export function listApiInputEnumCandidates(input) {
|
|
5
|
+
const material = input.source.kind === 'operation' ? input.source : input.source.requirement;
|
|
6
|
+
const operation = input.source.kind === 'operation'
|
|
7
|
+
? input.source.operation.key
|
|
8
|
+
: input.source.requirement.operation;
|
|
9
|
+
if (!input.schemas.length)
|
|
10
|
+
throw new Error(`API enum ${material.callId}${material.targetPointer} has no original OAS target schema`);
|
|
11
|
+
const schema = input.schemas.length === 1 ? input.schemas[0] : { anyOf: input.schemas };
|
|
12
|
+
const validator = createOasValueValidator(input.resources.bundle(schema), 'draft-2020-12', `API enum ${material.callId}${material.targetPointer}`);
|
|
13
|
+
const identity = input.source.kind === 'operation'
|
|
14
|
+
? JSON.stringify([
|
|
15
|
+
input.source.callId,
|
|
16
|
+
input.source.operation,
|
|
17
|
+
input.source.requestMediaType,
|
|
18
|
+
input.source.targetPointer,
|
|
19
|
+
])
|
|
20
|
+
: JSON.stringify(['requirement', input.source.requirement]);
|
|
21
|
+
const candidates = [];
|
|
22
|
+
const active = new Set();
|
|
23
|
+
function visit(current, pointer) {
|
|
24
|
+
if (typeof current === 'boolean' || active.has(current))
|
|
25
|
+
return;
|
|
26
|
+
active.add(current);
|
|
27
|
+
try {
|
|
28
|
+
if (Array.isArray(current.enum))
|
|
29
|
+
current.enum.forEach((value, index) => {
|
|
30
|
+
if (!validator.validate(value).valid)
|
|
31
|
+
return;
|
|
32
|
+
const schemaPointer = `${pointer}/enum/${index}`;
|
|
33
|
+
candidates.push({
|
|
34
|
+
id: createHash('sha256')
|
|
35
|
+
.update(identity)
|
|
36
|
+
.update(JSON.stringify([schemaPointer, value]))
|
|
37
|
+
.digest('hex'),
|
|
38
|
+
callId: material.callId,
|
|
39
|
+
operation: structuredClone(operation),
|
|
40
|
+
targetPointer: material.targetPointer,
|
|
41
|
+
schemaPointer,
|
|
42
|
+
value: structuredClone(value),
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
if (typeof current.$ref === 'string') {
|
|
46
|
+
const resolved = input.resources.resolve(current);
|
|
47
|
+
if (typeof resolved !== 'boolean') {
|
|
48
|
+
const reference = input.resources.reference(resolved);
|
|
49
|
+
if (typeof reference === 'boolean' ||
|
|
50
|
+
typeof reference.$ref !== 'string')
|
|
51
|
+
throw new Error(`API enum ${material.callId}${material.targetPointer} has no original schema reference`);
|
|
52
|
+
visit(resolved, decodeURIComponent(new URL(reference.$ref).hash.slice(1)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const key of ['allOf', 'anyOf', 'oneOf']) {
|
|
56
|
+
const children = current[key];
|
|
57
|
+
if (Array.isArray(children))
|
|
58
|
+
children.forEach((child, index) => {
|
|
59
|
+
if (typeof child === 'boolean' ||
|
|
60
|
+
(child !== null &&
|
|
61
|
+
typeof child === 'object' &&
|
|
62
|
+
!Array.isArray(child)))
|
|
63
|
+
visit(child, `${pointer}/${key}/${index}`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
active.delete(current);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
input.schemas.forEach((item, index) => visit(item, `/${index}`));
|
|
72
|
+
return candidates.filter((candidate, index) => candidates.findIndex((item) => item.id === candidate.id) === index);
|
|
73
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ResolveApiArgumentEnumInterpretationInput, ApiArgumentEnumResolution } from '../../types/api-argument-enum.js';
|
|
2
|
+
/** Replay copied OAS values against independently supplied input; keep semantic evidence separate. */
|
|
3
|
+
export declare function resolveApiArgumentEnumInterpretation(input: ResolveApiArgumentEnumInterpretationInput): ApiArgumentEnumResolution;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describeApiArgumentEnumContext } from './describe-api-argument-enum-context.js';
|
|
2
|
+
import { validateApiInputAnswerAnalysis } from '../clarification/extraction/validate-api-input-answer-analysis.js';
|
|
3
|
+
import { assignApiInputValue } from '../clarification/assign-api-input-value.js';
|
|
4
|
+
import { inspectApiArgumentRequirements } from '../clarification/inspect-api-argument-requirements.js';
|
|
5
|
+
import { applyConfirmedApiInputs } from '../clarification/apply-confirmed-api-inputs.js';
|
|
6
|
+
/** Replay copied OAS values against independently supplied input; keep semantic evidence separate. */
|
|
7
|
+
export function resolveApiArgumentEnumInterpretation(input) {
|
|
8
|
+
const context = describeApiArgumentEnumContext(input);
|
|
9
|
+
validateApiInputAnswerAnalysis({ ...context, analysis: input.analysis });
|
|
10
|
+
const values = applyConfirmedApiInputs({
|
|
11
|
+
...input,
|
|
12
|
+
values: input.proposal.values,
|
|
13
|
+
});
|
|
14
|
+
for (const answer of input.analysis.answers) {
|
|
15
|
+
const requirement = context.requirements.find((item) => item.id === answer.requirementId);
|
|
16
|
+
if (!requirement)
|
|
17
|
+
throw new Error(`API enum answer ${answer.requirementId} has no current OAS requirement`);
|
|
18
|
+
assignApiInputValue(values, requirement.targetPointer, structuredClone(answer.value), input);
|
|
19
|
+
}
|
|
20
|
+
const inspected = inspectApiArgumentRequirements({ ...input, values });
|
|
21
|
+
return {
|
|
22
|
+
values,
|
|
23
|
+
unresolvedInputs: inspected.requirements.map((item) => item.targetPointer),
|
|
24
|
+
interpretation: structuredClone({ ...context, analysis: input.analysis }),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ResolveApiLiteralProposalSourcesInput, VerifiedApiLiteralProposalSource } from '../../types/api-input-literal-source.js';
|
|
2
|
+
/** The same source/coverage checks serve independent generation and draft editing. */
|
|
3
|
+
export declare function resolveApiLiteralProposalSources(input: ResolveApiLiteralProposalSourcesInput): VerifiedApiLiteralProposalSource[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { pointerTokens, pointerValue } from '../../bindings/json-pointer.js';
|
|
2
|
+
import { verifyApiInputLiteralSource } from './verify-api-input-literal-source.js';
|
|
3
|
+
/** The same source/coverage checks serve independent generation and draft editing. */
|
|
4
|
+
export function resolveApiLiteralProposalSources(input) {
|
|
5
|
+
if (!input.callId?.trim() ||
|
|
6
|
+
!Array.isArray(input.sources) ||
|
|
7
|
+
!Array.isArray(input.messages))
|
|
8
|
+
throw new Error('Literal sources require callId, sources and original messages');
|
|
9
|
+
const sources = [];
|
|
10
|
+
for (const selection of input.sources) {
|
|
11
|
+
pointerTokens(selection.targetPointer);
|
|
12
|
+
const label = `API ${input.callId} literal ${selection.targetPointer}`;
|
|
13
|
+
if (sources.some((source) => overlaps(source.targetPointer, selection.targetPointer)))
|
|
14
|
+
throw new Error(`${label} has duplicate or overlapping literalSources`);
|
|
15
|
+
const message = input.messages.find((entry) => entry.id === selection.messageId);
|
|
16
|
+
if (!message)
|
|
17
|
+
throw new Error(`${label} cites unavailable user message ${selection.messageId}`);
|
|
18
|
+
const proposed = pointerValue(input.values, selection.targetPointer);
|
|
19
|
+
if (proposed === undefined)
|
|
20
|
+
throw new Error(`${label} source targets an absent proposed value`);
|
|
21
|
+
const parsed = verifyApiInputLiteralSource({
|
|
22
|
+
...selection,
|
|
23
|
+
text: message.text,
|
|
24
|
+
label,
|
|
25
|
+
value: proposed,
|
|
26
|
+
});
|
|
27
|
+
sources.push({ ...selection, occurrences: parsed.occurrences });
|
|
28
|
+
}
|
|
29
|
+
function check(value, pointer) {
|
|
30
|
+
if (sources.some((source) => isAncestor(source.targetPointer, pointer)))
|
|
31
|
+
return;
|
|
32
|
+
if (value !== null &&
|
|
33
|
+
typeof value === 'object' &&
|
|
34
|
+
Object.keys(value).length) {
|
|
35
|
+
for (const [key, child] of Object.entries(value))
|
|
36
|
+
check(child, `${pointer}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`API ${input.callId} literal ${pointer} requires an exact user source in literalSources`);
|
|
40
|
+
}
|
|
41
|
+
for (const [root, value] of Object.entries(input.values)) {
|
|
42
|
+
if (root !== 'body' &&
|
|
43
|
+
value !== null &&
|
|
44
|
+
typeof value === 'object' &&
|
|
45
|
+
!Object.keys(value).length)
|
|
46
|
+
continue;
|
|
47
|
+
check(value, `/${root}`);
|
|
48
|
+
}
|
|
49
|
+
return sources;
|
|
50
|
+
}
|
|
51
|
+
function isAncestor(ancestor, pointer) {
|
|
52
|
+
return (ancestor === '' ||
|
|
53
|
+
pointer === ancestor ||
|
|
54
|
+
pointer.startsWith(`${ancestor}/`));
|
|
55
|
+
}
|
|
56
|
+
function overlaps(left, right) {
|
|
57
|
+
return isAncestor(left, right) || isAncestor(right, left);
|
|
58
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WorkflowDraftApiEnumChoicesInput, WorkflowDraftApiEnumChoicesDescription } from '../../../types/workflow-draft-api-enum-selection.js';
|
|
2
|
+
/** Only unresolved original-OAS inputs. Defaults/examples never become choices. */
|
|
3
|
+
export declare function describeWorkflowDraftApiEnumChoices(input: WorkflowDraftApiEnumChoicesInput): WorkflowDraftApiEnumChoicesDescription;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { resolveApiCallArgumentsContext } from './resolve-api-call-arguments-context.js';
|
|
2
|
+
import { applyConfirmedApiInputs } from '../../../arguments/clarification/apply-confirmed-api-inputs.js';
|
|
3
|
+
import { inspectApiArgumentRequirements } from '../../../arguments/clarification/inspect-api-argument-requirements.js';
|
|
4
|
+
import { schemasAtPointer } from '../../../bindings/schema-at-pointer.js';
|
|
5
|
+
import { hasPointer } from '../../../bindings/json-pointer.js';
|
|
6
|
+
import { createSchemaResourceContext } from '../../../bindings/resources/create-schema-resource-context.js';
|
|
7
|
+
import { listApiInputEnumCandidates } from '../../../arguments/enum/list-api-input-enum-candidates.js';
|
|
8
|
+
/** Only unresolved original-OAS inputs. Defaults/examples never become choices. */
|
|
9
|
+
export function describeWorkflowDraftApiEnumChoices(input) {
|
|
10
|
+
const { description, contract } = resolveApiCallArgumentsContext(input);
|
|
11
|
+
const values = applyConfirmedApiInputs({
|
|
12
|
+
callId: description.nodeId,
|
|
13
|
+
operation: description.operation,
|
|
14
|
+
requestMediaType: description.requestMediaType,
|
|
15
|
+
bindings: input.bindingPlan.bindings,
|
|
16
|
+
confirmedInputs: input.confirmedInputs,
|
|
17
|
+
values: contract.valuesSchema.parse(input.proposal.values),
|
|
18
|
+
});
|
|
19
|
+
const inspected = inspectApiArgumentRequirements({
|
|
20
|
+
callId: description.nodeId,
|
|
21
|
+
operation: description.operation,
|
|
22
|
+
requestMediaType: description.requestMediaType,
|
|
23
|
+
bindings: input.bindingPlan.bindings,
|
|
24
|
+
values,
|
|
25
|
+
requestedInputs: input.proposal.requestedInputs,
|
|
26
|
+
});
|
|
27
|
+
const targets = new Set(inspected.requirements.flatMap((requirement) => [
|
|
28
|
+
requirement.targetPointer,
|
|
29
|
+
...(requirement.requestedInputs ?? []),
|
|
30
|
+
]));
|
|
31
|
+
const candidates = [];
|
|
32
|
+
const choiceTargets = [];
|
|
33
|
+
const resources = createSchemaResourceContext(description.requestSchema);
|
|
34
|
+
for (const targetPointer of targets) {
|
|
35
|
+
if (hasPointer(values, targetPointer) ||
|
|
36
|
+
input.bindingPlan.bindings.some((binding) => overlaps(binding.targetPointer, targetPointer)) ||
|
|
37
|
+
(input.confirmedInputs ?? []).some((item) => overlaps(item.targetPointer, targetPointer)))
|
|
38
|
+
continue;
|
|
39
|
+
const schemas = schemasAtPointer(resources.root, targetPointer, resources);
|
|
40
|
+
const selected = listApiInputEnumCandidates({
|
|
41
|
+
source: {
|
|
42
|
+
kind: 'operation',
|
|
43
|
+
callId: description.nodeId,
|
|
44
|
+
operation: description.operation,
|
|
45
|
+
requestMediaType: description.requestMediaType,
|
|
46
|
+
targetPointer,
|
|
47
|
+
},
|
|
48
|
+
schemas,
|
|
49
|
+
resources,
|
|
50
|
+
});
|
|
51
|
+
if (selected.length) {
|
|
52
|
+
candidates.push(...selected);
|
|
53
|
+
const requirement = inspected.requirements.find((item) => item.targetPointer === targetPointer ||
|
|
54
|
+
item.requestedInputs?.includes(targetPointer));
|
|
55
|
+
choiceTargets.push({
|
|
56
|
+
targetPointer,
|
|
57
|
+
schema: resources.bundle(schemas.length === 1 ? schemas[0] : { anyOf: schemas }),
|
|
58
|
+
...(requirement?.parameterDescription === undefined
|
|
59
|
+
? {}
|
|
60
|
+
: { description: requirement.parameterDescription }),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { candidates, targets: choiceTargets };
|
|
65
|
+
}
|
|
66
|
+
function overlaps(left, right) {
|
|
67
|
+
return (left === right ||
|
|
68
|
+
left.startsWith(right + '/') ||
|
|
69
|
+
right.startsWith(left + '/'));
|
|
70
|
+
}
|