cognitive-modules-cli 2.2.0 → 2.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +35 -29
- package/dist/cli.js +572 -28
- package/dist/commands/add.d.ts +33 -14
- package/dist/commands/add.js +222 -13
- package/dist/commands/compose.d.ts +31 -0
- package/dist/commands/compose.js +185 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +5 -0
- package/dist/commands/init.js +23 -1
- package/dist/commands/migrate.d.ts +30 -0
- package/dist/commands/migrate.js +650 -0
- package/dist/commands/pipe.d.ts +1 -0
- package/dist/commands/pipe.js +31 -11
- package/dist/commands/remove.js +33 -2
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +37 -27
- package/dist/commands/search.d.ts +28 -0
- package/dist/commands/search.js +143 -0
- package/dist/commands/test.d.ts +65 -0
- package/dist/commands/test.js +454 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +106 -14
- package/dist/commands/validate.d.ts +36 -0
- package/dist/commands/validate.js +97 -0
- package/dist/errors/index.d.ts +218 -0
- package/dist/errors/index.js +412 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1
- package/dist/mcp/server.js +84 -79
- package/dist/modules/composition.d.ts +251 -0
- package/dist/modules/composition.js +1330 -0
- package/dist/modules/index.d.ts +2 -0
- package/dist/modules/index.js +2 -0
- package/dist/modules/loader.d.ts +22 -2
- package/dist/modules/loader.js +171 -6
- package/dist/modules/runner.d.ts +422 -1
- package/dist/modules/runner.js +1472 -71
- package/dist/modules/subagent.d.ts +6 -1
- package/dist/modules/subagent.js +20 -13
- package/dist/modules/validator.d.ts +28 -0
- package/dist/modules/validator.js +637 -0
- package/dist/providers/anthropic.d.ts +15 -0
- package/dist/providers/anthropic.js +147 -5
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.js +18 -0
- package/dist/providers/gemini.d.ts +15 -0
- package/dist/providers/gemini.js +122 -5
- package/dist/providers/ollama.d.ts +15 -0
- package/dist/providers/ollama.js +111 -3
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.js +133 -0
- package/dist/registry/client.d.ts +204 -0
- package/dist/registry/client.js +356 -0
- package/dist/registry/index.d.ts +4 -0
- package/dist/registry/index.js +4 -0
- package/dist/server/http.js +173 -42
- package/dist/types.d.ts +123 -8
- package/dist/types.js +4 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/package.json +32 -7
- package/src/cli.ts +0 -410
- package/src/commands/add.ts +0 -315
- package/src/commands/index.ts +0 -12
- package/src/commands/init.ts +0 -94
- package/src/commands/list.ts +0 -33
- package/src/commands/pipe.ts +0 -76
- package/src/commands/remove.ts +0 -57
- package/src/commands/run.ts +0 -80
- package/src/commands/update.ts +0 -130
- package/src/commands/versions.ts +0 -79
- package/src/index.ts +0 -55
- package/src/mcp/index.ts +0 -5
- package/src/mcp/server.ts +0 -403
- package/src/modules/index.ts +0 -7
- package/src/modules/loader.ts +0 -318
- package/src/modules/runner.ts +0 -495
- package/src/modules/subagent.ts +0 -275
- package/src/providers/anthropic.ts +0 -89
- package/src/providers/base.ts +0 -29
- package/src/providers/deepseek.ts +0 -83
- package/src/providers/gemini.ts +0 -117
- package/src/providers/index.ts +0 -78
- package/src/providers/minimax.ts +0 -81
- package/src/providers/moonshot.ts +0 -82
- package/src/providers/ollama.ts +0 -83
- package/src/providers/openai.ts +0 -84
- package/src/providers/qwen.ts +0 -82
- package/src/server/http.ts +0 -316
- package/src/server/index.ts +0 -6
- package/src/types.ts +0 -495
- package/tsconfig.json +0 -17
package/src/modules/runner.ts
DELETED
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module Runner - Execute Cognitive Modules
|
|
3
|
-
* v2.2: Envelope format with meta/data separation, risk_rule, repair pass
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
Provider,
|
|
8
|
-
CognitiveModule,
|
|
9
|
-
ModuleResult,
|
|
10
|
-
ModuleResultV21,
|
|
11
|
-
ModuleResultV22,
|
|
12
|
-
Message,
|
|
13
|
-
ModuleInput,
|
|
14
|
-
EnvelopeResponse,
|
|
15
|
-
EnvelopeResponseV22,
|
|
16
|
-
EnvelopeMeta,
|
|
17
|
-
ModuleResultData,
|
|
18
|
-
RiskLevel,
|
|
19
|
-
RiskRule
|
|
20
|
-
} from '../types.js';
|
|
21
|
-
import { aggregateRisk, isV22Envelope } from '../types.js';
|
|
22
|
-
|
|
23
|
-
export interface RunOptions {
|
|
24
|
-
// Clean input (v2 style)
|
|
25
|
-
input?: ModuleInput;
|
|
26
|
-
|
|
27
|
-
// Legacy CLI args (v1 compatibility) - mapped to input.code or input.query
|
|
28
|
-
args?: string;
|
|
29
|
-
|
|
30
|
-
// Runtime options
|
|
31
|
-
verbose?: boolean;
|
|
32
|
-
|
|
33
|
-
// Force envelope format (default: auto-detect from module.output.envelope)
|
|
34
|
-
useEnvelope?: boolean;
|
|
35
|
-
|
|
36
|
-
// Force v2.2 format (default: auto-detect from module.tier)
|
|
37
|
-
useV22?: boolean;
|
|
38
|
-
|
|
39
|
-
// Enable repair pass for validation failures (default: true)
|
|
40
|
-
enableRepair?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// =============================================================================
|
|
44
|
-
// Repair Pass (v2.2)
|
|
45
|
-
// =============================================================================
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Attempt to repair envelope format issues without changing semantics.
|
|
49
|
-
*
|
|
50
|
-
* Repairs (lossless only):
|
|
51
|
-
* - Missing meta fields (fill with conservative defaults)
|
|
52
|
-
* - Truncate explain if too long
|
|
53
|
-
* - Trim whitespace from string fields
|
|
54
|
-
*
|
|
55
|
-
* Does NOT repair:
|
|
56
|
-
* - Invalid enum values (treated as validation failure)
|
|
57
|
-
*/
|
|
58
|
-
function repairEnvelope(
|
|
59
|
-
response: Record<string, unknown>,
|
|
60
|
-
riskRule: RiskRule = 'max_changes_risk',
|
|
61
|
-
maxExplainLength: number = 280
|
|
62
|
-
): EnvelopeResponseV22<unknown> {
|
|
63
|
-
const repaired = { ...response };
|
|
64
|
-
|
|
65
|
-
// Ensure meta exists
|
|
66
|
-
if (!repaired.meta || typeof repaired.meta !== 'object') {
|
|
67
|
-
repaired.meta = {};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const meta = repaired.meta as Record<string, unknown>;
|
|
71
|
-
const data = (repaired.data ?? {}) as Record<string, unknown>;
|
|
72
|
-
|
|
73
|
-
// Repair confidence
|
|
74
|
-
if (typeof meta.confidence !== 'number') {
|
|
75
|
-
meta.confidence = (data.confidence as number) ?? 0.5;
|
|
76
|
-
}
|
|
77
|
-
meta.confidence = Math.max(0, Math.min(1, meta.confidence as number));
|
|
78
|
-
|
|
79
|
-
// Repair risk using configurable aggregation rule
|
|
80
|
-
if (!meta.risk) {
|
|
81
|
-
meta.risk = aggregateRisk(data, riskRule);
|
|
82
|
-
}
|
|
83
|
-
// Trim whitespace only (lossless), validate is valid RiskLevel
|
|
84
|
-
if (typeof meta.risk === 'string') {
|
|
85
|
-
const trimmedRisk = meta.risk.trim().toLowerCase();
|
|
86
|
-
const validRisks = ['none', 'low', 'medium', 'high'];
|
|
87
|
-
meta.risk = validRisks.includes(trimmedRisk) ? trimmedRisk : 'medium';
|
|
88
|
-
} else {
|
|
89
|
-
meta.risk = 'medium'; // Default for invalid type
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Repair explain
|
|
93
|
-
if (typeof meta.explain !== 'string') {
|
|
94
|
-
const rationale = data.rationale as string | undefined;
|
|
95
|
-
meta.explain = rationale ? String(rationale).slice(0, maxExplainLength) : 'No explanation provided';
|
|
96
|
-
}
|
|
97
|
-
// Trim whitespace (lossless)
|
|
98
|
-
const explainStr = meta.explain as string;
|
|
99
|
-
meta.explain = explainStr.trim();
|
|
100
|
-
if ((meta.explain as string).length > maxExplainLength) {
|
|
101
|
-
meta.explain = (meta.explain as string).slice(0, maxExplainLength - 3) + '...';
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Build proper v2.2 response
|
|
105
|
-
const builtMeta: EnvelopeMeta = {
|
|
106
|
-
confidence: meta.confidence as number,
|
|
107
|
-
risk: meta.risk as RiskLevel,
|
|
108
|
-
explain: meta.explain as string
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const result: EnvelopeResponseV22<unknown> = repaired.ok === false ? {
|
|
112
|
-
ok: false,
|
|
113
|
-
meta: builtMeta,
|
|
114
|
-
error: (repaired.error as { code: string; message: string }) ?? { code: 'UNKNOWN', message: 'Unknown error' },
|
|
115
|
-
partial_data: repaired.partial_data
|
|
116
|
-
} : {
|
|
117
|
-
ok: true,
|
|
118
|
-
meta: builtMeta,
|
|
119
|
-
data: repaired.data
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Wrap v2.1 response to v2.2 format
|
|
127
|
-
*/
|
|
128
|
-
function wrapV21ToV22(
|
|
129
|
-
response: EnvelopeResponse<unknown>,
|
|
130
|
-
riskRule: RiskRule = 'max_changes_risk'
|
|
131
|
-
): EnvelopeResponseV22<unknown> {
|
|
132
|
-
if (isV22Envelope(response)) {
|
|
133
|
-
return response;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (response.ok) {
|
|
137
|
-
const data = (response.data ?? {}) as Record<string, unknown>;
|
|
138
|
-
const confidence = (data.confidence as number) ?? 0.5;
|
|
139
|
-
const rationale = (data.rationale as string) ?? '';
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
ok: true,
|
|
143
|
-
meta: {
|
|
144
|
-
confidence,
|
|
145
|
-
risk: aggregateRisk(data, riskRule),
|
|
146
|
-
explain: rationale.slice(0, 280) || 'No explanation provided'
|
|
147
|
-
},
|
|
148
|
-
data: data as ModuleResultData
|
|
149
|
-
};
|
|
150
|
-
} else {
|
|
151
|
-
const errorMsg = response.error?.message ?? 'Unknown error';
|
|
152
|
-
return {
|
|
153
|
-
ok: false,
|
|
154
|
-
meta: {
|
|
155
|
-
confidence: 0,
|
|
156
|
-
risk: 'high',
|
|
157
|
-
explain: errorMsg.slice(0, 280)
|
|
158
|
-
},
|
|
159
|
-
error: response.error ?? { code: 'UNKNOWN', message: errorMsg },
|
|
160
|
-
partial_data: response.partial_data
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export async function runModule(
|
|
166
|
-
module: CognitiveModule,
|
|
167
|
-
provider: Provider,
|
|
168
|
-
options: RunOptions = {}
|
|
169
|
-
): Promise<ModuleResult> {
|
|
170
|
-
const { args, input, verbose = false, useEnvelope, useV22, enableRepair = true } = options;
|
|
171
|
-
|
|
172
|
-
// Determine if we should use envelope format
|
|
173
|
-
const shouldUseEnvelope = useEnvelope ?? (module.output?.envelope === true || module.format === 'v2');
|
|
174
|
-
|
|
175
|
-
// Determine if we should use v2.2 format
|
|
176
|
-
const isV22Module = module.tier !== undefined || module.formatVersion === 'v2.2';
|
|
177
|
-
const shouldUseV22 = useV22 ?? (isV22Module || module.compat?.runtime_auto_wrap === true);
|
|
178
|
-
|
|
179
|
-
// Get risk_rule from module config
|
|
180
|
-
const riskRule: RiskRule = module.metaConfig?.risk_rule ?? 'max_changes_risk';
|
|
181
|
-
|
|
182
|
-
// Build clean input data (v2 style: no $ARGUMENTS pollution)
|
|
183
|
-
const inputData: ModuleInput = input || {};
|
|
184
|
-
|
|
185
|
-
// Map legacy --args to clean input
|
|
186
|
-
if (args && !inputData.code && !inputData.query) {
|
|
187
|
-
// Determine if args looks like code or natural language
|
|
188
|
-
if (looksLikeCode(args)) {
|
|
189
|
-
inputData.code = args;
|
|
190
|
-
} else {
|
|
191
|
-
inputData.query = args;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Build prompt with clean substitution
|
|
196
|
-
const prompt = buildPrompt(module, inputData);
|
|
197
|
-
|
|
198
|
-
if (verbose) {
|
|
199
|
-
console.error('--- Module ---');
|
|
200
|
-
console.error(`Name: ${module.name} (${module.format})`);
|
|
201
|
-
console.error(`Responsibility: ${module.responsibility}`);
|
|
202
|
-
console.error(`Envelope: ${shouldUseEnvelope}`);
|
|
203
|
-
console.error('--- Input ---');
|
|
204
|
-
console.error(JSON.stringify(inputData, null, 2));
|
|
205
|
-
console.error('--- Prompt ---');
|
|
206
|
-
console.error(prompt);
|
|
207
|
-
console.error('--- End ---');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Build system message based on module config
|
|
211
|
-
const systemParts: string[] = [
|
|
212
|
-
`You are executing the "${module.name}" Cognitive Module.`,
|
|
213
|
-
'',
|
|
214
|
-
`RESPONSIBILITY: ${module.responsibility}`,
|
|
215
|
-
];
|
|
216
|
-
|
|
217
|
-
if (module.excludes.length > 0) {
|
|
218
|
-
systemParts.push('', 'YOU MUST NOT:');
|
|
219
|
-
module.excludes.forEach(e => systemParts.push(`- ${e}`));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (module.constraints) {
|
|
223
|
-
systemParts.push('', 'CONSTRAINTS:');
|
|
224
|
-
if (module.constraints.no_network) systemParts.push('- No network access');
|
|
225
|
-
if (module.constraints.no_side_effects) systemParts.push('- No side effects');
|
|
226
|
-
if (module.constraints.no_file_write) systemParts.push('- No file writes');
|
|
227
|
-
if (module.constraints.no_inventing_data) systemParts.push('- Do not invent data');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (module.output?.require_behavior_equivalence) {
|
|
231
|
-
systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
|
|
232
|
-
systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
|
|
233
|
-
systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
|
|
234
|
-
|
|
235
|
-
const maxConfidence = module.constraints?.behavior_equivalence_false_max_confidence ?? 0.7;
|
|
236
|
-
systemParts.push(`- If behavior_equivalence=false, confidence MUST be <= ${maxConfidence}`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Add envelope format instructions
|
|
240
|
-
if (shouldUseEnvelope) {
|
|
241
|
-
if (shouldUseV22) {
|
|
242
|
-
systemParts.push('', 'RESPONSE FORMAT (Envelope v2.2):');
|
|
243
|
-
systemParts.push('- Wrap your response in the v2.2 envelope format with separate meta and data');
|
|
244
|
-
systemParts.push('- Success: { "ok": true, "meta": { "confidence": 0.9, "risk": "low", "explain": "short summary" }, "data": { ...payload... } }');
|
|
245
|
-
systemParts.push('- Error: { "ok": false, "meta": { "confidence": 0.0, "risk": "high", "explain": "error summary" }, "error": { "code": "ERROR_CODE", "message": "..." } }');
|
|
246
|
-
systemParts.push('- meta.explain must be ≤280 characters. data.rationale can be longer for detailed reasoning.');
|
|
247
|
-
systemParts.push('- meta.risk must be one of: "none", "low", "medium", "high"');
|
|
248
|
-
} else {
|
|
249
|
-
systemParts.push('', 'RESPONSE FORMAT (Envelope):');
|
|
250
|
-
systemParts.push('- Wrap your response in the envelope format');
|
|
251
|
-
systemParts.push('- Success: { "ok": true, "data": { ...your output... } }');
|
|
252
|
-
systemParts.push('- Error: { "ok": false, "error": { "code": "ERROR_CODE", "message": "..." } }');
|
|
253
|
-
systemParts.push('- Include "confidence" (0-1) and "rationale" in data');
|
|
254
|
-
}
|
|
255
|
-
if (module.output?.require_behavior_equivalence) {
|
|
256
|
-
systemParts.push('- Include "behavior_equivalence" (boolean) in data');
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
systemParts.push('', 'OUTPUT FORMAT:');
|
|
260
|
-
systemParts.push('- Respond with ONLY valid JSON');
|
|
261
|
-
systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
|
|
262
|
-
if (module.output?.require_behavior_equivalence) {
|
|
263
|
-
systemParts.push('- Include "behavior_equivalence" (boolean) field');
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const messages: Message[] = [
|
|
268
|
-
{ role: 'system', content: systemParts.join('\n') },
|
|
269
|
-
{ role: 'user', content: prompt },
|
|
270
|
-
];
|
|
271
|
-
|
|
272
|
-
// Invoke provider
|
|
273
|
-
const result = await provider.invoke({
|
|
274
|
-
messages,
|
|
275
|
-
jsonSchema: module.outputSchema,
|
|
276
|
-
temperature: 0.3,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
if (verbose) {
|
|
280
|
-
console.error('--- Response ---');
|
|
281
|
-
console.error(result.content);
|
|
282
|
-
console.error('--- End Response ---');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Parse response
|
|
286
|
-
let parsed: unknown;
|
|
287
|
-
try {
|
|
288
|
-
const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
289
|
-
const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
|
|
290
|
-
parsed = JSON.parse(jsonStr.trim());
|
|
291
|
-
} catch {
|
|
292
|
-
throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Handle envelope format
|
|
296
|
-
if (shouldUseEnvelope && isEnvelopeResponse(parsed)) {
|
|
297
|
-
let response = parseEnvelopeResponse(parsed, result.content);
|
|
298
|
-
|
|
299
|
-
// Upgrade to v2.2 if needed
|
|
300
|
-
if (shouldUseV22 && response.ok && !('meta' in response && response.meta)) {
|
|
301
|
-
const upgraded = wrapV21ToV22(parsed as EnvelopeResponse<unknown>, riskRule);
|
|
302
|
-
response = {
|
|
303
|
-
ok: true,
|
|
304
|
-
meta: upgraded.meta as EnvelopeMeta,
|
|
305
|
-
data: (upgraded as { data?: ModuleResultData }).data,
|
|
306
|
-
raw: result.content
|
|
307
|
-
} as ModuleResultV22;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Apply repair pass if enabled and response needs it
|
|
311
|
-
if (enableRepair && response.ok && shouldUseV22) {
|
|
312
|
-
const repaired = repairEnvelope(
|
|
313
|
-
response as unknown as Record<string, unknown>,
|
|
314
|
-
riskRule
|
|
315
|
-
);
|
|
316
|
-
response = {
|
|
317
|
-
ok: true,
|
|
318
|
-
meta: repaired.meta as EnvelopeMeta,
|
|
319
|
-
data: (repaired as { data?: ModuleResultData }).data,
|
|
320
|
-
raw: result.content
|
|
321
|
-
} as ModuleResultV22;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return response;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Handle legacy format (non-envelope)
|
|
328
|
-
const legacyResult = parseLegacyResponse(parsed, result.content);
|
|
329
|
-
|
|
330
|
-
// Upgrade to v2.2 if requested
|
|
331
|
-
if (shouldUseV22 && legacyResult.ok) {
|
|
332
|
-
const data = (legacyResult.data ?? {}) as Record<string, unknown>;
|
|
333
|
-
return {
|
|
334
|
-
ok: true,
|
|
335
|
-
meta: {
|
|
336
|
-
confidence: (data.confidence as number) ?? 0.5,
|
|
337
|
-
risk: aggregateRisk(data, riskRule),
|
|
338
|
-
explain: ((data.rationale as string) ?? '').slice(0, 280) || 'No explanation provided'
|
|
339
|
-
},
|
|
340
|
-
data: legacyResult.data,
|
|
341
|
-
raw: result.content
|
|
342
|
-
} as ModuleResultV22;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return legacyResult;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Check if response is in envelope format
|
|
350
|
-
*/
|
|
351
|
-
function isEnvelopeResponse(obj: unknown): obj is EnvelopeResponse {
|
|
352
|
-
if (typeof obj !== 'object' || obj === null) return false;
|
|
353
|
-
const o = obj as Record<string, unknown>;
|
|
354
|
-
return typeof o.ok === 'boolean';
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Parse envelope format response (supports both v2.1 and v2.2)
|
|
359
|
-
*/
|
|
360
|
-
function parseEnvelopeResponse(response: EnvelopeResponse<unknown>, raw: string): ModuleResult {
|
|
361
|
-
// Check if v2.2 format (has meta)
|
|
362
|
-
if (isV22Envelope(response)) {
|
|
363
|
-
if (response.ok) {
|
|
364
|
-
return {
|
|
365
|
-
ok: true,
|
|
366
|
-
meta: response.meta,
|
|
367
|
-
data: response.data as ModuleResultData,
|
|
368
|
-
raw,
|
|
369
|
-
} as ModuleResultV22;
|
|
370
|
-
} else {
|
|
371
|
-
return {
|
|
372
|
-
ok: false,
|
|
373
|
-
meta: response.meta,
|
|
374
|
-
error: response.error,
|
|
375
|
-
partial_data: response.partial_data,
|
|
376
|
-
raw,
|
|
377
|
-
} as ModuleResultV22;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// v2.1 format
|
|
382
|
-
if (response.ok) {
|
|
383
|
-
const data = (response.data ?? {}) as ModuleResultData & { confidence?: number };
|
|
384
|
-
return {
|
|
385
|
-
ok: true,
|
|
386
|
-
data: {
|
|
387
|
-
...data,
|
|
388
|
-
confidence: typeof data.confidence === 'number' ? data.confidence : 0.5,
|
|
389
|
-
rationale: typeof data.rationale === 'string' ? data.rationale : '',
|
|
390
|
-
behavior_equivalence: data.behavior_equivalence,
|
|
391
|
-
},
|
|
392
|
-
raw,
|
|
393
|
-
} as ModuleResultV21;
|
|
394
|
-
} else {
|
|
395
|
-
return {
|
|
396
|
-
ok: false,
|
|
397
|
-
error: response.error,
|
|
398
|
-
partial_data: response.partial_data,
|
|
399
|
-
raw,
|
|
400
|
-
} as ModuleResultV21;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Parse legacy (non-envelope) format response
|
|
406
|
-
*/
|
|
407
|
-
function parseLegacyResponse(output: unknown, raw: string): ModuleResult {
|
|
408
|
-
const outputObj = output as Record<string, unknown>;
|
|
409
|
-
const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
|
|
410
|
-
const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
|
|
411
|
-
const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
|
|
412
|
-
? outputObj.behavior_equivalence
|
|
413
|
-
: undefined;
|
|
414
|
-
|
|
415
|
-
// Check if this is an error response (has error.code)
|
|
416
|
-
if (outputObj.error && typeof outputObj.error === 'object') {
|
|
417
|
-
const errorObj = outputObj.error as Record<string, unknown>;
|
|
418
|
-
if (typeof errorObj.code === 'string') {
|
|
419
|
-
return {
|
|
420
|
-
ok: false,
|
|
421
|
-
error: {
|
|
422
|
-
code: errorObj.code,
|
|
423
|
-
message: typeof errorObj.message === 'string' ? errorObj.message : 'Unknown error',
|
|
424
|
-
},
|
|
425
|
-
raw,
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Return as v2.1 format (data includes confidence)
|
|
431
|
-
return {
|
|
432
|
-
ok: true,
|
|
433
|
-
data: {
|
|
434
|
-
...outputObj,
|
|
435
|
-
confidence,
|
|
436
|
-
rationale,
|
|
437
|
-
behavior_equivalence: behaviorEquivalence,
|
|
438
|
-
},
|
|
439
|
-
raw,
|
|
440
|
-
} as ModuleResultV21;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Build prompt with clean variable substitution
|
|
445
|
-
*/
|
|
446
|
-
function buildPrompt(module: CognitiveModule, input: ModuleInput): string {
|
|
447
|
-
let prompt = module.prompt;
|
|
448
|
-
|
|
449
|
-
// v2 style: substitute ${variable} placeholders
|
|
450
|
-
for (const [key, value] of Object.entries(input)) {
|
|
451
|
-
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
452
|
-
prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// v1 compatibility: substitute $ARGUMENTS
|
|
456
|
-
const argsValue = input.code || input.query || '';
|
|
457
|
-
prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
|
|
458
|
-
|
|
459
|
-
// Substitute $N placeholders (v1 compatibility)
|
|
460
|
-
if (typeof argsValue === 'string') {
|
|
461
|
-
const argsList = argsValue.split(/\s+/);
|
|
462
|
-
argsList.forEach((arg, i) => {
|
|
463
|
-
prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Append input summary if not already in prompt
|
|
468
|
-
if (!prompt.includes(argsValue) && argsValue) {
|
|
469
|
-
prompt += '\n\n## Input\n\n';
|
|
470
|
-
if (input.code) {
|
|
471
|
-
prompt += '```\n' + input.code + '\n```\n';
|
|
472
|
-
}
|
|
473
|
-
if (input.query) {
|
|
474
|
-
prompt += input.query + '\n';
|
|
475
|
-
}
|
|
476
|
-
if (input.language) {
|
|
477
|
-
prompt += `\nLanguage: ${input.language}\n`;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return prompt;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Heuristic to detect if input looks like code
|
|
486
|
-
*/
|
|
487
|
-
function looksLikeCode(str: string): boolean {
|
|
488
|
-
const codeIndicators = [
|
|
489
|
-
/^(def|function|class|const|let|var|import|export|public|private)\s/,
|
|
490
|
-
/[{};()]/,
|
|
491
|
-
/=>/,
|
|
492
|
-
/\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
|
|
493
|
-
];
|
|
494
|
-
return codeIndicators.some(re => re.test(str));
|
|
495
|
-
}
|