@contractspec/lib.evolution 0.0.0-canary-20260113162409
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/ai-agent/src/approval/index.d.mts +1 -0
- package/dist/ai-agent/src/approval/workflow.d.mts +136 -0
- package/dist/ai-agent/src/approval/workflow.d.mts.map +1 -0
- package/dist/ai-agent/src/types.d.mts +30 -0
- package/dist/ai-agent/src/types.d.mts.map +1 -0
- package/dist/analyzer/spec-analyzer.d.mts +35 -0
- package/dist/analyzer/spec-analyzer.d.mts.map +1 -0
- package/dist/analyzer/spec-analyzer.mjs +305 -0
- package/dist/analyzer/spec-analyzer.mjs.map +1 -0
- package/dist/approval/integration.d.mts +44 -0
- package/dist/approval/integration.d.mts.map +1 -0
- package/dist/approval/integration.mjs +126 -0
- package/dist/approval/integration.mjs.map +1 -0
- package/dist/generator/ai-spec-generator.d.mts +56 -0
- package/dist/generator/ai-spec-generator.d.mts.map +1 -0
- package/dist/generator/ai-spec-generator.mjs +186 -0
- package/dist/generator/ai-spec-generator.mjs.map +1 -0
- package/dist/generator/spec-generator.d.mts +46 -0
- package/dist/generator/spec-generator.d.mts.map +1 -0
- package/dist/generator/spec-generator.mjs +121 -0
- package/dist/generator/spec-generator.mjs.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +6 -0
- package/dist/types.d.mts +132 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SpecSuggestion, SpecSuggestionFilters, SpecSuggestionRepository, SpecSuggestionWriter, SuggestionStatus } from "../types.mjs";
|
|
2
|
+
import { AgentSessionState } from "../ai-agent/src/types.mjs";
|
|
3
|
+
import { ApprovalWorkflow } from "../ai-agent/src/approval/workflow.mjs";
|
|
4
|
+
import "../ai-agent/src/approval/index.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/approval/integration.d.ts
|
|
7
|
+
interface SpecSuggestionOrchestratorOptions {
|
|
8
|
+
repository: SpecSuggestionRepository;
|
|
9
|
+
approval?: ApprovalWorkflow;
|
|
10
|
+
writer?: SpecSuggestionWriter;
|
|
11
|
+
}
|
|
12
|
+
declare class SpecSuggestionOrchestrator {
|
|
13
|
+
private readonly options;
|
|
14
|
+
constructor(options: SpecSuggestionOrchestratorOptions);
|
|
15
|
+
submit(suggestion: SpecSuggestion, session?: AgentSessionState, approvalReason?: string): Promise<SpecSuggestion>;
|
|
16
|
+
approve(id: string, reviewer: string, notes?: string): Promise<void>;
|
|
17
|
+
reject(id: string, reviewer: string, notes?: string): Promise<void>;
|
|
18
|
+
list(filters?: SpecSuggestionFilters): Promise<SpecSuggestion[]>;
|
|
19
|
+
private ensureSuggestion;
|
|
20
|
+
}
|
|
21
|
+
interface FileSystemSuggestionWriterOptions {
|
|
22
|
+
outputDir?: string;
|
|
23
|
+
filenameTemplate?: (suggestion: SpecSuggestion) => string;
|
|
24
|
+
}
|
|
25
|
+
declare class FileSystemSuggestionWriter implements SpecSuggestionWriter {
|
|
26
|
+
private readonly outputDir;
|
|
27
|
+
private readonly filenameTemplate;
|
|
28
|
+
constructor(options?: FileSystemSuggestionWriterOptions);
|
|
29
|
+
write(suggestion: SpecSuggestion): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
declare class InMemorySpecSuggestionRepository implements SpecSuggestionRepository {
|
|
32
|
+
private readonly items;
|
|
33
|
+
create(suggestion: SpecSuggestion): Promise<void>;
|
|
34
|
+
getById(id: string): Promise<SpecSuggestion | undefined>;
|
|
35
|
+
updateStatus(id: string, status: SuggestionStatus, metadata?: {
|
|
36
|
+
reviewer?: string;
|
|
37
|
+
notes?: string;
|
|
38
|
+
decidedAt?: Date;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
list(filters?: SpecSuggestionFilters): Promise<SpecSuggestion[]>;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { FileSystemSuggestionWriter, FileSystemSuggestionWriterOptions, InMemorySpecSuggestionRepository, SpecSuggestionOrchestrator, SpecSuggestionOrchestratorOptions };
|
|
44
|
+
//# sourceMappingURL=integration.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.d.mts","names":[],"sources":["../../src/approval/integration.ts"],"sourcesContent":[],"mappings":";;;;;;UAYiB,iCAAA;cACH;aACD;EAFI,MAAA,CAAA,EAGN,oBAHM;;AAEJ,cAIA,0BAAA,CAJA;EACF,iBAAA,OAAA;EAAoB,WAAA,CAAA,OAAA,EAIS,iCAJT;EAGlB,MAAA,CAAA,UAAA,EAIG,cAJuB,EAAA,OAAA,CAAA,EAKzB,iBALyB,EAAA,cAAA,CAAA,EAAA,MAAA,CAAA,EAMZ,OANY,CAMZ,cANY,CAAA;EACC,OAAA,CAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAwBoB,OAxBpB,CAAA,IAAA,CAAA;EAGxB,MAAA,CAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EA0C2C,OA1C3C,CAAA,IAAA,CAAA;EACF,IAAA,CAAA,OAAA,CAAA,EAiDG,qBAjDH,CAAA,EAiDwB,OAjDxB,CAiDwB,cAjDxB,EAAA,CAAA;EACa,QAAA,gBAAA;;AAmBiC,UAwC3C,iCAAA,CAxC2C;EAqBD,SAAA,CAAA,EAAA,MAAA;EAQ1C,gBAAA,CAAA,EAAA,CAAA,UAAA,EAaiB,cAbjB,EAAA,GAAA,MAAA;;AAAqB,cAgBzB,0BAAA,YAAsC,oBAhBb,CAAA;EAAA,iBAAA,SAAA;EAWrB,iBAAA,gBAAA;EAKJ,WAAA,CAAA,OAA2B,CAA3B,EAMU,iCANiB;EAMjB,KAAA,CAAA,UAAA,EAUG,cAVH,CAAA,EAUoB,OAVpB,CAAA,MAAA,CAAA;;AAUoB,cAU9B,gCAAA,YAA4C,wBAVd,CAAA;EAhBQ,iBAAA,KAAA;EAAoB,MAAA,CAAA,UAAA,EA6B5C,cA7B4C,CAAA,EA6B3B,OA7B2B,CAAA,IAAA,CAAA;EA0B1D,OAAA,CAAA,EAAA,EAAA,MAAA,CAAA,EAOgB,OAPhB,CAOwB,cAPS,GAAA,SAAA,CAAA;EAGnB,YAAA,CAAA,EAAA,EAAA,MAAA,EAAA,MAAA,EAUf,gBAVe,EAAA,QAUf,CAVe,EAAA;IAAiB,QAAA,CAAA,EAAA,MAAA;IAIP,KAAA,CAAA,EAAA,MAAA;IAAR,SAAA,CAAA,EAOmC,IAPnC;EAMjB,CAAA,CAAA,EAEP,OAFO,CAAA,IAAA,CAAA;EACoD,IAAA,CAAA,OAAA,CAAA,EAgBzC,qBAhByC,CAAA,EAgBpB,OAhBoB,CAgBpB,cAhBoB,EAAA,CAAA"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import "@contractspec/lib.ai-agent/approval";
|
|
4
|
+
|
|
5
|
+
//#region src/approval/integration.ts
|
|
6
|
+
var SpecSuggestionOrchestrator = class {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
async submit(suggestion, session, approvalReason) {
|
|
11
|
+
await this.options.repository.create(suggestion);
|
|
12
|
+
if (session && this.options.approval) await this.options.approval.requestApproval({
|
|
13
|
+
sessionId: session.sessionId,
|
|
14
|
+
agentId: session.agentId,
|
|
15
|
+
tenantId: session.tenantId,
|
|
16
|
+
toolName: "evolution.apply_suggestion",
|
|
17
|
+
toolCallId: suggestion.id,
|
|
18
|
+
toolArgs: { suggestionId: suggestion.id },
|
|
19
|
+
reason: approvalReason ?? suggestion.proposal.summary,
|
|
20
|
+
payload: { suggestionId: suggestion.id }
|
|
21
|
+
});
|
|
22
|
+
return suggestion;
|
|
23
|
+
}
|
|
24
|
+
async approve(id, reviewer, notes) {
|
|
25
|
+
const suggestion = await this.ensureSuggestion(id);
|
|
26
|
+
await this.options.repository.updateStatus(id, "approved", {
|
|
27
|
+
reviewer,
|
|
28
|
+
notes,
|
|
29
|
+
decidedAt: /* @__PURE__ */ new Date()
|
|
30
|
+
});
|
|
31
|
+
if (this.options.writer) await this.options.writer.write({
|
|
32
|
+
...suggestion,
|
|
33
|
+
status: "approved",
|
|
34
|
+
approvals: {
|
|
35
|
+
reviewer,
|
|
36
|
+
notes,
|
|
37
|
+
decidedAt: /* @__PURE__ */ new Date(),
|
|
38
|
+
status: "approved"
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async reject(id, reviewer, notes) {
|
|
43
|
+
await this.options.repository.updateStatus(id, "rejected", {
|
|
44
|
+
reviewer,
|
|
45
|
+
notes,
|
|
46
|
+
decidedAt: /* @__PURE__ */ new Date()
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
list(filters) {
|
|
50
|
+
return this.options.repository.list(filters);
|
|
51
|
+
}
|
|
52
|
+
async ensureSuggestion(id) {
|
|
53
|
+
const suggestion = await this.options.repository.getById(id);
|
|
54
|
+
if (!suggestion) throw new Error(`Spec suggestion ${id} not found`);
|
|
55
|
+
return suggestion;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var FileSystemSuggestionWriter = class {
|
|
59
|
+
outputDir;
|
|
60
|
+
filenameTemplate;
|
|
61
|
+
constructor(options = {}) {
|
|
62
|
+
this.outputDir = options.outputDir ?? join(process.cwd(), "packages/libs/contracts/src/generated");
|
|
63
|
+
this.filenameTemplate = options.filenameTemplate ?? ((suggestion) => `${suggestion.target?.key ?? suggestion.intent.id}.v${suggestion.target?.version ?? "next"}.suggestion.json`);
|
|
64
|
+
}
|
|
65
|
+
async write(suggestion) {
|
|
66
|
+
await mkdir(this.outputDir, { recursive: true });
|
|
67
|
+
const filename = this.filenameTemplate(suggestion);
|
|
68
|
+
const filepath = join(this.outputDir, filename);
|
|
69
|
+
const payload = serializeSuggestion(suggestion);
|
|
70
|
+
await writeFile(filepath, JSON.stringify(payload, null, 2));
|
|
71
|
+
return filepath;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var InMemorySpecSuggestionRepository = class {
|
|
75
|
+
items = /* @__PURE__ */ new Map();
|
|
76
|
+
async create(suggestion) {
|
|
77
|
+
this.items.set(suggestion.id, suggestion);
|
|
78
|
+
}
|
|
79
|
+
async getById(id) {
|
|
80
|
+
return this.items.get(id);
|
|
81
|
+
}
|
|
82
|
+
async updateStatus(id, status, metadata) {
|
|
83
|
+
const suggestion = await this.getById(id);
|
|
84
|
+
if (!suggestion) return;
|
|
85
|
+
this.items.set(id, {
|
|
86
|
+
...suggestion,
|
|
87
|
+
status,
|
|
88
|
+
approvals: {
|
|
89
|
+
reviewer: metadata?.reviewer,
|
|
90
|
+
notes: metadata?.notes,
|
|
91
|
+
decidedAt: metadata?.decidedAt,
|
|
92
|
+
status
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async list(filters) {
|
|
97
|
+
const values = [...this.items.values()];
|
|
98
|
+
if (!filters) return values;
|
|
99
|
+
return values.filter((item) => {
|
|
100
|
+
if (filters.status && item.status !== filters.status) return false;
|
|
101
|
+
if (filters.operationKey && item.target?.key !== filters.operationKey) return false;
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function serializeSuggestion(suggestion) {
|
|
107
|
+
const { proposal, ...rest } = suggestion;
|
|
108
|
+
const { spec, ...proposalRest } = proposal;
|
|
109
|
+
return {
|
|
110
|
+
...rest,
|
|
111
|
+
proposal: {
|
|
112
|
+
...proposalRest,
|
|
113
|
+
specMeta: spec?.meta
|
|
114
|
+
},
|
|
115
|
+
createdAt: suggestion.createdAt.toISOString(),
|
|
116
|
+
intent: {
|
|
117
|
+
...suggestion.intent,
|
|
118
|
+
confidence: { ...suggestion.intent.confidence },
|
|
119
|
+
evidence: suggestion.intent.evidence
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
export { FileSystemSuggestionWriter, InMemorySpecSuggestionRepository, SpecSuggestionOrchestrator };
|
|
126
|
+
//# sourceMappingURL=integration.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.mjs","names":[],"sources":["../../src/approval/integration.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { ApprovalWorkflow } from '@contractspec/lib.ai-agent/approval';\nimport type { AgentSessionState } from '@contractspec/lib.ai-agent/types';\nimport {\n type SpecSuggestion,\n type SpecSuggestionFilters,\n type SpecSuggestionRepository,\n type SpecSuggestionWriter,\n type SuggestionStatus,\n} from '../types';\n\nexport interface SpecSuggestionOrchestratorOptions {\n repository: SpecSuggestionRepository;\n approval?: ApprovalWorkflow;\n writer?: SpecSuggestionWriter;\n}\n\nexport class SpecSuggestionOrchestrator {\n constructor(private readonly options: SpecSuggestionOrchestratorOptions) {}\n\n async submit(\n suggestion: SpecSuggestion,\n session?: AgentSessionState,\n approvalReason?: string\n ) {\n await this.options.repository.create(suggestion);\n if (session && this.options.approval) {\n // AI SDK v6: requestApproval takes a single params object\n await this.options.approval.requestApproval({\n sessionId: session.sessionId,\n agentId: session.agentId,\n tenantId: session.tenantId,\n toolName: 'evolution.apply_suggestion',\n toolCallId: suggestion.id,\n toolArgs: { suggestionId: suggestion.id },\n reason: approvalReason ?? suggestion.proposal.summary,\n payload: { suggestionId: suggestion.id },\n });\n }\n return suggestion;\n }\n\n async approve(id: string, reviewer: string, notes?: string) {\n const suggestion = await this.ensureSuggestion(id);\n await this.options.repository.updateStatus(id, 'approved', {\n reviewer,\n notes,\n decidedAt: new Date(),\n });\n if (this.options.writer) {\n await this.options.writer.write({\n ...suggestion,\n status: 'approved',\n approvals: {\n reviewer,\n notes,\n decidedAt: new Date(),\n status: 'approved',\n },\n });\n }\n }\n\n async reject(id: string, reviewer: string, notes?: string) {\n await this.options.repository.updateStatus(id, 'rejected', {\n reviewer,\n notes,\n decidedAt: new Date(),\n });\n }\n\n list(filters?: SpecSuggestionFilters) {\n return this.options.repository.list(filters);\n }\n\n private async ensureSuggestion(id: string) {\n const suggestion = await this.options.repository.getById(id);\n if (!suggestion) throw new Error(`Spec suggestion ${id} not found`);\n return suggestion;\n }\n}\n\nexport interface FileSystemSuggestionWriterOptions {\n outputDir?: string;\n filenameTemplate?: (suggestion: SpecSuggestion) => string;\n}\n\nexport class FileSystemSuggestionWriter implements SpecSuggestionWriter {\n private readonly outputDir: string;\n private readonly filenameTemplate: NonNullable<\n FileSystemSuggestionWriterOptions['filenameTemplate']\n >;\n\n constructor(options: FileSystemSuggestionWriterOptions = {}) {\n this.outputDir =\n options.outputDir ??\n join(process.cwd(), 'packages/libs/contracts/src/generated');\n this.filenameTemplate =\n options.filenameTemplate ??\n ((suggestion) =>\n `${suggestion.target?.key ?? suggestion.intent.id}.v${suggestion.target?.version ?? 'next'}.suggestion.json`);\n }\n\n async write(suggestion: SpecSuggestion): Promise<string> {\n await mkdir(this.outputDir, { recursive: true });\n const filename = this.filenameTemplate(suggestion);\n const filepath = join(this.outputDir, filename);\n const payload = serializeSuggestion(suggestion);\n await writeFile(filepath, JSON.stringify(payload, null, 2));\n return filepath;\n }\n}\n\nexport class InMemorySpecSuggestionRepository implements SpecSuggestionRepository {\n private readonly items = new Map<string, SpecSuggestion>();\n\n async create(suggestion: SpecSuggestion): Promise<void> {\n this.items.set(suggestion.id, suggestion);\n }\n\n async getById(id: string): Promise<SpecSuggestion | undefined> {\n return this.items.get(id);\n }\n\n async updateStatus(\n id: string,\n status: SuggestionStatus,\n metadata?: { reviewer?: string; notes?: string; decidedAt?: Date }\n ): Promise<void> {\n const suggestion = await this.getById(id);\n if (!suggestion) return;\n this.items.set(id, {\n ...suggestion,\n status,\n approvals: {\n reviewer: metadata?.reviewer,\n notes: metadata?.notes,\n decidedAt: metadata?.decidedAt,\n status,\n },\n });\n }\n\n async list(filters?: SpecSuggestionFilters) {\n const values = [...this.items.values()];\n if (!filters) return values;\n return values.filter((item) => {\n if (filters.status && item.status !== filters.status) return false;\n if (filters.operationKey && item.target?.key !== filters.operationKey)\n return false;\n return true;\n });\n }\n}\n\nfunction serializeSuggestion(suggestion: SpecSuggestion) {\n const { proposal, ...rest } = suggestion;\n const { spec, ...proposalRest } = proposal;\n return {\n ...rest,\n proposal: {\n ...proposalRest,\n specMeta: spec?.meta,\n },\n createdAt: suggestion.createdAt.toISOString(),\n intent: {\n ...suggestion.intent,\n confidence: { ...suggestion.intent.confidence },\n evidence: suggestion.intent.evidence,\n },\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction deserializeSuggestion(payload: unknown): SpecSuggestion | undefined {\n if (!payload || typeof payload !== 'object') return undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const raw = payload as Record<string, any>;\n return {\n ...raw,\n createdAt: raw.createdAt ? new Date(raw.createdAt) : new Date(),\n intent: {\n ...raw.intent,\n confidence: raw.intent?.confidence,\n evidence: raw.intent?.evidence ?? [],\n },\n proposal: {\n ...raw.proposal,\n metadata: {\n ...(raw.proposal?.metadata ?? {}),\n specMeta: raw.proposal?.specMeta,\n },\n },\n } as SpecSuggestion;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { EvolutionConfig, IntentPattern, SpecSuggestion } from "../types.mjs";
|
|
2
|
+
import { LanguageModel } from "ai";
|
|
3
|
+
|
|
4
|
+
//#region src/generator/ai-spec-generator.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for the AI-powered spec generator.
|
|
8
|
+
*/
|
|
9
|
+
interface AISpecGeneratorConfig {
|
|
10
|
+
/** AI SDK language model */
|
|
11
|
+
model: LanguageModel;
|
|
12
|
+
/** Evolution configuration */
|
|
13
|
+
evolutionConfig?: EvolutionConfig;
|
|
14
|
+
/** Custom system prompt */
|
|
15
|
+
systemPrompt?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* AI-powered spec generator using AI SDK v6.
|
|
19
|
+
*
|
|
20
|
+
* Uses structured output (Output.object) to generate
|
|
21
|
+
* well-formed spec suggestions from intent patterns.
|
|
22
|
+
*/
|
|
23
|
+
declare class AISpecGenerator {
|
|
24
|
+
private readonly model;
|
|
25
|
+
private readonly config;
|
|
26
|
+
private readonly systemPrompt;
|
|
27
|
+
constructor(options: AISpecGeneratorConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Generate a spec suggestion from an intent pattern using AI.
|
|
30
|
+
*/
|
|
31
|
+
generateFromIntent(intent: IntentPattern, options?: {
|
|
32
|
+
additionalContext?: string;
|
|
33
|
+
existingSpec?: Record<string, unknown>;
|
|
34
|
+
}): Promise<SpecSuggestion>;
|
|
35
|
+
/**
|
|
36
|
+
* Generate multiple suggestions for a batch of intents.
|
|
37
|
+
*/
|
|
38
|
+
generateBatch(intents: IntentPattern[], options?: {
|
|
39
|
+
maxConcurrent?: number;
|
|
40
|
+
}): Promise<SpecSuggestion[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Validate and enhance an existing suggestion using AI.
|
|
43
|
+
*/
|
|
44
|
+
enhanceSuggestion(suggestion: SpecSuggestion): Promise<SpecSuggestion>;
|
|
45
|
+
private buildPrompt;
|
|
46
|
+
private buildSuggestion;
|
|
47
|
+
private calculatePriority;
|
|
48
|
+
private determineInitialStatus;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create an AI-powered spec generator.
|
|
52
|
+
*/
|
|
53
|
+
declare function createAISpecGenerator(config: AISpecGeneratorConfig): AISpecGenerator;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { AISpecGenerator, AISpecGeneratorConfig, createAISpecGenerator };
|
|
56
|
+
//# sourceMappingURL=ai-spec-generator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-spec-generator.d.mts","names":[],"sources":["../../src/generator/ai-spec-generator.ts"],"sourcesContent":[],"mappings":";;;;;;;AA2CA;AAea,UAfI,qBAAA,CAeW;EAKL;EAmBX,KAAA,EArCH,aAqCG;EAGS;EAER,eAAA,CAAA,EAxCO,eAwCP;EAAR;EAoBQ,YAAA,CAAA,EAAA,MAAA;;;;;;;AA4Lb;cA7Oa,eAAA;;;;uBAKU;;;;6BAmBX;;mBAGS;MAEhB,QAAQ;;;;yBAoBA;;MAER,QAAQ;;;;gCAmByB,iBAAiB,QAAQ;;;;;;;;;iBAuK/C,qBAAA,SACN,wBACP"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Output, generateText } from "ai";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/generator/ai-spec-generator.ts
|
|
6
|
+
/**
|
|
7
|
+
* Zod schema for AI-generated spec suggestions.
|
|
8
|
+
*/
|
|
9
|
+
const SpecSuggestionProposalSchema = z.object({
|
|
10
|
+
summary: z.string().describe("Brief summary of the proposed change"),
|
|
11
|
+
rationale: z.string().describe("Detailed explanation of why this change is needed"),
|
|
12
|
+
changeType: z.enum([
|
|
13
|
+
"new-spec",
|
|
14
|
+
"revision",
|
|
15
|
+
"policy-update",
|
|
16
|
+
"schema-update"
|
|
17
|
+
]).describe("Type of change being proposed"),
|
|
18
|
+
recommendedActions: z.array(z.string()).describe("List of specific actions to implement the change"),
|
|
19
|
+
estimatedImpact: z.enum([
|
|
20
|
+
"low",
|
|
21
|
+
"medium",
|
|
22
|
+
"high"
|
|
23
|
+
]).describe("Estimated impact of implementing this change"),
|
|
24
|
+
riskLevel: z.enum([
|
|
25
|
+
"low",
|
|
26
|
+
"medium",
|
|
27
|
+
"high"
|
|
28
|
+
]).describe("Risk level associated with this change"),
|
|
29
|
+
diff: z.string().optional().describe("Optional diff or code snippet showing the change")
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* AI-powered spec generator using AI SDK v6.
|
|
33
|
+
*
|
|
34
|
+
* Uses structured output (Output.object) to generate
|
|
35
|
+
* well-formed spec suggestions from intent patterns.
|
|
36
|
+
*/
|
|
37
|
+
var AISpecGenerator = class {
|
|
38
|
+
model;
|
|
39
|
+
config;
|
|
40
|
+
systemPrompt;
|
|
41
|
+
constructor(options) {
|
|
42
|
+
this.model = options.model;
|
|
43
|
+
this.config = options.evolutionConfig ?? {};
|
|
44
|
+
this.systemPrompt = options.systemPrompt ?? `You are a ContractSpec evolution expert. Your role is to analyze telemetry data, anomalies, and usage patterns to suggest improvements to API contracts and specifications.
|
|
45
|
+
|
|
46
|
+
When generating suggestions:
|
|
47
|
+
1. Be specific and actionable
|
|
48
|
+
2. Consider backwards compatibility
|
|
49
|
+
3. Prioritize stability and reliability
|
|
50
|
+
4. Explain the rationale clearly
|
|
51
|
+
5. Estimate impact and risk accurately`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Generate a spec suggestion from an intent pattern using AI.
|
|
55
|
+
*/
|
|
56
|
+
async generateFromIntent(intent, options = {}) {
|
|
57
|
+
const prompt = this.buildPrompt(intent, options);
|
|
58
|
+
const { output } = await generateText({
|
|
59
|
+
model: this.model,
|
|
60
|
+
system: this.systemPrompt,
|
|
61
|
+
prompt,
|
|
62
|
+
output: Output.object({ schema: SpecSuggestionProposalSchema })
|
|
63
|
+
});
|
|
64
|
+
return this.buildSuggestion(intent, output);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate multiple suggestions for a batch of intents.
|
|
68
|
+
*/
|
|
69
|
+
async generateBatch(intents, options = {}) {
|
|
70
|
+
const maxConcurrent = options.maxConcurrent ?? 3;
|
|
71
|
+
const results = [];
|
|
72
|
+
for (let i = 0; i < intents.length; i += maxConcurrent) {
|
|
73
|
+
const batch = intents.slice(i, i + maxConcurrent);
|
|
74
|
+
const batchResults = await Promise.all(batch.map((intent) => this.generateFromIntent(intent)));
|
|
75
|
+
results.push(...batchResults);
|
|
76
|
+
}
|
|
77
|
+
return results;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate and enhance an existing suggestion using AI.
|
|
81
|
+
*/
|
|
82
|
+
async enhanceSuggestion(suggestion) {
|
|
83
|
+
const prompt = `Review and enhance this spec suggestion:
|
|
84
|
+
|
|
85
|
+
Intent: ${suggestion.intent.type} - ${suggestion.intent.description}
|
|
86
|
+
Current Summary: ${suggestion.proposal.summary}
|
|
87
|
+
Current Rationale: ${suggestion.proposal.rationale}
|
|
88
|
+
|
|
89
|
+
Evidence:
|
|
90
|
+
${suggestion.evidence.map((e) => `- ${e.type}: ${e.description}`).join("\n")}
|
|
91
|
+
|
|
92
|
+
Please provide an improved version with more specific recommendations.`;
|
|
93
|
+
const { output } = await generateText({
|
|
94
|
+
model: this.model,
|
|
95
|
+
system: this.systemPrompt,
|
|
96
|
+
prompt,
|
|
97
|
+
output: Output.object({ schema: SpecSuggestionProposalSchema })
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
...suggestion,
|
|
101
|
+
proposal: {
|
|
102
|
+
...suggestion.proposal,
|
|
103
|
+
summary: output.summary,
|
|
104
|
+
rationale: output.rationale,
|
|
105
|
+
changeType: output.changeType,
|
|
106
|
+
diff: output.diff,
|
|
107
|
+
metadata: {
|
|
108
|
+
...suggestion.proposal.metadata,
|
|
109
|
+
aiEnhanced: true,
|
|
110
|
+
recommendedActions: output.recommendedActions,
|
|
111
|
+
estimatedImpact: output.estimatedImpact,
|
|
112
|
+
riskLevel: output.riskLevel
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
buildPrompt(intent, options) {
|
|
118
|
+
const parts = [
|
|
119
|
+
`Analyze this intent pattern and generate a spec suggestion:`,
|
|
120
|
+
``,
|
|
121
|
+
`Intent Type: ${intent.type}`,
|
|
122
|
+
`Description: ${intent.description}`,
|
|
123
|
+
`Confidence: ${(intent.confidence.score * 100).toFixed(0)}% (sample size: ${intent.confidence.sampleSize})`
|
|
124
|
+
];
|
|
125
|
+
if (intent.operation) parts.push(`Operation: ${intent.operation.key} v${intent.operation.version}`);
|
|
126
|
+
if (intent.evidence.length > 0) {
|
|
127
|
+
parts.push(``, `Evidence:`);
|
|
128
|
+
for (const evidence of intent.evidence) parts.push(`- [${evidence.type}] ${evidence.description}`);
|
|
129
|
+
}
|
|
130
|
+
if (intent.metadata) parts.push(``, `Metadata: ${JSON.stringify(intent.metadata, null, 2)}`);
|
|
131
|
+
if (options.existingSpec) parts.push(``, `Existing Spec:`, "```json", JSON.stringify(options.existingSpec, null, 2), "```");
|
|
132
|
+
if (options.additionalContext) parts.push(``, `Additional Context:`, options.additionalContext);
|
|
133
|
+
return parts.join("\n");
|
|
134
|
+
}
|
|
135
|
+
buildSuggestion(intent, aiOutput) {
|
|
136
|
+
const now = /* @__PURE__ */ new Date();
|
|
137
|
+
const proposal = {
|
|
138
|
+
summary: aiOutput.summary,
|
|
139
|
+
rationale: aiOutput.rationale,
|
|
140
|
+
changeType: aiOutput.changeType,
|
|
141
|
+
diff: aiOutput.diff,
|
|
142
|
+
metadata: {
|
|
143
|
+
aiGenerated: true,
|
|
144
|
+
recommendedActions: aiOutput.recommendedActions,
|
|
145
|
+
estimatedImpact: aiOutput.estimatedImpact,
|
|
146
|
+
riskLevel: aiOutput.riskLevel
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
id: randomUUID(),
|
|
151
|
+
intent,
|
|
152
|
+
target: intent.operation,
|
|
153
|
+
proposal,
|
|
154
|
+
confidence: intent.confidence.score,
|
|
155
|
+
priority: this.calculatePriority(intent, aiOutput),
|
|
156
|
+
createdAt: now,
|
|
157
|
+
createdBy: "ai-spec-generator",
|
|
158
|
+
status: this.determineInitialStatus(intent),
|
|
159
|
+
evidence: intent.evidence,
|
|
160
|
+
tags: ["ai-generated", intent.type]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
calculatePriority(intent, aiOutput) {
|
|
164
|
+
const impactScore = aiOutput.estimatedImpact === "high" ? 1 : aiOutput.estimatedImpact === "medium" ? .5 : .25;
|
|
165
|
+
const intentScore = intent.confidence.score;
|
|
166
|
+
const urgency = intent.type === "error-spike" ? .3 : intent.type === "latency-regression" ? .2 : 0;
|
|
167
|
+
const combined = impactScore * .4 + intentScore * .4 + urgency;
|
|
168
|
+
if (combined >= .7) return "high";
|
|
169
|
+
if (combined >= .4) return "medium";
|
|
170
|
+
return "low";
|
|
171
|
+
}
|
|
172
|
+
determineInitialStatus(intent) {
|
|
173
|
+
if (this.config.autoApproveThreshold && intent.confidence.score >= this.config.autoApproveThreshold && !this.config.requireApproval) return "approved";
|
|
174
|
+
return "pending";
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Create an AI-powered spec generator.
|
|
179
|
+
*/
|
|
180
|
+
function createAISpecGenerator(config) {
|
|
181
|
+
return new AISpecGenerator(config);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
185
|
+
export { AISpecGenerator, createAISpecGenerator };
|
|
186
|
+
//# sourceMappingURL=ai-spec-generator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-spec-generator.mjs","names":[],"sources":["../../src/generator/ai-spec-generator.ts"],"sourcesContent":["import type { LanguageModel } from 'ai';\nimport { generateText, Output } from 'ai';\nimport * as z from 'zod';\nimport { randomUUID } from 'node:crypto';\nimport type {\n EvolutionConfig,\n IntentPattern,\n SpecSuggestion,\n SpecSuggestionProposal,\n SuggestionStatus,\n} from '../types';\n\n/**\n * Zod schema for AI-generated spec suggestions.\n */\nconst SpecSuggestionProposalSchema = z.object({\n summary: z.string().describe('Brief summary of the proposed change'),\n rationale: z\n .string()\n .describe('Detailed explanation of why this change is needed'),\n changeType: z\n .enum(['new-spec', 'revision', 'policy-update', 'schema-update'])\n .describe('Type of change being proposed'),\n recommendedActions: z\n .array(z.string())\n .describe('List of specific actions to implement the change'),\n estimatedImpact: z\n .enum(['low', 'medium', 'high'])\n .describe('Estimated impact of implementing this change'),\n riskLevel: z\n .enum(['low', 'medium', 'high'])\n .describe('Risk level associated with this change'),\n diff: z\n .string()\n .optional()\n .describe('Optional diff or code snippet showing the change'),\n});\n\ntype AIGeneratedProposal = z.infer<typeof SpecSuggestionProposalSchema>;\n\n/**\n * Configuration for the AI-powered spec generator.\n */\nexport interface AISpecGeneratorConfig {\n /** AI SDK language model */\n model: LanguageModel;\n /** Evolution configuration */\n evolutionConfig?: EvolutionConfig;\n /** Custom system prompt */\n systemPrompt?: string;\n}\n\n/**\n * AI-powered spec generator using AI SDK v6.\n *\n * Uses structured output (Output.object) to generate\n * well-formed spec suggestions from intent patterns.\n */\nexport class AISpecGenerator {\n private readonly model: LanguageModel;\n private readonly config: EvolutionConfig;\n private readonly systemPrompt: string;\n\n constructor(options: AISpecGeneratorConfig) {\n this.model = options.model;\n this.config = options.evolutionConfig ?? {};\n this.systemPrompt =\n options.systemPrompt ??\n `You are a ContractSpec evolution expert. Your role is to analyze telemetry data, anomalies, and usage patterns to suggest improvements to API contracts and specifications.\n\nWhen generating suggestions:\n1. Be specific and actionable\n2. Consider backwards compatibility\n3. Prioritize stability and reliability\n4. Explain the rationale clearly\n5. Estimate impact and risk accurately`;\n }\n\n /**\n * Generate a spec suggestion from an intent pattern using AI.\n */\n async generateFromIntent(\n intent: IntentPattern,\n options: {\n additionalContext?: string;\n existingSpec?: Record<string, unknown>;\n } = {}\n ): Promise<SpecSuggestion> {\n const prompt = this.buildPrompt(intent, options);\n\n // AI SDK v6: Output.object() only takes { schema }\n const { output } = await generateText({\n model: this.model,\n system: this.systemPrompt,\n prompt,\n output: Output.object({\n schema: SpecSuggestionProposalSchema,\n }),\n });\n\n return this.buildSuggestion(intent, output);\n }\n\n /**\n * Generate multiple suggestions for a batch of intents.\n */\n async generateBatch(\n intents: IntentPattern[],\n options: { maxConcurrent?: number } = {}\n ): Promise<SpecSuggestion[]> {\n const maxConcurrent = options.maxConcurrent ?? 3;\n const results: SpecSuggestion[] = [];\n\n // Process in batches to avoid overwhelming the API\n for (let i = 0; i < intents.length; i += maxConcurrent) {\n const batch = intents.slice(i, i + maxConcurrent);\n const batchResults = await Promise.all(\n batch.map((intent) => this.generateFromIntent(intent))\n );\n results.push(...batchResults);\n }\n\n return results;\n }\n\n /**\n * Validate and enhance an existing suggestion using AI.\n */\n async enhanceSuggestion(suggestion: SpecSuggestion): Promise<SpecSuggestion> {\n const prompt = `Review and enhance this spec suggestion:\n\nIntent: ${suggestion.intent.type} - ${suggestion.intent.description}\nCurrent Summary: ${suggestion.proposal.summary}\nCurrent Rationale: ${suggestion.proposal.rationale}\n\nEvidence:\n${suggestion.evidence.map((e) => `- ${e.type}: ${e.description}`).join('\\n')}\n\nPlease provide an improved version with more specific recommendations.`;\n\n // AI SDK v6: Output.object() only takes { schema }\n const { output } = await generateText({\n model: this.model,\n system: this.systemPrompt,\n prompt,\n output: Output.object({\n schema: SpecSuggestionProposalSchema,\n }),\n });\n\n return {\n ...suggestion,\n proposal: {\n ...suggestion.proposal,\n summary: output.summary,\n rationale: output.rationale,\n changeType: output.changeType,\n diff: output.diff,\n metadata: {\n ...suggestion.proposal.metadata,\n aiEnhanced: true,\n recommendedActions: output.recommendedActions,\n estimatedImpact: output.estimatedImpact,\n riskLevel: output.riskLevel,\n },\n },\n };\n }\n\n private buildPrompt(\n intent: IntentPattern,\n options: {\n additionalContext?: string;\n existingSpec?: Record<string, unknown>;\n }\n ): string {\n const parts = [\n `Analyze this intent pattern and generate a spec suggestion:`,\n ``,\n `Intent Type: ${intent.type}`,\n `Description: ${intent.description}`,\n `Confidence: ${(intent.confidence.score * 100).toFixed(0)}% (sample size: ${intent.confidence.sampleSize})`,\n ];\n\n if (intent.operation) {\n parts.push(\n `Operation: ${intent.operation.key} v${intent.operation.version}`\n );\n }\n\n if (intent.evidence.length > 0) {\n parts.push(``, `Evidence:`);\n for (const evidence of intent.evidence) {\n parts.push(`- [${evidence.type}] ${evidence.description}`);\n }\n }\n\n if (intent.metadata) {\n parts.push(``, `Metadata: ${JSON.stringify(intent.metadata, null, 2)}`);\n }\n\n if (options.existingSpec) {\n parts.push(\n ``,\n `Existing Spec:`,\n '```json',\n JSON.stringify(options.existingSpec, null, 2),\n '```'\n );\n }\n\n if (options.additionalContext) {\n parts.push(``, `Additional Context:`, options.additionalContext);\n }\n\n return parts.join('\\n');\n }\n\n private buildSuggestion(\n intent: IntentPattern,\n aiOutput: AIGeneratedProposal\n ): SpecSuggestion {\n const now = new Date();\n\n const proposal: SpecSuggestionProposal = {\n summary: aiOutput.summary,\n rationale: aiOutput.rationale,\n changeType: aiOutput.changeType,\n diff: aiOutput.diff,\n metadata: {\n aiGenerated: true,\n recommendedActions: aiOutput.recommendedActions,\n estimatedImpact: aiOutput.estimatedImpact,\n riskLevel: aiOutput.riskLevel,\n },\n };\n\n return {\n id: randomUUID(),\n intent,\n target: intent.operation,\n proposal,\n confidence: intent.confidence.score,\n priority: this.calculatePriority(intent, aiOutput),\n createdAt: now,\n createdBy: 'ai-spec-generator',\n status: this.determineInitialStatus(intent),\n evidence: intent.evidence,\n tags: ['ai-generated', intent.type],\n };\n }\n\n private calculatePriority(\n intent: IntentPattern,\n aiOutput: AIGeneratedProposal\n ): 'low' | 'medium' | 'high' {\n // Combine AI assessment with intent severity\n const impactScore =\n aiOutput.estimatedImpact === 'high'\n ? 1\n : aiOutput.estimatedImpact === 'medium'\n ? 0.5\n : 0.25;\n\n const intentScore = intent.confidence.score;\n const urgency =\n intent.type === 'error-spike'\n ? 0.3\n : intent.type === 'latency-regression'\n ? 0.2\n : 0;\n\n const combined = impactScore * 0.4 + intentScore * 0.4 + urgency;\n\n if (combined >= 0.7) return 'high';\n if (combined >= 0.4) return 'medium';\n return 'low';\n }\n\n private determineInitialStatus(intent: IntentPattern): SuggestionStatus {\n // Auto-approve high-confidence, low-risk suggestions if configured\n if (\n this.config.autoApproveThreshold &&\n intent.confidence.score >= this.config.autoApproveThreshold &&\n !this.config.requireApproval\n ) {\n return 'approved';\n }\n return 'pending';\n }\n}\n\n/**\n * Create an AI-powered spec generator.\n */\nexport function createAISpecGenerator(\n config: AISpecGeneratorConfig\n): AISpecGenerator {\n return new AISpecGenerator(config);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EvolutionConfig, IntentPattern, OperationCoordinate, SpecSuggestion, SpecSuggestionProposal, SuggestionStatus } from "../types.mjs";
|
|
2
|
+
import { Logger } from "@contractspec/lib.observability";
|
|
3
|
+
import { AnyOperationSpec, OperationSpec, ResourceRefDescriptor } from "@contractspec/lib.contracts";
|
|
4
|
+
import { AnySchemaModel } from "@contractspec/lib.schema";
|
|
5
|
+
|
|
6
|
+
//#region src/generator/spec-generator.d.ts
|
|
7
|
+
interface SpecGeneratorOptions {
|
|
8
|
+
config?: EvolutionConfig;
|
|
9
|
+
logger?: Logger;
|
|
10
|
+
clock?: () => Date;
|
|
11
|
+
getSpec?: (key: string, version?: string) => AnyOperationSpec | undefined;
|
|
12
|
+
}
|
|
13
|
+
interface GenerateSpecOptions {
|
|
14
|
+
summary?: string;
|
|
15
|
+
rationale?: string;
|
|
16
|
+
changeType?: SpecSuggestionProposal['changeType'];
|
|
17
|
+
kind?: SpecSuggestionProposal['kind'];
|
|
18
|
+
spec?: AnyOperationSpec;
|
|
19
|
+
diff?: string;
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
status?: SuggestionStatus;
|
|
22
|
+
tags?: string[];
|
|
23
|
+
createdBy?: string;
|
|
24
|
+
}
|
|
25
|
+
type SpecPatch = Partial<OperationSpec<AnySchemaModel, AnySchemaModel | ResourceRefDescriptor<boolean>>> & {
|
|
26
|
+
meta?: Partial<AnyOperationSpec['meta']>;
|
|
27
|
+
};
|
|
28
|
+
declare class SpecGenerator {
|
|
29
|
+
private readonly config;
|
|
30
|
+
private readonly logger?;
|
|
31
|
+
private readonly clock;
|
|
32
|
+
private readonly getSpec?;
|
|
33
|
+
constructor(options?: SpecGeneratorOptions);
|
|
34
|
+
generateFromIntent(intent: IntentPattern, options?: GenerateSpecOptions): SpecSuggestion;
|
|
35
|
+
generateVariant(operation: OperationCoordinate, patch: SpecPatch, intent: IntentPattern, options?: Omit<GenerateSpecOptions, 'spec'>): SpecSuggestion;
|
|
36
|
+
validateSuggestion(suggestion: SpecSuggestion, config?: EvolutionConfig): {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
reasons: string[];
|
|
39
|
+
};
|
|
40
|
+
private intentToVerb;
|
|
41
|
+
private intentToPriority;
|
|
42
|
+
private inferChangeType;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { GenerateSpecOptions, SpecGenerator, SpecGeneratorOptions, SpecPatch };
|
|
46
|
+
//# sourceMappingURL=spec-generator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-generator.d.mts","names":[],"sources":["../../src/generator/spec-generator.ts"],"sourcesContent":[],"mappings":";;;;;;UAiBiB,oBAAA;WACN;EADM,MAAA,CAAA,EAEN,MAFM;EACN,KAAA,CAAA,EAAA,GAAA,GAEK,IAFL;EACA,OAAA,CAAA,EAAA,CAAA,GAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,MAAA,EAAA,GAEoC,gBAFpC,GAAA,SAAA;;AAEoC,UAG9B,mBAAA,CAH8B;EAAgB,OAAA,CAAA,EAAA,MAAA;EAG9C,SAAA,CAAA,EAAA,MAAA;EAGF,UAAA,CAAA,EAAA,sBAAA,CAAA,YAAA,CAAA;EACN,IAAA,CAAA,EAAA,sBAAA,CAAA,MAAA,CAAA;EACA,IAAA,CAAA,EAAA,gBAAA;EAEI,IAAA,CAAA,EAAA,MAAA;EACF,QAAA,CAAA,EADE,MACF,CAAA,MAAA,EAAA,OAAA,CAAA;EAAgB,MAAA,CAAA,EAAhB,gBAAgB;EAKf,IAAA,CAAA,EAAA,MAAS,EAAA;EACL,SAAA,CAAA,EAAA,MAAA;;AAAiC,KADrC,SAAA,GAAY,OACyB,CAA/C,aAA+C,CAAjC,cAAiC,EAAjB,cAAiB,GAAA,qBAAA,CAAA,OAAA,CAAA,CAAA,CAAA,GAAA;EAA/C,IAAA,CAAA,EACW,OADX,CACmB,gBADnB,CAAA,MAAA,CAAA,CAAA;CADsB;AAEH,cAER,aAAA,CAFQ;EAAR,iBAAA,MAAA;EAAO,iBAAA,MAAA;EAEP,iBAAa,KAAA;EAMH,iBAAA,OAAA;EAQX,WAAA,CAAA,OAAA,CAAA,EARW,oBAQX;EACC,kBAAA,CAAA,MAAA,EADD,aACC,EAAA,OAAA,CAAA,EAAA,mBAAA,CAAA,EACR,cADQ;EACR,eAAA,CAAA,SAAA,EAyCU,mBAzCV,EAAA,KAAA,EA0CM,SA1CN,EAAA,MAAA,EA2CO,aA3CP,EAAA,OAAA,CAAA,EA4CQ,IA5CR,CA4Ca,mBA5Cb,EAAA,MAAA,CAAA,CAAA,EA4C8C,cA5C9C;EAyCU,kBAAA,CAAA,UAAA,EAmBC,cAnBD,EAAA,MAAA,CAAA,EAoBH,eApBG,CAAA,EAAA;IACJ,EAAA,EAAA,OAAA;IACC,OAAA,EAAA,MAAA,EAAA;EACM,CAAA;EAAL,QAAA,YAAA;EAAsC,QAAA,gBAAA;EAgBnC,QAAA,eAAA"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Logger } from "@contractspec/lib.observability";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
//#region src/generator/spec-generator.ts
|
|
5
|
+
var SpecGenerator = class {
|
|
6
|
+
config;
|
|
7
|
+
logger;
|
|
8
|
+
clock;
|
|
9
|
+
getSpec;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.config = options.config ?? {};
|
|
12
|
+
this.logger = options.logger;
|
|
13
|
+
this.clock = options.clock ?? (() => /* @__PURE__ */ new Date());
|
|
14
|
+
this.getSpec = options.getSpec;
|
|
15
|
+
}
|
|
16
|
+
generateFromIntent(intent, options = {}) {
|
|
17
|
+
const now = this.clock();
|
|
18
|
+
const summary = options.summary ?? `${this.intentToVerb(intent.type)} ${intent.operation?.key ?? "operation"}`;
|
|
19
|
+
const rationale = options.rationale ?? [intent.description, intent.metadata?.observedValue ? `Observed ${intent.metadata.observedValue}` : void 0].filter(Boolean).join(" — ");
|
|
20
|
+
return {
|
|
21
|
+
id: randomUUID(),
|
|
22
|
+
intent,
|
|
23
|
+
target: intent.operation,
|
|
24
|
+
proposal: {
|
|
25
|
+
summary,
|
|
26
|
+
rationale,
|
|
27
|
+
changeType: options.changeType ?? this.inferChangeType(intent),
|
|
28
|
+
kind: options.kind,
|
|
29
|
+
spec: options.spec,
|
|
30
|
+
diff: options.diff,
|
|
31
|
+
metadata: options.metadata
|
|
32
|
+
},
|
|
33
|
+
confidence: intent.confidence.score,
|
|
34
|
+
priority: this.intentToPriority(intent),
|
|
35
|
+
createdAt: now,
|
|
36
|
+
createdBy: options.createdBy ?? "auto-evolution",
|
|
37
|
+
status: options.status ?? "pending",
|
|
38
|
+
evidence: intent.evidence,
|
|
39
|
+
tags: options.tags
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
generateVariant(operation, patch, intent, options = {}) {
|
|
43
|
+
if (!this.getSpec) throw new Error("SpecGenerator requires getSpec() to generate variants");
|
|
44
|
+
const base = this.getSpec(operation.key, operation.version);
|
|
45
|
+
if (!base) throw new Error(`Cannot generate variant; spec ${operation.key}.v${operation.version} not found`);
|
|
46
|
+
const merged = mergeContract(base, patch);
|
|
47
|
+
return this.generateFromIntent(intent, {
|
|
48
|
+
...options,
|
|
49
|
+
spec: merged
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
validateSuggestion(suggestion, config = this.config) {
|
|
53
|
+
const reasons = [];
|
|
54
|
+
if (config.minConfidence != null && suggestion.confidence < config.minConfidence) reasons.push(`Confidence ${suggestion.confidence.toFixed(2)} below minimum ${config.minConfidence}`);
|
|
55
|
+
if (config.requireApproval && suggestion.status === "approved") reasons.push("Suggestion cannot be auto-approved when approval is required");
|
|
56
|
+
if (suggestion.proposal.spec && !suggestion.proposal.spec.meta?.key) reasons.push("Proposal spec must include meta.key");
|
|
57
|
+
if (!suggestion.proposal.summary) reasons.push("Proposal summary is required");
|
|
58
|
+
const ok = reasons.length === 0;
|
|
59
|
+
if (!ok) this.logger?.warn("SpecGenerator.validateSuggestion.failed", {
|
|
60
|
+
suggestionId: suggestion.id,
|
|
61
|
+
reasons
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
ok,
|
|
65
|
+
reasons
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
intentToVerb(intent) {
|
|
69
|
+
switch (intent) {
|
|
70
|
+
case "error-spike": return "Stabilize";
|
|
71
|
+
case "latency-regression": return "Optimize";
|
|
72
|
+
case "missing-operation": return "Introduce";
|
|
73
|
+
case "throughput-drop": return "Rebalance";
|
|
74
|
+
default: return "Adjust";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
intentToPriority(intent) {
|
|
78
|
+
const severity = intent.confidence.score;
|
|
79
|
+
if (intent.type === "error-spike" || severity >= .8) return "high";
|
|
80
|
+
if (severity >= .5) return "medium";
|
|
81
|
+
return "low";
|
|
82
|
+
}
|
|
83
|
+
inferChangeType(intent) {
|
|
84
|
+
switch (intent.type) {
|
|
85
|
+
case "missing-operation": return "new-spec";
|
|
86
|
+
case "schema-mismatch": return "schema-update";
|
|
87
|
+
case "error-spike": return "policy-update";
|
|
88
|
+
default: return "revision";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
function mergeContract(base, patch) {
|
|
93
|
+
return {
|
|
94
|
+
...base,
|
|
95
|
+
...patch,
|
|
96
|
+
meta: {
|
|
97
|
+
...base.meta,
|
|
98
|
+
...patch.meta
|
|
99
|
+
},
|
|
100
|
+
io: {
|
|
101
|
+
...base.io,
|
|
102
|
+
...patch.io
|
|
103
|
+
},
|
|
104
|
+
policy: {
|
|
105
|
+
...base.policy,
|
|
106
|
+
...patch.policy
|
|
107
|
+
},
|
|
108
|
+
telemetry: {
|
|
109
|
+
...base.telemetry,
|
|
110
|
+
...patch.telemetry
|
|
111
|
+
},
|
|
112
|
+
sideEffects: {
|
|
113
|
+
...base.sideEffects,
|
|
114
|
+
...patch.sideEffects
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
export { SpecGenerator };
|
|
121
|
+
//# sourceMappingURL=spec-generator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-generator.mjs","names":[],"sources":["../../src/generator/spec-generator.ts"],"sourcesContent":["import type {\n AnyOperationSpec,\n OperationSpec,\n ResourceRefDescriptor,\n} from '@contractspec/lib.contracts';\nimport type { AnySchemaModel } from '@contractspec/lib.schema';\nimport { Logger } from '@contractspec/lib.observability';\nimport { randomUUID } from 'node:crypto';\nimport {\n type EvolutionConfig,\n type IntentPattern,\n type OperationCoordinate,\n type SpecSuggestion,\n type SpecSuggestionProposal,\n type SuggestionStatus,\n} from '../types';\n\nexport interface SpecGeneratorOptions {\n config?: EvolutionConfig;\n logger?: Logger;\n clock?: () => Date;\n getSpec?: (key: string, version?: string) => AnyOperationSpec | undefined;\n}\n\nexport interface GenerateSpecOptions {\n summary?: string;\n rationale?: string;\n changeType?: SpecSuggestionProposal['changeType'];\n kind?: SpecSuggestionProposal['kind'];\n spec?: AnyOperationSpec;\n diff?: string;\n metadata?: Record<string, unknown>;\n status?: SuggestionStatus;\n tags?: string[];\n createdBy?: string;\n}\n\nexport type SpecPatch = Partial<\n OperationSpec<AnySchemaModel, AnySchemaModel | ResourceRefDescriptor<boolean>>\n> & { meta?: Partial<AnyOperationSpec['meta']> };\n\nexport class SpecGenerator {\n private readonly config: EvolutionConfig;\n private readonly logger?: Logger;\n private readonly clock: () => Date;\n private readonly getSpec?: SpecGeneratorOptions['getSpec'];\n\n constructor(options: SpecGeneratorOptions = {}) {\n this.config = options.config ?? {};\n this.logger = options.logger;\n this.clock = options.clock ?? (() => new Date());\n this.getSpec = options.getSpec;\n }\n\n generateFromIntent(\n intent: IntentPattern,\n options: GenerateSpecOptions = {}\n ): SpecSuggestion {\n const now = this.clock();\n const summary =\n options.summary ??\n `${this.intentToVerb(intent.type)} ${intent.operation?.key ?? 'operation'}`;\n const rationale =\n options.rationale ??\n [\n intent.description,\n intent.metadata?.observedValue\n ? `Observed ${intent.metadata.observedValue}`\n : undefined,\n ]\n .filter(Boolean)\n .join(' — ');\n\n const suggestion: SpecSuggestion = {\n id: randomUUID(),\n intent,\n target: intent.operation,\n proposal: {\n summary,\n rationale,\n changeType: options.changeType ?? this.inferChangeType(intent),\n kind: options.kind,\n spec: options.spec,\n diff: options.diff,\n metadata: options.metadata,\n },\n confidence: intent.confidence.score,\n priority: this.intentToPriority(intent),\n createdAt: now,\n createdBy: options.createdBy ?? 'auto-evolution',\n status: options.status ?? 'pending',\n evidence: intent.evidence,\n tags: options.tags,\n };\n return suggestion;\n }\n\n generateVariant(\n operation: OperationCoordinate,\n patch: SpecPatch,\n intent: IntentPattern,\n options: Omit<GenerateSpecOptions, 'spec'> = {}\n ) {\n if (!this.getSpec) {\n throw new Error('SpecGenerator requires getSpec() to generate variants');\n }\n const base = this.getSpec(operation.key, operation.version);\n if (!base) {\n throw new Error(\n `Cannot generate variant; spec ${operation.key}.v${operation.version} not found`\n );\n }\n const merged = mergeContract(base, patch);\n return this.generateFromIntent(intent, { ...options, spec: merged });\n }\n\n validateSuggestion(\n suggestion: SpecSuggestion,\n config: EvolutionConfig = this.config\n ) {\n const reasons: string[] = [];\n if (\n config.minConfidence != null &&\n suggestion.confidence < config.minConfidence\n ) {\n reasons.push(\n `Confidence ${suggestion.confidence.toFixed(2)} below minimum ${config.minConfidence}`\n );\n }\n if (config.requireApproval && suggestion.status === 'approved') {\n reasons.push(\n 'Suggestion cannot be auto-approved when approval is required'\n );\n }\n if (suggestion.proposal.spec && !suggestion.proposal.spec.meta?.key) {\n reasons.push('Proposal spec must include meta.key');\n }\n if (!suggestion.proposal.summary) {\n reasons.push('Proposal summary is required');\n }\n const ok = reasons.length === 0;\n if (!ok) {\n this.logger?.warn('SpecGenerator.validateSuggestion.failed', {\n suggestionId: suggestion.id,\n reasons,\n });\n }\n return { ok, reasons };\n }\n\n private intentToVerb(intent: IntentPattern['type']) {\n switch (intent) {\n case 'error-spike':\n return 'Stabilize';\n case 'latency-regression':\n return 'Optimize';\n case 'missing-operation':\n return 'Introduce';\n case 'throughput-drop':\n return 'Rebalance';\n default:\n return 'Adjust';\n }\n }\n\n private intentToPriority(intent: IntentPattern) {\n const severity = intent.confidence.score;\n if (intent.type === 'error-spike' || severity >= 0.8) return 'high';\n if (severity >= 0.5) return 'medium';\n return 'low';\n }\n\n private inferChangeType(\n intent: IntentPattern\n ): SpecSuggestionProposal['changeType'] {\n switch (intent.type) {\n case 'missing-operation':\n return 'new-spec';\n case 'schema-mismatch':\n return 'schema-update';\n case 'error-spike':\n return 'policy-update';\n default:\n return 'revision';\n }\n }\n}\n\nfunction mergeContract(\n base: AnyOperationSpec,\n patch: SpecPatch\n): AnyOperationSpec {\n return {\n ...base,\n ...patch,\n meta: { ...base.meta, ...patch.meta },\n io: {\n ...base.io,\n ...patch.io,\n },\n policy: {\n ...base.policy,\n ...patch.policy,\n },\n telemetry: {\n ...base.telemetry,\n ...patch.telemetry,\n },\n sideEffects: {\n ...base.sideEffects,\n ...patch.sideEffects,\n },\n } as AnyOperationSpec;\n}\n"],"mappings":"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"}
|