cognitive-runtime 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +7 -3
- package/dist/commands/pipe.js +0 -2
- package/dist/commands/run.js +0 -2
- package/dist/index.d.ts +1 -1
- package/dist/modules/loader.d.ts +4 -0
- package/dist/modules/loader.js +119 -15
- package/dist/modules/runner.d.ts +3 -4
- package/dist/modules/runner.js +99 -43
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +24 -22
- package/dist/types.d.ts +33 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/src/cli.ts +7 -3
- package/src/commands/pipe.ts +0 -2
- package/src/commands/run.ts +0 -2
- package/src/index.ts +5 -0
- package/src/modules/loader.ts +125 -14
- package/src/modules/runner.ts +109 -47
- package/src/providers/index.ts +28 -23
- package/src/types.ts +57 -1
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { parseArgs } from 'node:util';
|
|
11
11
|
import { getProvider, listProviders } from './providers/index.js';
|
|
12
12
|
import { run, list, pipe, init } from './commands/index.js';
|
|
13
|
-
const VERSION = '0.
|
|
13
|
+
const VERSION = '0.4.0';
|
|
14
14
|
async function main() {
|
|
15
15
|
const args = process.argv.slice(2);
|
|
16
16
|
const command = args[0];
|
|
@@ -29,6 +29,7 @@ async function main() {
|
|
|
29
29
|
args: { type: 'string', short: 'a' },
|
|
30
30
|
input: { type: 'string', short: 'i' },
|
|
31
31
|
module: { type: 'string', short: 'm' },
|
|
32
|
+
model: { type: 'string', short: 'M' },
|
|
32
33
|
provider: { type: 'string', short: 'p' },
|
|
33
34
|
pretty: { type: 'boolean', default: false },
|
|
34
35
|
verbose: { type: 'boolean', short: 'V', default: false },
|
|
@@ -39,7 +40,7 @@ async function main() {
|
|
|
39
40
|
// Get provider
|
|
40
41
|
let provider;
|
|
41
42
|
try {
|
|
42
|
-
provider = getProvider(values.provider);
|
|
43
|
+
provider = getProvider(values.provider, values.model);
|
|
43
44
|
}
|
|
44
45
|
catch (e) {
|
|
45
46
|
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
@@ -174,7 +175,8 @@ OPTIONS:
|
|
|
174
175
|
-a, --args <str> Arguments to pass to module
|
|
175
176
|
-i, --input <json> JSON input for module
|
|
176
177
|
-m, --module <name> Module name (for pipe)
|
|
177
|
-
-
|
|
178
|
+
-M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
|
|
179
|
+
-p, --provider <name> LLM provider (gemini, openai, anthropic, deepseek, minimax, moonshot, qwen, ollama)
|
|
178
180
|
--pretty Pretty-print JSON output
|
|
179
181
|
-V, --verbose Verbose output
|
|
180
182
|
--no-validate Skip schema validation
|
|
@@ -183,6 +185,7 @@ OPTIONS:
|
|
|
183
185
|
|
|
184
186
|
EXAMPLES:
|
|
185
187
|
cog run code-reviewer --args "def foo(): pass"
|
|
188
|
+
cog run code-reviewer --provider openai --model gpt-4o --args "..."
|
|
186
189
|
cog list
|
|
187
190
|
echo "review this code" | cog pipe --module code-reviewer
|
|
188
191
|
cog init my-module
|
|
@@ -197,6 +200,7 @@ ENVIRONMENT:
|
|
|
197
200
|
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
198
201
|
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
199
202
|
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
203
|
+
COG_MODEL Override default model for any provider
|
|
200
204
|
`);
|
|
201
205
|
}
|
|
202
206
|
main().catch(e => {
|
package/dist/commands/pipe.js
CHANGED
|
@@ -36,8 +36,6 @@ export async function pipe(ctx, options) {
|
|
|
36
36
|
const result = await runModule(module, ctx.provider, {
|
|
37
37
|
args: inputData ? undefined : input,
|
|
38
38
|
input: inputData,
|
|
39
|
-
validateInput: !options.noValidate,
|
|
40
|
-
validateOutput: !options.noValidate,
|
|
41
39
|
});
|
|
42
40
|
// Output JSON to stdout
|
|
43
41
|
console.log(JSON.stringify(result.output));
|
package/dist/commands/run.js
CHANGED
|
@@ -30,8 +30,6 @@ export async function run(moduleName, ctx, options = {}) {
|
|
|
30
30
|
const result = await runModule(module, ctx.provider, {
|
|
31
31
|
args: options.args,
|
|
32
32
|
input: inputData,
|
|
33
|
-
validateInput: !options.noValidate,
|
|
34
|
-
validateOutput: !options.noValidate,
|
|
35
33
|
verbose: options.verbose || ctx.verbose,
|
|
36
34
|
});
|
|
37
35
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Exports all public APIs for programmatic use.
|
|
5
5
|
*/
|
|
6
|
-
export type { Provider, InvokeParams, InvokeResult, Message, CognitiveModule, ModuleResult, CommandContext, CommandResult, } from './types.js';
|
|
6
|
+
export type { Provider, InvokeParams, InvokeResult, Message, CognitiveModule, ModuleResult, ModuleInput, ModuleConstraints, ToolsPolicy, OutputContract, FailureContract, CommandContext, CommandResult, } from './types.js';
|
|
7
7
|
export { getProvider, listProviders, GeminiProvider, OpenAIProvider, AnthropicProvider, BaseProvider, } from './providers/index.js';
|
|
8
8
|
export { loadModule, findModule, listModules, getDefaultSearchPaths, runModule, } from './modules/index.js';
|
|
9
9
|
export { run, list, pipe } from './commands/index.js';
|
package/dist/modules/loader.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Loader - Load and parse Cognitive Modules
|
|
3
|
+
* Supports both v1 (MODULE.md) and v2 (module.yaml + prompt.md) formats
|
|
3
4
|
*/
|
|
4
5
|
import type { CognitiveModule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Load a Cognitive Module (auto-detects format)
|
|
8
|
+
*/
|
|
5
9
|
export declare function loadModule(modulePath: string): Promise<CognitiveModule>;
|
|
6
10
|
export declare function findModule(name: string, searchPaths: string[]): Promise<CognitiveModule | null>;
|
|
7
11
|
export declare function listModules(searchPaths: string[]): Promise<CognitiveModule[]>;
|
package/dist/modules/loader.js
CHANGED
|
@@ -1,11 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Loader - Load and parse Cognitive Modules
|
|
3
|
+
* Supports both v1 (MODULE.md) and v2 (module.yaml + prompt.md) formats
|
|
3
4
|
*/
|
|
4
5
|
import * as fs from 'node:fs/promises';
|
|
5
6
|
import * as path from 'node:path';
|
|
6
7
|
import yaml from 'js-yaml';
|
|
7
8
|
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/;
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Detect module format version
|
|
11
|
+
*/
|
|
12
|
+
async function detectFormat(modulePath) {
|
|
13
|
+
const v2Manifest = path.join(modulePath, 'module.yaml');
|
|
14
|
+
try {
|
|
15
|
+
await fs.access(v2Manifest);
|
|
16
|
+
return 'v2';
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return 'v1';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load v2 format module (module.yaml + prompt.md)
|
|
24
|
+
*/
|
|
25
|
+
async function loadModuleV2(modulePath) {
|
|
26
|
+
const manifestFile = path.join(modulePath, 'module.yaml');
|
|
27
|
+
const promptFile = path.join(modulePath, 'prompt.md');
|
|
28
|
+
const schemaFile = path.join(modulePath, 'schema.json');
|
|
29
|
+
// Read module.yaml
|
|
30
|
+
const manifestContent = await fs.readFile(manifestFile, 'utf-8');
|
|
31
|
+
const manifest = yaml.load(manifestContent);
|
|
32
|
+
// Read prompt.md
|
|
33
|
+
let prompt = '';
|
|
34
|
+
try {
|
|
35
|
+
prompt = await fs.readFile(promptFile, 'utf-8');
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// prompt.md is optional, manifest may include inline prompt
|
|
39
|
+
}
|
|
40
|
+
// Read schema.json
|
|
41
|
+
let inputSchema;
|
|
42
|
+
let outputSchema;
|
|
43
|
+
let errorSchema;
|
|
44
|
+
try {
|
|
45
|
+
const schemaContent = await fs.readFile(schemaFile, 'utf-8');
|
|
46
|
+
const schema = JSON.parse(schemaContent);
|
|
47
|
+
inputSchema = schema.input;
|
|
48
|
+
outputSchema = schema.output;
|
|
49
|
+
errorSchema = schema.error;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Schema file is optional but recommended
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
name: manifest.name || path.basename(modulePath),
|
|
56
|
+
version: manifest.version || '1.0.0',
|
|
57
|
+
responsibility: manifest.responsibility || '',
|
|
58
|
+
excludes: manifest.excludes || [],
|
|
59
|
+
constraints: manifest.constraints,
|
|
60
|
+
tools: manifest.tools,
|
|
61
|
+
output: manifest.output,
|
|
62
|
+
failure: manifest.failure,
|
|
63
|
+
context: manifest.context,
|
|
64
|
+
prompt,
|
|
65
|
+
inputSchema,
|
|
66
|
+
outputSchema,
|
|
67
|
+
errorSchema,
|
|
68
|
+
location: modulePath,
|
|
69
|
+
format: 'v2',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load v1 format module (MODULE.md with frontmatter)
|
|
74
|
+
*/
|
|
75
|
+
async function loadModuleV1(modulePath) {
|
|
9
76
|
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
10
77
|
const schemaFile = path.join(modulePath, 'schema.json');
|
|
11
78
|
// Read MODULE.md
|
|
@@ -29,29 +96,66 @@ export async function loadModule(modulePath) {
|
|
|
29
96
|
catch {
|
|
30
97
|
// Schema file is optional
|
|
31
98
|
}
|
|
99
|
+
// Extract constraints from v1 format
|
|
100
|
+
const constraints = {};
|
|
101
|
+
const v1Constraints = frontmatter.constraints;
|
|
102
|
+
if (v1Constraints) {
|
|
103
|
+
constraints.no_network = v1Constraints.no_network;
|
|
104
|
+
constraints.no_side_effects = v1Constraints.no_side_effects;
|
|
105
|
+
constraints.no_inventing_data = v1Constraints.no_inventing_data;
|
|
106
|
+
}
|
|
32
107
|
return {
|
|
33
108
|
name: frontmatter.name || path.basename(modulePath),
|
|
34
109
|
version: frontmatter.version || '1.0.0',
|
|
35
110
|
responsibility: frontmatter.responsibility || '',
|
|
36
111
|
excludes: frontmatter.excludes || [],
|
|
112
|
+
constraints: Object.keys(constraints).length > 0 ? constraints : undefined,
|
|
37
113
|
context: frontmatter.context,
|
|
38
114
|
prompt,
|
|
39
115
|
inputSchema,
|
|
40
116
|
outputSchema,
|
|
41
117
|
location: modulePath,
|
|
118
|
+
format: 'v1',
|
|
42
119
|
};
|
|
43
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Load a Cognitive Module (auto-detects format)
|
|
123
|
+
*/
|
|
124
|
+
export async function loadModule(modulePath) {
|
|
125
|
+
const format = await detectFormat(modulePath);
|
|
126
|
+
if (format === 'v2') {
|
|
127
|
+
return loadModuleV2(modulePath);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
return loadModuleV1(modulePath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Check if a directory contains a valid module
|
|
135
|
+
*/
|
|
136
|
+
async function isValidModule(modulePath) {
|
|
137
|
+
const v2Manifest = path.join(modulePath, 'module.yaml');
|
|
138
|
+
const v1Module = path.join(modulePath, 'MODULE.md');
|
|
139
|
+
try {
|
|
140
|
+
await fs.access(v2Manifest);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
try {
|
|
145
|
+
await fs.access(v1Module);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
44
153
|
export async function findModule(name, searchPaths) {
|
|
45
154
|
for (const basePath of searchPaths) {
|
|
46
155
|
const modulePath = path.join(basePath, name);
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
await fs.access(moduleFile);
|
|
156
|
+
if (await isValidModule(modulePath)) {
|
|
50
157
|
return await loadModule(modulePath);
|
|
51
158
|
}
|
|
52
|
-
catch {
|
|
53
|
-
// Module not found in this path, continue
|
|
54
|
-
}
|
|
55
159
|
}
|
|
56
160
|
return null;
|
|
57
161
|
}
|
|
@@ -63,14 +167,14 @@ export async function listModules(searchPaths) {
|
|
|
63
167
|
for (const entry of entries) {
|
|
64
168
|
if (entry.isDirectory()) {
|
|
65
169
|
const modulePath = path.join(basePath, entry.name);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
170
|
+
if (await isValidModule(modulePath)) {
|
|
171
|
+
try {
|
|
172
|
+
const module = await loadModule(modulePath);
|
|
173
|
+
modules.push(module);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Skip invalid modules
|
|
177
|
+
}
|
|
74
178
|
}
|
|
75
179
|
}
|
|
76
180
|
}
|
package/dist/modules/runner.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
* v2: Clean input mapping, no CLI pollution
|
|
3
4
|
*/
|
|
4
|
-
import type { Provider, CognitiveModule, ModuleResult } from '../types.js';
|
|
5
|
+
import type { Provider, CognitiveModule, ModuleResult, ModuleInput } from '../types.js';
|
|
5
6
|
export interface RunOptions {
|
|
7
|
+
input?: ModuleInput;
|
|
6
8
|
args?: string;
|
|
7
|
-
input?: Record<string, unknown>;
|
|
8
|
-
validateInput?: boolean;
|
|
9
|
-
validateOutput?: boolean;
|
|
10
9
|
verbose?: boolean;
|
|
11
10
|
}
|
|
12
11
|
export declare function runModule(module: CognitiveModule, provider: Provider, options?: RunOptions): Promise<ModuleResult>;
|
package/dist/modules/runner.js
CHANGED
|
@@ -1,43 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
* v2: Clean input mapping, no CLI pollution
|
|
3
4
|
*/
|
|
4
5
|
export async function runModule(module, provider, options = {}) {
|
|
5
6
|
const { args, input, verbose = false } = options;
|
|
6
|
-
// Build input data
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
// Build clean input data (v2 style: no $ARGUMENTS pollution)
|
|
8
|
+
const inputData = input || {};
|
|
9
|
+
// Map legacy --args to clean input
|
|
10
|
+
if (args && !inputData.code && !inputData.query) {
|
|
11
|
+
// Determine if args looks like code or natural language
|
|
12
|
+
if (looksLikeCode(args)) {
|
|
13
|
+
inputData.code = args;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
inputData.query = args;
|
|
17
|
+
}
|
|
10
18
|
}
|
|
11
|
-
// Build prompt
|
|
19
|
+
// Build prompt with clean substitution
|
|
12
20
|
const prompt = buildPrompt(module, inputData);
|
|
13
21
|
if (verbose) {
|
|
22
|
+
console.error('--- Module ---');
|
|
23
|
+
console.error(`Name: ${module.name} (${module.format})`);
|
|
24
|
+
console.error(`Responsibility: ${module.responsibility}`);
|
|
25
|
+
console.error('--- Input ---');
|
|
26
|
+
console.error(JSON.stringify(inputData, null, 2));
|
|
14
27
|
console.error('--- Prompt ---');
|
|
15
28
|
console.error(prompt);
|
|
16
|
-
console.error('--- End
|
|
29
|
+
console.error('--- End ---');
|
|
30
|
+
}
|
|
31
|
+
// Build system message based on module config
|
|
32
|
+
const systemParts = [
|
|
33
|
+
`You are executing the "${module.name}" Cognitive Module.`,
|
|
34
|
+
'',
|
|
35
|
+
`RESPONSIBILITY: ${module.responsibility}`,
|
|
36
|
+
];
|
|
37
|
+
if (module.excludes.length > 0) {
|
|
38
|
+
systemParts.push('', 'YOU MUST NOT:');
|
|
39
|
+
module.excludes.forEach(e => systemParts.push(`- ${e}`));
|
|
40
|
+
}
|
|
41
|
+
if (module.constraints) {
|
|
42
|
+
systemParts.push('', 'CONSTRAINTS:');
|
|
43
|
+
if (module.constraints.no_network)
|
|
44
|
+
systemParts.push('- No network access');
|
|
45
|
+
if (module.constraints.no_side_effects)
|
|
46
|
+
systemParts.push('- No side effects');
|
|
47
|
+
if (module.constraints.no_file_write)
|
|
48
|
+
systemParts.push('- No file writes');
|
|
49
|
+
if (module.constraints.no_inventing_data)
|
|
50
|
+
systemParts.push('- Do not invent data');
|
|
51
|
+
}
|
|
52
|
+
if (module.output?.require_behavior_equivalence) {
|
|
53
|
+
systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
|
|
54
|
+
systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
|
|
55
|
+
systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
|
|
56
|
+
}
|
|
57
|
+
systemParts.push('', 'OUTPUT FORMAT:');
|
|
58
|
+
systemParts.push('- Respond with ONLY valid JSON');
|
|
59
|
+
systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
|
|
60
|
+
if (module.output?.require_behavior_equivalence) {
|
|
61
|
+
systemParts.push('- Include "behavior_equivalence" (boolean) field');
|
|
17
62
|
}
|
|
18
|
-
// Build messages
|
|
19
63
|
const messages = [
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
content: `You are executing the "${module.name}" Cognitive Module.
|
|
23
|
-
|
|
24
|
-
RESPONSIBILITY: ${module.responsibility}
|
|
25
|
-
|
|
26
|
-
YOU MUST NOT:
|
|
27
|
-
${module.excludes.map(e => `- ${e}`).join('\n')}
|
|
28
|
-
|
|
29
|
-
REQUIRED OUTPUT FORMAT:
|
|
30
|
-
You MUST respond with a valid JSON object. Include these fields:
|
|
31
|
-
- All fields required by the output schema
|
|
32
|
-
- "confidence": a number between 0 and 1
|
|
33
|
-
- "rationale": a string explaining your reasoning
|
|
34
|
-
|
|
35
|
-
Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
role: 'user',
|
|
39
|
-
content: prompt,
|
|
40
|
-
},
|
|
64
|
+
{ role: 'system', content: systemParts.join('\n') },
|
|
65
|
+
{ role: 'user', content: prompt },
|
|
41
66
|
];
|
|
42
67
|
// Invoke provider
|
|
43
68
|
const result = await provider.invoke({
|
|
@@ -53,7 +78,6 @@ Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
|
53
78
|
// Parse response
|
|
54
79
|
let output;
|
|
55
80
|
try {
|
|
56
|
-
// Try to extract JSON from markdown code blocks
|
|
57
81
|
const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
58
82
|
const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
|
|
59
83
|
output = JSON.parse(jsonStr.trim());
|
|
@@ -61,33 +85,65 @@ Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
|
61
85
|
catch {
|
|
62
86
|
throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
|
|
63
87
|
}
|
|
64
|
-
// Extract
|
|
88
|
+
// Extract standard fields
|
|
65
89
|
const outputObj = output;
|
|
66
90
|
const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
|
|
67
91
|
const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
|
|
92
|
+
const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
|
|
93
|
+
? outputObj.behavior_equivalence
|
|
94
|
+
: undefined;
|
|
68
95
|
return {
|
|
69
96
|
output,
|
|
70
97
|
confidence,
|
|
71
98
|
rationale,
|
|
99
|
+
behaviorEquivalence,
|
|
72
100
|
raw: result.content,
|
|
73
101
|
};
|
|
74
102
|
}
|
|
75
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Build prompt with clean variable substitution
|
|
105
|
+
*/
|
|
106
|
+
function buildPrompt(module, input) {
|
|
76
107
|
let prompt = module.prompt;
|
|
77
|
-
//
|
|
78
|
-
const
|
|
108
|
+
// v2 style: substitute ${variable} placeholders
|
|
109
|
+
for (const [key, value] of Object.entries(input)) {
|
|
110
|
+
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
111
|
+
prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
|
|
112
|
+
}
|
|
113
|
+
// v1 compatibility: substitute $ARGUMENTS
|
|
114
|
+
const argsValue = input.code || input.query || '';
|
|
79
115
|
prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
|
|
80
|
-
// Substitute $N placeholders
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
// Substitute $N placeholders (v1 compatibility)
|
|
117
|
+
if (typeof argsValue === 'string') {
|
|
118
|
+
const argsList = argsValue.split(/\s+/);
|
|
119
|
+
argsList.forEach((arg, i) => {
|
|
120
|
+
prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Append input summary if not already in prompt
|
|
124
|
+
if (!prompt.includes(argsValue) && argsValue) {
|
|
125
|
+
prompt += '\n\n## Input\n\n';
|
|
126
|
+
if (input.code) {
|
|
127
|
+
prompt += '```\n' + input.code + '\n```\n';
|
|
128
|
+
}
|
|
129
|
+
if (input.query) {
|
|
130
|
+
prompt += input.query + '\n';
|
|
131
|
+
}
|
|
132
|
+
if (input.language) {
|
|
133
|
+
prompt += `\nLanguage: ${input.language}\n`;
|
|
90
134
|
}
|
|
91
135
|
}
|
|
92
136
|
return prompt;
|
|
93
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Heuristic to detect if input looks like code
|
|
140
|
+
*/
|
|
141
|
+
function looksLikeCode(str) {
|
|
142
|
+
const codeIndicators = [
|
|
143
|
+
/^(def|function|class|const|let|var|import|export|public|private)\s/,
|
|
144
|
+
/[{};()]/,
|
|
145
|
+
/=>/,
|
|
146
|
+
/\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
|
|
147
|
+
];
|
|
148
|
+
return codeIndicators.some(re => re.test(str));
|
|
149
|
+
}
|
|
@@ -11,7 +11,7 @@ export { DeepSeekProvider } from './deepseek.js';
|
|
|
11
11
|
export { MoonshotProvider } from './moonshot.js';
|
|
12
12
|
export { QwenProvider } from './qwen.js';
|
|
13
13
|
export { OllamaProvider } from './ollama.js';
|
|
14
|
-
export declare function getProvider(name?: string): Provider;
|
|
14
|
+
export declare function getProvider(name?: string, model?: string): Provider;
|
|
15
15
|
export declare function listProviders(): Array<{
|
|
16
16
|
name: string;
|
|
17
17
|
configured: boolean;
|
package/dist/providers/index.js
CHANGED
|
@@ -19,44 +19,46 @@ export { MoonshotProvider } from './moonshot.js';
|
|
|
19
19
|
export { QwenProvider } from './qwen.js';
|
|
20
20
|
export { OllamaProvider } from './ollama.js';
|
|
21
21
|
const providers = {
|
|
22
|
-
gemini: () => new GeminiProvider(),
|
|
23
|
-
openai: () => new OpenAIProvider(),
|
|
24
|
-
anthropic: () => new AnthropicProvider(),
|
|
25
|
-
minimax: () => new MiniMaxProvider(),
|
|
26
|
-
deepseek: () => new DeepSeekProvider(),
|
|
27
|
-
moonshot: () => new MoonshotProvider(),
|
|
28
|
-
kimi: () => new MoonshotProvider(), // Alias
|
|
29
|
-
qwen: () => new QwenProvider(),
|
|
30
|
-
tongyi: () => new QwenProvider(), // Alias
|
|
31
|
-
dashscope: () => new QwenProvider(), // Alias
|
|
32
|
-
ollama: () => new OllamaProvider(),
|
|
33
|
-
local: () => new OllamaProvider(), // Alias
|
|
22
|
+
gemini: (model) => new GeminiProvider(undefined, model),
|
|
23
|
+
openai: (model) => new OpenAIProvider(undefined, model),
|
|
24
|
+
anthropic: (model) => new AnthropicProvider(undefined, model),
|
|
25
|
+
minimax: (model) => new MiniMaxProvider(undefined, model),
|
|
26
|
+
deepseek: (model) => new DeepSeekProvider(undefined, model),
|
|
27
|
+
moonshot: (model) => new MoonshotProvider(undefined, model),
|
|
28
|
+
kimi: (model) => new MoonshotProvider(undefined, model), // Alias
|
|
29
|
+
qwen: (model) => new QwenProvider(undefined, model),
|
|
30
|
+
tongyi: (model) => new QwenProvider(undefined, model), // Alias
|
|
31
|
+
dashscope: (model) => new QwenProvider(undefined, model), // Alias
|
|
32
|
+
ollama: (model) => new OllamaProvider(model),
|
|
33
|
+
local: (model) => new OllamaProvider(model), // Alias
|
|
34
34
|
};
|
|
35
|
-
export function getProvider(name) {
|
|
35
|
+
export function getProvider(name, model) {
|
|
36
|
+
// Check for model override from environment
|
|
37
|
+
const modelOverride = model || process.env.COG_MODEL;
|
|
36
38
|
// Auto-detect if not specified
|
|
37
39
|
if (!name) {
|
|
38
40
|
if (process.env.GEMINI_API_KEY)
|
|
39
|
-
return new GeminiProvider();
|
|
41
|
+
return new GeminiProvider(undefined, modelOverride);
|
|
40
42
|
if (process.env.OPENAI_API_KEY)
|
|
41
|
-
return new OpenAIProvider();
|
|
43
|
+
return new OpenAIProvider(undefined, modelOverride);
|
|
42
44
|
if (process.env.ANTHROPIC_API_KEY)
|
|
43
|
-
return new AnthropicProvider();
|
|
45
|
+
return new AnthropicProvider(undefined, modelOverride);
|
|
44
46
|
if (process.env.DEEPSEEK_API_KEY)
|
|
45
|
-
return new DeepSeekProvider();
|
|
47
|
+
return new DeepSeekProvider(undefined, modelOverride);
|
|
46
48
|
if (process.env.MINIMAX_API_KEY)
|
|
47
|
-
return new MiniMaxProvider();
|
|
49
|
+
return new MiniMaxProvider(undefined, modelOverride);
|
|
48
50
|
if (process.env.MOONSHOT_API_KEY)
|
|
49
|
-
return new MoonshotProvider();
|
|
51
|
+
return new MoonshotProvider(undefined, modelOverride);
|
|
50
52
|
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY)
|
|
51
|
-
return new QwenProvider();
|
|
53
|
+
return new QwenProvider(undefined, modelOverride);
|
|
52
54
|
// Ollama is always available as fallback if nothing else is configured
|
|
53
|
-
return new OllamaProvider();
|
|
55
|
+
return new OllamaProvider(modelOverride);
|
|
54
56
|
}
|
|
55
57
|
const factory = providers[name.toLowerCase()];
|
|
56
58
|
if (!factory) {
|
|
57
59
|
throw new Error(`Unknown provider: ${name}. Available: ${Object.keys(providers).join(', ')}`);
|
|
58
60
|
}
|
|
59
|
-
return factory();
|
|
61
|
+
return factory(modelOverride);
|
|
60
62
|
}
|
|
61
63
|
export function listProviders() {
|
|
62
64
|
return [
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cognitive Runtime - Core Types
|
|
3
|
+
* Version 2.0 - With tools policy, failure contract, and behavior equivalence
|
|
3
4
|
*/
|
|
4
5
|
export interface Provider {
|
|
5
6
|
name: string;
|
|
@@ -29,16 +30,41 @@ export interface CognitiveModule {
|
|
|
29
30
|
version: string;
|
|
30
31
|
responsibility: string;
|
|
31
32
|
excludes: string[];
|
|
33
|
+
constraints?: ModuleConstraints;
|
|
34
|
+
tools?: ToolsPolicy;
|
|
35
|
+
output?: OutputContract;
|
|
36
|
+
failure?: FailureContract;
|
|
32
37
|
context?: 'fork' | 'main';
|
|
33
38
|
prompt: string;
|
|
34
39
|
inputSchema?: object;
|
|
35
40
|
outputSchema?: object;
|
|
41
|
+
errorSchema?: object;
|
|
36
42
|
location: string;
|
|
43
|
+
format: 'v1' | 'v2';
|
|
44
|
+
}
|
|
45
|
+
export interface ModuleConstraints {
|
|
46
|
+
no_network?: boolean;
|
|
47
|
+
no_side_effects?: boolean;
|
|
48
|
+
no_file_write?: boolean;
|
|
49
|
+
no_inventing_data?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ToolsPolicy {
|
|
52
|
+
allowed: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface OutputContract {
|
|
55
|
+
mode?: 'json_strict' | 'json_lenient' | 'text';
|
|
56
|
+
require_confidence?: boolean;
|
|
57
|
+
require_rationale?: boolean;
|
|
58
|
+
require_behavior_equivalence?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface FailureContract {
|
|
61
|
+
schema?: object;
|
|
37
62
|
}
|
|
38
63
|
export interface ModuleResult {
|
|
39
64
|
output: unknown;
|
|
40
65
|
confidence: number;
|
|
41
66
|
rationale: string;
|
|
67
|
+
behaviorEquivalence?: boolean;
|
|
42
68
|
raw?: string;
|
|
43
69
|
}
|
|
44
70
|
export interface CommandContext {
|
|
@@ -51,3 +77,10 @@ export interface CommandResult {
|
|
|
51
77
|
data?: unknown;
|
|
52
78
|
error?: string;
|
|
53
79
|
}
|
|
80
|
+
export interface ModuleInput {
|
|
81
|
+
code?: string;
|
|
82
|
+
query?: string;
|
|
83
|
+
language?: string;
|
|
84
|
+
options?: Record<string, unknown>;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { getProvider, listProviders } from './providers/index.js';
|
|
|
13
13
|
import { run, list, pipe, init } from './commands/index.js';
|
|
14
14
|
import type { CommandContext } from './types.js';
|
|
15
15
|
|
|
16
|
-
const VERSION = '0.
|
|
16
|
+
const VERSION = '0.4.0';
|
|
17
17
|
|
|
18
18
|
async function main() {
|
|
19
19
|
const args = process.argv.slice(2);
|
|
@@ -36,6 +36,7 @@ async function main() {
|
|
|
36
36
|
args: { type: 'string', short: 'a' },
|
|
37
37
|
input: { type: 'string', short: 'i' },
|
|
38
38
|
module: { type: 'string', short: 'm' },
|
|
39
|
+
model: { type: 'string', short: 'M' },
|
|
39
40
|
provider: { type: 'string', short: 'p' },
|
|
40
41
|
pretty: { type: 'boolean', default: false },
|
|
41
42
|
verbose: { type: 'boolean', short: 'V', default: false },
|
|
@@ -47,7 +48,7 @@ async function main() {
|
|
|
47
48
|
// Get provider
|
|
48
49
|
let provider;
|
|
49
50
|
try {
|
|
50
|
-
provider = getProvider(values.provider);
|
|
51
|
+
provider = getProvider(values.provider, values.model);
|
|
51
52
|
} catch (e) {
|
|
52
53
|
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
53
54
|
process.exit(1);
|
|
@@ -196,7 +197,8 @@ OPTIONS:
|
|
|
196
197
|
-a, --args <str> Arguments to pass to module
|
|
197
198
|
-i, --input <json> JSON input for module
|
|
198
199
|
-m, --module <name> Module name (for pipe)
|
|
199
|
-
-
|
|
200
|
+
-M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
|
|
201
|
+
-p, --provider <name> LLM provider (gemini, openai, anthropic, deepseek, minimax, moonshot, qwen, ollama)
|
|
200
202
|
--pretty Pretty-print JSON output
|
|
201
203
|
-V, --verbose Verbose output
|
|
202
204
|
--no-validate Skip schema validation
|
|
@@ -205,6 +207,7 @@ OPTIONS:
|
|
|
205
207
|
|
|
206
208
|
EXAMPLES:
|
|
207
209
|
cog run code-reviewer --args "def foo(): pass"
|
|
210
|
+
cog run code-reviewer --provider openai --model gpt-4o --args "..."
|
|
208
211
|
cog list
|
|
209
212
|
echo "review this code" | cog pipe --module code-reviewer
|
|
210
213
|
cog init my-module
|
|
@@ -219,6 +222,7 @@ ENVIRONMENT:
|
|
|
219
222
|
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
220
223
|
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
221
224
|
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
225
|
+
COG_MODEL Override default model for any provider
|
|
222
226
|
`);
|
|
223
227
|
}
|
|
224
228
|
|
package/src/commands/pipe.ts
CHANGED
|
@@ -52,8 +52,6 @@ export async function pipe(
|
|
|
52
52
|
const result = await runModule(module, ctx.provider, {
|
|
53
53
|
args: inputData ? undefined : input,
|
|
54
54
|
input: inputData,
|
|
55
|
-
validateInput: !options.noValidate,
|
|
56
|
-
validateOutput: !options.noValidate,
|
|
57
55
|
});
|
|
58
56
|
|
|
59
57
|
// Output JSON to stdout
|
package/src/commands/run.ts
CHANGED
|
@@ -47,8 +47,6 @@ export async function run(
|
|
|
47
47
|
const result = await runModule(module, ctx.provider, {
|
|
48
48
|
args: options.args,
|
|
49
49
|
input: inputData,
|
|
50
|
-
validateInput: !options.noValidate,
|
|
51
|
-
validateOutput: !options.noValidate,
|
|
52
50
|
verbose: options.verbose || ctx.verbose,
|
|
53
51
|
});
|
|
54
52
|
|
package/src/index.ts
CHANGED
package/src/modules/loader.ts
CHANGED
|
@@ -1,15 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Loader - Load and parse Cognitive Modules
|
|
3
|
+
* Supports both v1 (MODULE.md) and v2 (module.yaml + prompt.md) formats
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import * as fs from 'node:fs/promises';
|
|
6
7
|
import * as path from 'node:path';
|
|
7
8
|
import yaml from 'js-yaml';
|
|
8
|
-
import type { CognitiveModule } from '../types.js';
|
|
9
|
+
import type { CognitiveModule, ModuleConstraints, ToolsPolicy, OutputContract, FailureContract } from '../types.js';
|
|
9
10
|
|
|
10
11
|
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Detect module format version
|
|
15
|
+
*/
|
|
16
|
+
async function detectFormat(modulePath: string): Promise<'v1' | 'v2'> {
|
|
17
|
+
const v2Manifest = path.join(modulePath, 'module.yaml');
|
|
18
|
+
try {
|
|
19
|
+
await fs.access(v2Manifest);
|
|
20
|
+
return 'v2';
|
|
21
|
+
} catch {
|
|
22
|
+
return 'v1';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load v2 format module (module.yaml + prompt.md)
|
|
28
|
+
*/
|
|
29
|
+
async function loadModuleV2(modulePath: string): Promise<CognitiveModule> {
|
|
30
|
+
const manifestFile = path.join(modulePath, 'module.yaml');
|
|
31
|
+
const promptFile = path.join(modulePath, 'prompt.md');
|
|
32
|
+
const schemaFile = path.join(modulePath, 'schema.json');
|
|
33
|
+
|
|
34
|
+
// Read module.yaml
|
|
35
|
+
const manifestContent = await fs.readFile(manifestFile, 'utf-8');
|
|
36
|
+
const manifest = yaml.load(manifestContent) as Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
// Read prompt.md
|
|
39
|
+
let prompt = '';
|
|
40
|
+
try {
|
|
41
|
+
prompt = await fs.readFile(promptFile, 'utf-8');
|
|
42
|
+
} catch {
|
|
43
|
+
// prompt.md is optional, manifest may include inline prompt
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Read schema.json
|
|
47
|
+
let inputSchema: object | undefined;
|
|
48
|
+
let outputSchema: object | undefined;
|
|
49
|
+
let errorSchema: object | undefined;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const schemaContent = await fs.readFile(schemaFile, 'utf-8');
|
|
53
|
+
const schema = JSON.parse(schemaContent);
|
|
54
|
+
inputSchema = schema.input;
|
|
55
|
+
outputSchema = schema.output;
|
|
56
|
+
errorSchema = schema.error;
|
|
57
|
+
} catch {
|
|
58
|
+
// Schema file is optional but recommended
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
name: manifest.name as string || path.basename(modulePath),
|
|
63
|
+
version: manifest.version as string || '1.0.0',
|
|
64
|
+
responsibility: manifest.responsibility as string || '',
|
|
65
|
+
excludes: (manifest.excludes as string[]) || [],
|
|
66
|
+
constraints: manifest.constraints as ModuleConstraints | undefined,
|
|
67
|
+
tools: manifest.tools as ToolsPolicy | undefined,
|
|
68
|
+
output: manifest.output as OutputContract | undefined,
|
|
69
|
+
failure: manifest.failure as FailureContract | undefined,
|
|
70
|
+
context: manifest.context as 'fork' | 'main' | undefined,
|
|
71
|
+
prompt,
|
|
72
|
+
inputSchema,
|
|
73
|
+
outputSchema,
|
|
74
|
+
errorSchema,
|
|
75
|
+
location: modulePath,
|
|
76
|
+
format: 'v2',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Load v1 format module (MODULE.md with frontmatter)
|
|
82
|
+
*/
|
|
83
|
+
async function loadModuleV1(modulePath: string): Promise<CognitiveModule> {
|
|
13
84
|
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
14
85
|
const schemaFile = path.join(modulePath, 'schema.json');
|
|
15
86
|
|
|
@@ -38,29 +109,69 @@ export async function loadModule(modulePath: string): Promise<CognitiveModule> {
|
|
|
38
109
|
// Schema file is optional
|
|
39
110
|
}
|
|
40
111
|
|
|
112
|
+
// Extract constraints from v1 format
|
|
113
|
+
const constraints: ModuleConstraints = {};
|
|
114
|
+
const v1Constraints = frontmatter.constraints as Record<string, boolean> | undefined;
|
|
115
|
+
if (v1Constraints) {
|
|
116
|
+
constraints.no_network = v1Constraints.no_network;
|
|
117
|
+
constraints.no_side_effects = v1Constraints.no_side_effects;
|
|
118
|
+
constraints.no_inventing_data = v1Constraints.no_inventing_data;
|
|
119
|
+
}
|
|
120
|
+
|
|
41
121
|
return {
|
|
42
122
|
name: frontmatter.name as string || path.basename(modulePath),
|
|
43
123
|
version: frontmatter.version as string || '1.0.0',
|
|
44
124
|
responsibility: frontmatter.responsibility as string || '',
|
|
45
125
|
excludes: (frontmatter.excludes as string[]) || [],
|
|
126
|
+
constraints: Object.keys(constraints).length > 0 ? constraints : undefined,
|
|
46
127
|
context: frontmatter.context as 'fork' | 'main' | undefined,
|
|
47
128
|
prompt,
|
|
48
129
|
inputSchema,
|
|
49
130
|
outputSchema,
|
|
50
131
|
location: modulePath,
|
|
132
|
+
format: 'v1',
|
|
51
133
|
};
|
|
52
134
|
}
|
|
53
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Load a Cognitive Module (auto-detects format)
|
|
138
|
+
*/
|
|
139
|
+
export async function loadModule(modulePath: string): Promise<CognitiveModule> {
|
|
140
|
+
const format = await detectFormat(modulePath);
|
|
141
|
+
|
|
142
|
+
if (format === 'v2') {
|
|
143
|
+
return loadModuleV2(modulePath);
|
|
144
|
+
} else {
|
|
145
|
+
return loadModuleV1(modulePath);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if a directory contains a valid module
|
|
151
|
+
*/
|
|
152
|
+
async function isValidModule(modulePath: string): Promise<boolean> {
|
|
153
|
+
const v2Manifest = path.join(modulePath, 'module.yaml');
|
|
154
|
+
const v1Module = path.join(modulePath, 'MODULE.md');
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
await fs.access(v2Manifest);
|
|
158
|
+
return true;
|
|
159
|
+
} catch {
|
|
160
|
+
try {
|
|
161
|
+
await fs.access(v1Module);
|
|
162
|
+
return true;
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
54
169
|
export async function findModule(name: string, searchPaths: string[]): Promise<CognitiveModule | null> {
|
|
55
170
|
for (const basePath of searchPaths) {
|
|
56
171
|
const modulePath = path.join(basePath, name);
|
|
57
|
-
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
58
172
|
|
|
59
|
-
|
|
60
|
-
await fs.access(moduleFile);
|
|
173
|
+
if (await isValidModule(modulePath)) {
|
|
61
174
|
return await loadModule(modulePath);
|
|
62
|
-
} catch {
|
|
63
|
-
// Module not found in this path, continue
|
|
64
175
|
}
|
|
65
176
|
}
|
|
66
177
|
|
|
@@ -77,14 +188,14 @@ export async function listModules(searchPaths: string[]): Promise<CognitiveModul
|
|
|
77
188
|
for (const entry of entries) {
|
|
78
189
|
if (entry.isDirectory()) {
|
|
79
190
|
const modulePath = path.join(basePath, entry.name);
|
|
80
|
-
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
81
191
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
192
|
+
if (await isValidModule(modulePath)) {
|
|
193
|
+
try {
|
|
194
|
+
const module = await loadModule(modulePath);
|
|
195
|
+
modules.push(module);
|
|
196
|
+
} catch {
|
|
197
|
+
// Skip invalid modules
|
|
198
|
+
}
|
|
88
199
|
}
|
|
89
200
|
}
|
|
90
201
|
}
|
package/src/modules/runner.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
* v2: Clean input mapping, no CLI pollution
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
import type { Provider, CognitiveModule, ModuleResult, Message } from '../types.js';
|
|
6
|
+
import type { Provider, CognitiveModule, ModuleResult, Message, ModuleInput } from '../types.js';
|
|
6
7
|
|
|
7
8
|
export interface RunOptions {
|
|
9
|
+
// Clean input (v2 style)
|
|
10
|
+
input?: ModuleInput;
|
|
11
|
+
|
|
12
|
+
// Legacy CLI args (v1 compatibility) - mapped to input.code or input.query
|
|
8
13
|
args?: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
validateOutput?: boolean;
|
|
14
|
+
|
|
15
|
+
// Runtime options
|
|
12
16
|
verbose?: boolean;
|
|
13
17
|
}
|
|
14
18
|
|
|
@@ -19,44 +23,69 @@ export async function runModule(
|
|
|
19
23
|
): Promise<ModuleResult> {
|
|
20
24
|
const { args, input, verbose = false } = options;
|
|
21
25
|
|
|
22
|
-
// Build input data
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
// Build clean input data (v2 style: no $ARGUMENTS pollution)
|
|
27
|
+
const inputData: ModuleInput = input || {};
|
|
28
|
+
|
|
29
|
+
// Map legacy --args to clean input
|
|
30
|
+
if (args && !inputData.code && !inputData.query) {
|
|
31
|
+
// Determine if args looks like code or natural language
|
|
32
|
+
if (looksLikeCode(args)) {
|
|
33
|
+
inputData.code = args;
|
|
34
|
+
} else {
|
|
35
|
+
inputData.query = args;
|
|
36
|
+
}
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
// Build prompt
|
|
39
|
+
// Build prompt with clean substitution
|
|
29
40
|
const prompt = buildPrompt(module, inputData);
|
|
30
41
|
|
|
31
42
|
if (verbose) {
|
|
43
|
+
console.error('--- Module ---');
|
|
44
|
+
console.error(`Name: ${module.name} (${module.format})`);
|
|
45
|
+
console.error(`Responsibility: ${module.responsibility}`);
|
|
46
|
+
console.error('--- Input ---');
|
|
47
|
+
console.error(JSON.stringify(inputData, null, 2));
|
|
32
48
|
console.error('--- Prompt ---');
|
|
33
49
|
console.error(prompt);
|
|
34
|
-
console.error('--- End
|
|
50
|
+
console.error('--- End ---');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build system message based on module config
|
|
54
|
+
const systemParts: string[] = [
|
|
55
|
+
`You are executing the "${module.name}" Cognitive Module.`,
|
|
56
|
+
'',
|
|
57
|
+
`RESPONSIBILITY: ${module.responsibility}`,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
if (module.excludes.length > 0) {
|
|
61
|
+
systemParts.push('', 'YOU MUST NOT:');
|
|
62
|
+
module.excludes.forEach(e => systemParts.push(`- ${e}`));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (module.constraints) {
|
|
66
|
+
systemParts.push('', 'CONSTRAINTS:');
|
|
67
|
+
if (module.constraints.no_network) systemParts.push('- No network access');
|
|
68
|
+
if (module.constraints.no_side_effects) systemParts.push('- No side effects');
|
|
69
|
+
if (module.constraints.no_file_write) systemParts.push('- No file writes');
|
|
70
|
+
if (module.constraints.no_inventing_data) systemParts.push('- Do not invent data');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (module.output?.require_behavior_equivalence) {
|
|
74
|
+
systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
|
|
75
|
+
systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
|
|
76
|
+
systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
systemParts.push('', 'OUTPUT FORMAT:');
|
|
80
|
+
systemParts.push('- Respond with ONLY valid JSON');
|
|
81
|
+
systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
|
|
82
|
+
if (module.output?.require_behavior_equivalence) {
|
|
83
|
+
systemParts.push('- Include "behavior_equivalence" (boolean) field');
|
|
35
84
|
}
|
|
36
85
|
|
|
37
|
-
// Build messages
|
|
38
86
|
const messages: Message[] = [
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
content: `You are executing the "${module.name}" Cognitive Module.
|
|
42
|
-
|
|
43
|
-
RESPONSIBILITY: ${module.responsibility}
|
|
44
|
-
|
|
45
|
-
YOU MUST NOT:
|
|
46
|
-
${module.excludes.map(e => `- ${e}`).join('\n')}
|
|
47
|
-
|
|
48
|
-
REQUIRED OUTPUT FORMAT:
|
|
49
|
-
You MUST respond with a valid JSON object. Include these fields:
|
|
50
|
-
- All fields required by the output schema
|
|
51
|
-
- "confidence": a number between 0 and 1
|
|
52
|
-
- "rationale": a string explaining your reasoning
|
|
53
|
-
|
|
54
|
-
Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
role: 'user',
|
|
58
|
-
content: prompt,
|
|
59
|
-
},
|
|
87
|
+
{ role: 'system', content: systemParts.join('\n') },
|
|
88
|
+
{ role: 'user', content: prompt },
|
|
60
89
|
];
|
|
61
90
|
|
|
62
91
|
// Invoke provider
|
|
@@ -75,7 +104,6 @@ Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
|
75
104
|
// Parse response
|
|
76
105
|
let output: unknown;
|
|
77
106
|
try {
|
|
78
|
-
// Try to extract JSON from markdown code blocks
|
|
79
107
|
const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
80
108
|
const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
|
|
81
109
|
output = JSON.parse(jsonStr.trim());
|
|
@@ -83,39 +111,73 @@ Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
|
83
111
|
throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
|
|
84
112
|
}
|
|
85
113
|
|
|
86
|
-
// Extract
|
|
114
|
+
// Extract standard fields
|
|
87
115
|
const outputObj = output as Record<string, unknown>;
|
|
88
116
|
const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
|
|
89
117
|
const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
|
|
118
|
+
const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
|
|
119
|
+
? outputObj.behavior_equivalence
|
|
120
|
+
: undefined;
|
|
90
121
|
|
|
91
122
|
return {
|
|
92
123
|
output,
|
|
93
124
|
confidence,
|
|
94
125
|
rationale,
|
|
126
|
+
behaviorEquivalence,
|
|
95
127
|
raw: result.content,
|
|
96
128
|
};
|
|
97
129
|
}
|
|
98
130
|
|
|
99
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Build prompt with clean variable substitution
|
|
133
|
+
*/
|
|
134
|
+
function buildPrompt(module: CognitiveModule, input: ModuleInput): string {
|
|
100
135
|
let prompt = module.prompt;
|
|
101
136
|
|
|
102
|
-
//
|
|
103
|
-
const
|
|
137
|
+
// v2 style: substitute ${variable} placeholders
|
|
138
|
+
for (const [key, value] of Object.entries(input)) {
|
|
139
|
+
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
140
|
+
prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// v1 compatibility: substitute $ARGUMENTS
|
|
144
|
+
const argsValue = input.code || input.query || '';
|
|
104
145
|
prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
|
|
105
146
|
|
|
106
|
-
// Substitute $N placeholders
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
147
|
+
// Substitute $N placeholders (v1 compatibility)
|
|
148
|
+
if (typeof argsValue === 'string') {
|
|
149
|
+
const argsList = argsValue.split(/\s+/);
|
|
150
|
+
argsList.forEach((arg, i) => {
|
|
151
|
+
prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
112
154
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
155
|
+
// Append input summary if not already in prompt
|
|
156
|
+
if (!prompt.includes(argsValue) && argsValue) {
|
|
157
|
+
prompt += '\n\n## Input\n\n';
|
|
158
|
+
if (input.code) {
|
|
159
|
+
prompt += '```\n' + input.code + '\n```\n';
|
|
160
|
+
}
|
|
161
|
+
if (input.query) {
|
|
162
|
+
prompt += input.query + '\n';
|
|
163
|
+
}
|
|
164
|
+
if (input.language) {
|
|
165
|
+
prompt += `\nLanguage: ${input.language}\n`;
|
|
117
166
|
}
|
|
118
167
|
}
|
|
119
168
|
|
|
120
169
|
return prompt;
|
|
121
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Heuristic to detect if input looks like code
|
|
174
|
+
*/
|
|
175
|
+
function looksLikeCode(str: string): boolean {
|
|
176
|
+
const codeIndicators = [
|
|
177
|
+
/^(def|function|class|const|let|var|import|export|public|private)\s/,
|
|
178
|
+
/[{};()]/,
|
|
179
|
+
/=>/,
|
|
180
|
+
/\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
|
|
181
|
+
];
|
|
182
|
+
return codeIndicators.some(re => re.test(str));
|
|
183
|
+
}
|
package/src/providers/index.ts
CHANGED
|
@@ -22,33 +22,38 @@ export { MoonshotProvider } from './moonshot.js';
|
|
|
22
22
|
export { QwenProvider } from './qwen.js';
|
|
23
23
|
export { OllamaProvider } from './ollama.js';
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
type ProviderFactory = (model?: string) => Provider;
|
|
26
|
+
|
|
27
|
+
const providers: Record<string, ProviderFactory> = {
|
|
28
|
+
gemini: (model) => new GeminiProvider(undefined, model),
|
|
29
|
+
openai: (model) => new OpenAIProvider(undefined, model),
|
|
30
|
+
anthropic: (model) => new AnthropicProvider(undefined, model),
|
|
31
|
+
minimax: (model) => new MiniMaxProvider(undefined, model),
|
|
32
|
+
deepseek: (model) => new DeepSeekProvider(undefined, model),
|
|
33
|
+
moonshot: (model) => new MoonshotProvider(undefined, model),
|
|
34
|
+
kimi: (model) => new MoonshotProvider(undefined, model), // Alias
|
|
35
|
+
qwen: (model) => new QwenProvider(undefined, model),
|
|
36
|
+
tongyi: (model) => new QwenProvider(undefined, model), // Alias
|
|
37
|
+
dashscope: (model) => new QwenProvider(undefined, model), // Alias
|
|
38
|
+
ollama: (model) => new OllamaProvider(model),
|
|
39
|
+
local: (model) => new OllamaProvider(model), // Alias
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
export function getProvider(name?: string): Provider {
|
|
42
|
+
export function getProvider(name?: string, model?: string): Provider {
|
|
43
|
+
// Check for model override from environment
|
|
44
|
+
const modelOverride = model || process.env.COG_MODEL;
|
|
45
|
+
|
|
41
46
|
// Auto-detect if not specified
|
|
42
47
|
if (!name) {
|
|
43
|
-
if (process.env.GEMINI_API_KEY) return new GeminiProvider();
|
|
44
|
-
if (process.env.OPENAI_API_KEY) return new OpenAIProvider();
|
|
45
|
-
if (process.env.ANTHROPIC_API_KEY) return new AnthropicProvider();
|
|
46
|
-
if (process.env.DEEPSEEK_API_KEY) return new DeepSeekProvider();
|
|
47
|
-
if (process.env.MINIMAX_API_KEY) return new MiniMaxProvider();
|
|
48
|
-
if (process.env.MOONSHOT_API_KEY) return new MoonshotProvider();
|
|
49
|
-
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY) return new QwenProvider();
|
|
48
|
+
if (process.env.GEMINI_API_KEY) return new GeminiProvider(undefined, modelOverride);
|
|
49
|
+
if (process.env.OPENAI_API_KEY) return new OpenAIProvider(undefined, modelOverride);
|
|
50
|
+
if (process.env.ANTHROPIC_API_KEY) return new AnthropicProvider(undefined, modelOverride);
|
|
51
|
+
if (process.env.DEEPSEEK_API_KEY) return new DeepSeekProvider(undefined, modelOverride);
|
|
52
|
+
if (process.env.MINIMAX_API_KEY) return new MiniMaxProvider(undefined, modelOverride);
|
|
53
|
+
if (process.env.MOONSHOT_API_KEY) return new MoonshotProvider(undefined, modelOverride);
|
|
54
|
+
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY) return new QwenProvider(undefined, modelOverride);
|
|
50
55
|
// Ollama is always available as fallback if nothing else is configured
|
|
51
|
-
return new OllamaProvider();
|
|
56
|
+
return new OllamaProvider(modelOverride);
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
const factory = providers[name.toLowerCase()];
|
|
@@ -56,7 +61,7 @@ export function getProvider(name?: string): Provider {
|
|
|
56
61
|
throw new Error(`Unknown provider: ${name}. Available: ${Object.keys(providers).join(', ')}`);
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
return factory();
|
|
64
|
+
return factory(modelOverride);
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
export function listProviders(): Array<{ name: string; configured: boolean; model: string }> {
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cognitive Runtime - Core Types
|
|
3
|
+
* Version 2.0 - With tools policy, failure contract, and behavior equivalence
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
// Provider interface - all LLM providers implement this
|
|
@@ -30,23 +31,69 @@ export interface InvokeResult {
|
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
// Module types
|
|
34
|
+
// Module types (v2)
|
|
34
35
|
export interface CognitiveModule {
|
|
36
|
+
// Core identity
|
|
35
37
|
name: string;
|
|
36
38
|
version: string;
|
|
37
39
|
responsibility: string;
|
|
40
|
+
|
|
41
|
+
// Constraints
|
|
38
42
|
excludes: string[];
|
|
43
|
+
constraints?: ModuleConstraints;
|
|
44
|
+
|
|
45
|
+
// Tools policy
|
|
46
|
+
tools?: ToolsPolicy;
|
|
47
|
+
|
|
48
|
+
// Output contract
|
|
49
|
+
output?: OutputContract;
|
|
50
|
+
|
|
51
|
+
// Failure contract
|
|
52
|
+
failure?: FailureContract;
|
|
53
|
+
|
|
54
|
+
// Execution context
|
|
39
55
|
context?: 'fork' | 'main';
|
|
56
|
+
|
|
57
|
+
// Prompt (from prompt.md or MODULE.md body)
|
|
40
58
|
prompt: string;
|
|
59
|
+
|
|
60
|
+
// Schemas
|
|
41
61
|
inputSchema?: object;
|
|
42
62
|
outputSchema?: object;
|
|
63
|
+
errorSchema?: object;
|
|
64
|
+
|
|
65
|
+
// Metadata
|
|
43
66
|
location: string;
|
|
67
|
+
format: 'v1' | 'v2'; // v1 = MODULE.md, v2 = module.yaml + prompt.md
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ModuleConstraints {
|
|
71
|
+
no_network?: boolean;
|
|
72
|
+
no_side_effects?: boolean;
|
|
73
|
+
no_file_write?: boolean;
|
|
74
|
+
no_inventing_data?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ToolsPolicy {
|
|
78
|
+
allowed: string[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface OutputContract {
|
|
82
|
+
mode?: 'json_strict' | 'json_lenient' | 'text';
|
|
83
|
+
require_confidence?: boolean;
|
|
84
|
+
require_rationale?: boolean;
|
|
85
|
+
require_behavior_equivalence?: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface FailureContract {
|
|
89
|
+
schema?: object;
|
|
44
90
|
}
|
|
45
91
|
|
|
46
92
|
export interface ModuleResult {
|
|
47
93
|
output: unknown;
|
|
48
94
|
confidence: number;
|
|
49
95
|
rationale: string;
|
|
96
|
+
behaviorEquivalence?: boolean;
|
|
50
97
|
raw?: string;
|
|
51
98
|
}
|
|
52
99
|
|
|
@@ -62,3 +109,12 @@ export interface CommandResult {
|
|
|
62
109
|
data?: unknown;
|
|
63
110
|
error?: string;
|
|
64
111
|
}
|
|
112
|
+
|
|
113
|
+
// Module input (clean, no CLI pollution)
|
|
114
|
+
export interface ModuleInput {
|
|
115
|
+
code?: string;
|
|
116
|
+
query?: string;
|
|
117
|
+
language?: string;
|
|
118
|
+
options?: Record<string, unknown>;
|
|
119
|
+
[key: string]: unknown;
|
|
120
|
+
}
|