cognitive-modules-cli 2.2.13 → 2.2.15
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 +16 -3
- package/README.md +77 -43
- package/dist/cli.js +43 -14
- package/dist/modules/runner.js +128 -16
- package/dist/modules/subagent.d.ts +5 -1
- package/dist/modules/subagent.js +44 -6
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +15 -22
- package/dist/providers/index.d.ts +5 -1
- package/dist/providers/index.js +37 -20
- package/dist/providers/minimax.js +3 -1
- package/dist/types.js +11 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented in this file.
|
|
4
4
|
|
|
5
|
-
## 2.2.
|
|
5
|
+
## 2.2.15 - 2026-03-09
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- Runtime: add schema-guided output canonicalization so enum labels and unordered arrays can be normalized before validation/repair.
|
|
8
|
+
- Benchmarks: tighten gate/extraction contracts around canonical labels and publish `Benchmark Evidence` showing Gemini + MiniMax contract stability.
|
|
9
|
+
- Use case: add the official `pr-risk-gate` module plus a copy-paste GitHub Actions template for blocking risky PRs in CI.
|
|
10
|
+
|
|
11
|
+
## 2.2.14 - 2026-03-08
|
|
12
|
+
|
|
13
|
+
- Release: add docs build to `release:check`, so docs regressions fail before npm publish.
|
|
14
|
+
- Hardening: `packages/cogn` now runs the primary runtime release gate before alias-package publish checks.
|
|
15
|
+
- Providers: preserve Anthropic streaming input/output token accounting correctly.
|
|
16
|
+
- Test coverage: add request-shaping tests for the full stable provider set (`openai`, `anthropic`, `gemini`, `minimax`, `deepseek`, `qwen`).
|
|
9
17
|
|
|
10
18
|
## 2.2.13 - 2026-02-08
|
|
11
19
|
|
|
12
20
|
- Fix: do not repair/convert successful envelopes into error envelopes when output validation is disabled (`--profile core`/`validate=off`).
|
|
13
21
|
|
|
22
|
+
## 2.2.12 - 2026-02-08
|
|
23
|
+
|
|
24
|
+
- Conformance: add `runtime` suite (offline, deterministic vectors) to validate publish-grade JSON parsing and profile gates.
|
|
25
|
+
- UX: conformance help/usage now documents `--suite runtime` explicitly.
|
|
26
|
+
|
|
14
27
|
## 2.2.11 - 2026-02-07
|
|
15
28
|
|
|
16
29
|
- Fix: Gemini `responseSchema` compatibility by dropping non-string `enum`/`const` constraints (Gemini rejects boolean enums).
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/cognitive-modules-cli)
|
|
4
4
|
|
|
5
|
-
Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入口 `npx cogn@2.2.
|
|
5
|
+
Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入口 `npx cogn@2.2.15 ...`,避免 PATH/命令冲突。
|
|
6
6
|
|
|
7
7
|
> 这是 [cognitive-modules](../../README.md) monorepo 的一部分。
|
|
8
8
|
|
|
@@ -10,11 +10,11 @@ Node.js/TypeScript 版本的 Cognitive Modules CLI。文档统一使用明确入
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# 零安装(推荐)
|
|
13
|
-
npx cogn@2.2.
|
|
13
|
+
npx cogn@2.2.15 --help
|
|
14
14
|
|
|
15
15
|
# 全局安装(可选)
|
|
16
|
-
npm install -g cogn@2.2.
|
|
17
|
-
# 或:npm install -g cognitive-modules-cli@2.2.
|
|
16
|
+
npm install -g cogn@2.2.15
|
|
17
|
+
# 或:npm install -g cognitive-modules-cli@2.2.15
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## 快速开始
|
|
@@ -24,43 +24,56 @@ npm install -g cogn@2.2.13
|
|
|
24
24
|
export OPENAI_API_KEY=sk-xxx
|
|
25
25
|
|
|
26
26
|
# 查看 providers 能力矩阵(结构化输出/流式)
|
|
27
|
-
npx cogn@2.2.
|
|
27
|
+
npx cogn@2.2.15 providers --pretty
|
|
28
28
|
|
|
29
29
|
# 运行模块
|
|
30
|
-
npx cogn@2.2.
|
|
30
|
+
npx cogn@2.2.15 run code-reviewer --args "def login(u,p): return db.query(f'SELECT * FROM users WHERE name={u}')" --pretty
|
|
31
31
|
|
|
32
32
|
# 列出模块
|
|
33
|
-
npx cogn@2.2.
|
|
33
|
+
npx cogn@2.2.15 list
|
|
34
34
|
|
|
35
35
|
# 管道模式
|
|
36
|
-
echo "review this code" | npx cogn@2.2.
|
|
36
|
+
echo "review this code" | npx cogn@2.2.15 pipe --module code-reviewer
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## 支持的 Provider
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
对外“稳定支持面”收敛为 6 个 provider(减少认知负担与维护成本):
|
|
42
|
+
|
|
43
|
+
| Provider(Stable) | 环境变量 | 说明 |
|
|
42
44
|
|----------|----------|------|
|
|
43
|
-
| OpenAI | `OPENAI_API_KEY` | OpenAI API |
|
|
44
|
-
| Anthropic | `ANTHROPIC_API_KEY` | Claude |
|
|
45
|
+
| OpenAI (ChatGPT) | `OPENAI_API_KEY` | OpenAI API |
|
|
46
|
+
| Anthropic (Claude) | `ANTHROPIC_API_KEY` | Claude |
|
|
45
47
|
| Gemini | `GEMINI_API_KEY` | Google Gemini |
|
|
46
|
-
| DeepSeek | `DEEPSEEK_API_KEY` | DeepSeek |
|
|
47
48
|
| MiniMax | `MINIMAX_API_KEY` | MiniMax |
|
|
48
|
-
|
|
|
49
|
-
| Qwen | `DASHSCOPE_API_KEY` / `QWEN_API_KEY` | 通义千问 |
|
|
50
|
-
|
|
49
|
+
| DeepSeek | `DEEPSEEK_API_KEY` | DeepSeek API |
|
|
50
|
+
| Qwen (DashScope) | `DASHSCOPE_API_KEY` / `QWEN_API_KEY` | 通义千问 |
|
|
51
|
+
|
|
52
|
+
实验/社区 provider 仍保留实现,但默认不在文档/能力矩阵中承诺稳定性(需要显式指定 `--provider`):
|
|
53
|
+
|
|
54
|
+
| Provider(Experimental/Community) | 环境变量 | 说明 |
|
|
55
|
+
|----------|----------|------|
|
|
56
|
+
| Moonshot (Kimi) | `MOONSHOT_API_KEY` | experimental |
|
|
57
|
+
| Ollama (local) | `OLLAMA_HOST` | community |
|
|
58
|
+
|
|
59
|
+
查看全部 provider(含实验/社区):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx cogn@2.2.15 providers --pretty --all
|
|
63
|
+
```
|
|
51
64
|
|
|
52
65
|
## 命令
|
|
53
66
|
|
|
54
67
|
```bash
|
|
55
68
|
# Core(单文件极简路径)
|
|
56
|
-
npx cogn@2.2.
|
|
57
|
-
npx cogn@2.2.
|
|
58
|
-
npx cogn@2.2.
|
|
69
|
+
npx cogn@2.2.15 core new # 生成 demo.md
|
|
70
|
+
npx cogn@2.2.15 core run demo.md --args "..." # 运行单文件模块
|
|
71
|
+
npx cogn@2.2.15 core promote demo.md # 升级为 v2 模块目录
|
|
59
72
|
|
|
60
73
|
# 渐进复杂度(Profiles)
|
|
61
|
-
npx cogn@2.2.
|
|
62
|
-
npx cogn@2.2.
|
|
63
|
-
npx cogn@2.2.
|
|
74
|
+
npx cogn@2.2.15 run code-reviewer --args "..." --profile core # 极简:跳过校验
|
|
75
|
+
npx cogn@2.2.15 run code-reviewer --args "..." --profile standard # 推荐:日常默认
|
|
76
|
+
npx cogn@2.2.15 run code-reviewer --args "..." --profile certified # 最严格:v2.2 + 审计 + registry provenance/完整性门禁
|
|
64
77
|
# 兼容别名(不推荐写进新文档):
|
|
65
78
|
# - default -> standard
|
|
66
79
|
# - strict -> standard(deprecated preset)
|
|
@@ -70,41 +83,41 @@ npx cogn@2.2.13 run code-reviewer --args "..." --profile certified # 最严格
|
|
|
70
83
|
# - --audit(写入 ~/.cognitive/audit/)
|
|
71
84
|
|
|
72
85
|
# 模块操作
|
|
73
|
-
npx cogn@2.2.
|
|
74
|
-
npx cogn@2.2.
|
|
75
|
-
npx cogn@2.2.
|
|
76
|
-
npx cogn@2.2.
|
|
77
|
-
npx cogn@2.2.
|
|
78
|
-
npx cogn@2.2.
|
|
79
|
-
npx cogn@2.2.
|
|
80
|
-
npx cogn@2.2.
|
|
86
|
+
npx cogn@2.2.15 list # 列出模块
|
|
87
|
+
npx cogn@2.2.15 run <module> --args "..." # 运行模块
|
|
88
|
+
npx cogn@2.2.15 add <url> -m <module> # 从 GitHub 添加模块
|
|
89
|
+
npx cogn@2.2.15 update <module> # 更新模块
|
|
90
|
+
npx cogn@2.2.15 remove <module> # 删除模块
|
|
91
|
+
npx cogn@2.2.15 versions <url> # 查看可用版本
|
|
92
|
+
npx cogn@2.2.15 init <name> # 创建新模块
|
|
93
|
+
npx cogn@2.2.15 pipe --module <name> # 管道模式
|
|
81
94
|
|
|
82
95
|
# 组合执行
|
|
83
|
-
npx cogn@2.2.
|
|
84
|
-
npx cogn@2.2.
|
|
96
|
+
npx cogn@2.2.15 compose <module> --args "..."
|
|
97
|
+
npx cogn@2.2.15 compose-info <module>
|
|
85
98
|
|
|
86
99
|
# 校验与迁移
|
|
87
|
-
npx cogn@2.2.
|
|
88
|
-
npx cogn@2.2.
|
|
89
|
-
npx cogn@2.2.
|
|
90
|
-
npx cogn@2.2.
|
|
100
|
+
npx cogn@2.2.15 validate <module> --v22
|
|
101
|
+
npx cogn@2.2.15 validate --all
|
|
102
|
+
npx cogn@2.2.15 migrate <module> --dry-run
|
|
103
|
+
npx cogn@2.2.15 migrate --all --no-backup
|
|
91
104
|
|
|
92
105
|
# 服务器
|
|
93
|
-
npx cogn@2.2.
|
|
94
|
-
npx cogn@2.2.
|
|
106
|
+
npx cogn@2.2.15 serve --port 8000 # 启动 HTTP API 服务
|
|
107
|
+
npx cogn@2.2.15 mcp # 启动 MCP 服务(Claude Code / Cursor)
|
|
95
108
|
|
|
96
109
|
# 环境检查
|
|
97
|
-
npx cogn@2.2.
|
|
110
|
+
npx cogn@2.2.15 doctor
|
|
98
111
|
|
|
99
112
|
# Registry(索引与分发)
|
|
100
113
|
# 默认 registry index(latest):
|
|
101
114
|
# https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
|
|
102
115
|
# 可通过环境变量或全局参数覆盖:
|
|
103
|
-
COGNITIVE_REGISTRY_URL=... npx cogn@2.2.
|
|
104
|
-
COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 npx cogn@2.2.
|
|
105
|
-
npx cogn@2.2.
|
|
106
|
-
npx cogn@2.2.
|
|
107
|
-
npx cogn@2.2.
|
|
116
|
+
COGNITIVE_REGISTRY_URL=... npx cogn@2.2.15 search
|
|
117
|
+
COGNITIVE_REGISTRY_TIMEOUT_MS=15000 COGNITIVE_REGISTRY_MAX_BYTES=2097152 npx cogn@2.2.15 search
|
|
118
|
+
npx cogn@2.2.15 search --registry https://github.com/Cognary/cognitive/releases/download/vX.Y.Z/cognitive-registry.v2.json
|
|
119
|
+
npx cogn@2.2.15 registry verify --remote --index https://github.com/Cognary/cognitive/releases/latest/download/cognitive-registry.v2.json
|
|
120
|
+
npx cogn@2.2.15 registry verify --remote --concurrency 2
|
|
108
121
|
```
|
|
109
122
|
|
|
110
123
|
## 开发
|
|
@@ -127,6 +140,27 @@ npm run dev -- run code-reviewer --args "..."
|
|
|
127
140
|
npm run release:check
|
|
128
141
|
```
|
|
129
142
|
|
|
143
|
+
## Cognitive 对照基准
|
|
144
|
+
|
|
145
|
+
如果你要验证“使用 Cognitive”和“直接 prompt/skill 风格 JSON 提示”之间的差异,可以运行内置对照基准:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# 先确保 dist 已构建
|
|
149
|
+
npm run build
|
|
150
|
+
|
|
151
|
+
# 只看计划,不实际调用模型
|
|
152
|
+
npm run bench:cognitive-vs-raw -- --provider gemini --plan
|
|
153
|
+
|
|
154
|
+
# 实际运行(示例)
|
|
155
|
+
npm run bench:cognitive-vs-raw -- --provider gemini --model gemini-3-pro-preview --runs 3
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
相关文件:
|
|
159
|
+
|
|
160
|
+
- `benchmarks/cognitive-vs-raw/README.md`
|
|
161
|
+
- `benchmarks/cognitive-vs-raw/suite.example.json`
|
|
162
|
+
- `benchmarks/cognitive-vs-raw/report-template.md`
|
|
163
|
+
|
|
130
164
|
## License
|
|
131
165
|
|
|
132
166
|
MIT
|
package/dist/cli.js
CHANGED
|
@@ -62,14 +62,39 @@ async function main() {
|
|
|
62
62
|
process.exit(1);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
const commandRequiresProvider = (() => {
|
|
66
|
+
if (command === 'core')
|
|
67
|
+
return positionals[0] === 'run';
|
|
68
|
+
switch (command) {
|
|
69
|
+
case 'run':
|
|
70
|
+
case 'pipe':
|
|
71
|
+
case 'compose':
|
|
72
|
+
case 'test':
|
|
73
|
+
case 'conformance':
|
|
74
|
+
case 'serve':
|
|
75
|
+
case 'mcp':
|
|
76
|
+
return true;
|
|
77
|
+
default:
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
// Get provider (only required for commands that actually invoke an LLM).
|
|
82
|
+
// For non-provider commands (list/validate/registry/docs helpers), we keep the CLI usable even
|
|
83
|
+
// when no API keys are configured by using a placeholder provider that should not be invoked.
|
|
66
84
|
let provider;
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
if (commandRequiresProvider) {
|
|
86
|
+
try {
|
|
87
|
+
provider = getProvider(values.provider, values.model);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
69
93
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
else {
|
|
95
|
+
// If the user explicitly asked for a provider, respect it even if unconfigured.
|
|
96
|
+
// Otherwise, pick a stable placeholder.
|
|
97
|
+
provider = getProvider(values.provider ?? 'openai', values.model);
|
|
73
98
|
}
|
|
74
99
|
let policy;
|
|
75
100
|
try {
|
|
@@ -248,7 +273,7 @@ async function main() {
|
|
|
248
273
|
console.log('');
|
|
249
274
|
// 2. Provider configuration
|
|
250
275
|
console.log('LLM Providers:');
|
|
251
|
-
const providers = listProviders();
|
|
276
|
+
const providers = listProviders({ all: Boolean(values.all) });
|
|
252
277
|
let hasConfiguredProvider = false;
|
|
253
278
|
for (const p of providers) {
|
|
254
279
|
const status = p.configured ? '✓' : '–';
|
|
@@ -257,6 +282,7 @@ async function main() {
|
|
|
257
282
|
console.log(` Model: ${p.model}`);
|
|
258
283
|
console.log(` Structured output: ${p.structuredOutput}`);
|
|
259
284
|
console.log(` Streaming: ${p.streaming ? 'yes' : 'no'}`);
|
|
285
|
+
console.log(` Support: ${p.support}`);
|
|
260
286
|
console.log(` Status: ${apiKeyStatus}`);
|
|
261
287
|
if (p.configured)
|
|
262
288
|
hasConfiguredProvider = true;
|
|
@@ -270,7 +296,8 @@ async function main() {
|
|
|
270
296
|
}
|
|
271
297
|
catch {
|
|
272
298
|
console.log(' ✗ None configured');
|
|
273
|
-
console.log(' → Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY,
|
|
299
|
+
console.log(' → Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, MINIMAX_API_KEY, DEEPSEEK_API_KEY, DASHSCOPE_API_KEY/QWEN_API_KEY');
|
|
300
|
+
console.log(' → Experimental providers require explicit --provider (see: cog providers --all)');
|
|
274
301
|
}
|
|
275
302
|
console.log('');
|
|
276
303
|
// 4. Module scan
|
|
@@ -355,7 +382,7 @@ async function main() {
|
|
|
355
382
|
break;
|
|
356
383
|
}
|
|
357
384
|
case 'providers': {
|
|
358
|
-
const providers = listProviders();
|
|
385
|
+
const providers = listProviders({ all: Boolean(values.all) });
|
|
359
386
|
console.log(JSON.stringify({ providers }, null, values.pretty ? 2 : 0));
|
|
360
387
|
break;
|
|
361
388
|
}
|
|
@@ -1008,7 +1035,7 @@ OPTIONS:
|
|
|
1008
1035
|
-t, --tag <version> Git tag/version (for add/update)
|
|
1009
1036
|
-b, --branch <name> Git branch (for add)
|
|
1010
1037
|
-M, --model <name> LLM model (e.g., gpt-4o, gemini-2.0-flash)
|
|
1011
|
-
-p, --provider <name> LLM provider (
|
|
1038
|
+
-p, --provider <name> LLM provider (stable: openai, anthropic, gemini, minimax, deepseek, qwen)
|
|
1012
1039
|
--pretty Pretty-print JSON output
|
|
1013
1040
|
-V, --verbose Verbose output
|
|
1014
1041
|
--no-validate Skip schema validation
|
|
@@ -1022,7 +1049,7 @@ OPTIONS:
|
|
|
1022
1049
|
--v22 Use strict v2.2 validation (for validate)
|
|
1023
1050
|
--dry-run Show what would be done without changes (for migrate)
|
|
1024
1051
|
--no-backup Skip backup before migration (for migrate)
|
|
1025
|
-
--all Process all modules (
|
|
1052
|
+
--all Process all modules (validate/migrate/test) OR include experimental providers (providers/doctor)
|
|
1026
1053
|
--conformance Run official spec vectors (for test)
|
|
1027
1054
|
--suite <name> Conformance suite: envelope|runtime|stream|registry|all
|
|
1028
1055
|
--level <n> Conformance level: 1|2|3
|
|
@@ -1100,15 +1127,17 @@ ENVIRONMENT:
|
|
|
1100
1127
|
GEMINI_API_KEY Google Gemini
|
|
1101
1128
|
OPENAI_API_KEY OpenAI
|
|
1102
1129
|
ANTHROPIC_API_KEY Anthropic Claude
|
|
1103
|
-
DEEPSEEK_API_KEY DeepSeek
|
|
1104
1130
|
MINIMAX_API_KEY MiniMax
|
|
1105
|
-
|
|
1131
|
+
DEEPSEEK_API_KEY DeepSeek
|
|
1106
1132
|
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
1107
|
-
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
1108
1133
|
COG_MODEL Override default model for any provider
|
|
1109
1134
|
COGNITIVE_REGISTRY_URL Override registry index URL
|
|
1110
1135
|
COGNITIVE_REGISTRY_TIMEOUT_MS Registry index fetch timeout (ms)
|
|
1111
1136
|
COGNITIVE_REGISTRY_MAX_BYTES Registry index max bytes
|
|
1137
|
+
|
|
1138
|
+
EXPERIMENTAL/COMMUNITY (not in the stable support promise; use --provider explicitly):
|
|
1139
|
+
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
1140
|
+
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
1112
1141
|
`);
|
|
1113
1142
|
}
|
|
1114
1143
|
main().catch(e => {
|
package/dist/modules/runner.js
CHANGED
|
@@ -21,6 +21,74 @@ function safeSnippet(s, max = 500) {
|
|
|
21
21
|
return raw;
|
|
22
22
|
return raw.slice(0, max) + `…(+${raw.length - max} chars)`;
|
|
23
23
|
}
|
|
24
|
+
function stableCanonicalKey(value) {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
return `[${value.map((item) => stableCanonicalKey(item)).join(',')}]`;
|
|
27
|
+
}
|
|
28
|
+
if (value && typeof value === 'object') {
|
|
29
|
+
const obj = value;
|
|
30
|
+
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableCanonicalKey(obj[key])}`).join(',')}}`;
|
|
31
|
+
}
|
|
32
|
+
return JSON.stringify(value);
|
|
33
|
+
}
|
|
34
|
+
function normalizeStringValue(value, hint) {
|
|
35
|
+
let normalized = value.trim().replace(/\s+/g, ' ');
|
|
36
|
+
switch (hint) {
|
|
37
|
+
case 'lowercase':
|
|
38
|
+
normalized = normalized.toLowerCase();
|
|
39
|
+
break;
|
|
40
|
+
case 'lower_snake_case':
|
|
41
|
+
normalized = normalized
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
44
|
+
.replace(/^_+|_+$/g, '');
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
function canonicalizeDataBySchema(value, schema) {
|
|
52
|
+
if (!schema || typeof schema !== 'object' || schema === null) {
|
|
53
|
+
return typeof value === 'string' ? normalizeStringValue(value) : value;
|
|
54
|
+
}
|
|
55
|
+
const schemaObj = schema;
|
|
56
|
+
const type = schemaObj.type;
|
|
57
|
+
if (typeof value === 'string') {
|
|
58
|
+
const hint = typeof schemaObj['x-cognitive-string-normalize'] === 'string'
|
|
59
|
+
? String(schemaObj['x-cognitive-string-normalize'])
|
|
60
|
+
: undefined;
|
|
61
|
+
const normalized = normalizeStringValue(value, hint);
|
|
62
|
+
if (Array.isArray(schemaObj.enum)) {
|
|
63
|
+
const enumMatch = schemaObj.enum.find((candidate) => {
|
|
64
|
+
if (typeof candidate !== 'string')
|
|
65
|
+
return false;
|
|
66
|
+
return normalizeStringValue(candidate, hint).toLowerCase() === normalized.toLowerCase();
|
|
67
|
+
});
|
|
68
|
+
return enumMatch ?? normalized;
|
|
69
|
+
}
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
const itemSchema = schemaObj.items;
|
|
74
|
+
const normalizedItems = value.map((item) => canonicalizeDataBySchema(item, itemSchema));
|
|
75
|
+
if (schemaObj['x-cognitive-unordered'] === true) {
|
|
76
|
+
normalizedItems.sort((a, b) => stableCanonicalKey(a).localeCompare(stableCanonicalKey(b)));
|
|
77
|
+
}
|
|
78
|
+
return normalizedItems;
|
|
79
|
+
}
|
|
80
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && type === 'object') {
|
|
81
|
+
const properties = (schemaObj.properties && typeof schemaObj.properties === 'object')
|
|
82
|
+
? schemaObj.properties
|
|
83
|
+
: {};
|
|
84
|
+
const normalized = {};
|
|
85
|
+
for (const [key, child] of Object.entries(value)) {
|
|
86
|
+
normalized[key] = canonicalizeDataBySchema(child, properties[key]);
|
|
87
|
+
}
|
|
88
|
+
return normalized;
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
24
92
|
function parseJsonWithCandidates(raw) {
|
|
25
93
|
const candidates = extractJsonCandidates(raw);
|
|
26
94
|
const attempts = [];
|
|
@@ -936,6 +1004,12 @@ function repairEnvelope(response, riskRule = 'max_changes_risk', maxExplainLengt
|
|
|
936
1004
|
if (meta.explain.length > maxExplainLength) {
|
|
937
1005
|
meta.explain = meta.explain.slice(0, maxExplainLength - 3) + '...';
|
|
938
1006
|
}
|
|
1007
|
+
// data.rationale is commonly required by module contracts and is safely
|
|
1008
|
+
// synthesizable from meta.explain when the model omitted the longer field.
|
|
1009
|
+
if (typeof data.rationale !== 'string' || data.rationale.trim().length === 0) {
|
|
1010
|
+
data.rationale = meta.explain;
|
|
1011
|
+
repaired.data = data;
|
|
1012
|
+
}
|
|
939
1013
|
// Build proper v2.2 response with version
|
|
940
1014
|
const builtMeta = {
|
|
941
1015
|
confidence: meta.confidence,
|
|
@@ -947,8 +1021,9 @@ function repairEnvelope(response, riskRule = 'max_changes_risk', maxExplainLengt
|
|
|
947
1021
|
version: ENVELOPE_VERSION,
|
|
948
1022
|
meta: builtMeta,
|
|
949
1023
|
// E4000 is an internal/runtime error fallback (should rarely happen after repair).
|
|
950
|
-
error: repaired.error ??
|
|
951
|
-
|
|
1024
|
+
error: repaired.error ??
|
|
1025
|
+
{ code: 'E4000', message: typeof meta.explain === 'string' && meta.explain.trim() ? meta.explain : 'Unknown error' },
|
|
1026
|
+
partial_data: repaired.partial_data ?? repaired.data
|
|
952
1027
|
} : {
|
|
953
1028
|
ok: true,
|
|
954
1029
|
version: ENVELOPE_VERSION,
|
|
@@ -990,10 +1065,38 @@ function repairErrorEnvelope(data, maxExplainLength = 280) {
|
|
|
990
1065
|
explain: meta.explain,
|
|
991
1066
|
},
|
|
992
1067
|
// E4000 is an internal/runtime error fallback (should rarely happen after repair).
|
|
993
|
-
error: repaired.error ??
|
|
994
|
-
|
|
1068
|
+
error: repaired.error ??
|
|
1069
|
+
{ code: 'E4000', message: typeof meta.explain === 'string' && meta.explain.trim() ? meta.explain : 'Unknown error' },
|
|
1070
|
+
partial_data: repaired.partial_data ?? repaired.data,
|
|
995
1071
|
};
|
|
996
1072
|
}
|
|
1073
|
+
function isPlainRecord(value) {
|
|
1074
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1075
|
+
}
|
|
1076
|
+
function normalizeEnvelopeLikeObject(parsed) {
|
|
1077
|
+
if (!isPlainRecord(parsed) || !isPlainRecord(parsed.meta) || typeof parsed.ok !== 'boolean') {
|
|
1078
|
+
return parsed;
|
|
1079
|
+
}
|
|
1080
|
+
const normalized = deepClone(parsed);
|
|
1081
|
+
const reserved = new Set(['ok', 'version', 'meta', 'data', 'error', 'partial_data']);
|
|
1082
|
+
const payloadEntries = Object.entries(normalized).filter(([key]) => !reserved.has(key));
|
|
1083
|
+
if (normalized.ok === true) {
|
|
1084
|
+
if (!isPlainRecord(normalized.data) && payloadEntries.length > 0) {
|
|
1085
|
+
normalized.data = Object.fromEntries(payloadEntries);
|
|
1086
|
+
for (const [key] of payloadEntries)
|
|
1087
|
+
delete normalized[key];
|
|
1088
|
+
}
|
|
1089
|
+
return normalized;
|
|
1090
|
+
}
|
|
1091
|
+
const hasError = isPlainRecord(normalized.error) && typeof normalized.error.code === 'string';
|
|
1092
|
+
if (!hasError && payloadEntries.length > 0) {
|
|
1093
|
+
normalized.data = Object.fromEntries(payloadEntries);
|
|
1094
|
+
for (const [key] of payloadEntries)
|
|
1095
|
+
delete normalized[key];
|
|
1096
|
+
normalized.ok = true;
|
|
1097
|
+
}
|
|
1098
|
+
return normalized;
|
|
1099
|
+
}
|
|
997
1100
|
/**
|
|
998
1101
|
* Wrap v2.1 response to v2.2 format
|
|
999
1102
|
*/
|
|
@@ -1671,14 +1774,15 @@ export async function runModule(module, provider, options = {}) {
|
|
|
1671
1774
|
}
|
|
1672
1775
|
// Convert to v2.2 envelope
|
|
1673
1776
|
let response;
|
|
1674
|
-
|
|
1675
|
-
|
|
1777
|
+
const normalizedParsed = normalizeEnvelopeLikeObject(parsed);
|
|
1778
|
+
if (isV22Envelope(normalizedParsed)) {
|
|
1779
|
+
response = normalizedParsed;
|
|
1676
1780
|
}
|
|
1677
|
-
else if (isEnvelopeResponse(
|
|
1678
|
-
response = wrapV21ToV22(
|
|
1781
|
+
else if (isEnvelopeResponse(normalizedParsed)) {
|
|
1782
|
+
response = wrapV21ToV22(normalizedParsed, riskRule);
|
|
1679
1783
|
}
|
|
1680
1784
|
else {
|
|
1681
|
-
response = convertLegacyToEnvelope(
|
|
1785
|
+
response = convertLegacyToEnvelope(normalizedParsed);
|
|
1682
1786
|
}
|
|
1683
1787
|
// Add version and meta fields
|
|
1684
1788
|
response.version = ENVELOPE_VERSION;
|
|
@@ -1727,6 +1831,9 @@ export async function runModule(module, provider, options = {}) {
|
|
|
1727
1831
|
// Get data schema (support both "data" and "output" aliases)
|
|
1728
1832
|
const dataSchema = module.dataSchema || module.outputSchema;
|
|
1729
1833
|
const metaSchema = module.metaSchema;
|
|
1834
|
+
if (dataSchema) {
|
|
1835
|
+
response.data = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
|
|
1836
|
+
}
|
|
1730
1837
|
const dataToValidate = response.data ?? {};
|
|
1731
1838
|
if (dataSchema && Object.keys(dataSchema).length > 0) {
|
|
1732
1839
|
let dataErrors = validateData(dataToValidate, dataSchema, 'Data');
|
|
@@ -1735,7 +1842,8 @@ export async function runModule(module, provider, options = {}) {
|
|
|
1735
1842
|
response = repairEnvelope(response, riskRule);
|
|
1736
1843
|
response.version = ENVELOPE_VERSION;
|
|
1737
1844
|
// Re-validate after repair
|
|
1738
|
-
const repairedData = response.data ?? {};
|
|
1845
|
+
const repairedData = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
|
|
1846
|
+
response.data = repairedData;
|
|
1739
1847
|
dataErrors = validateData(repairedData, dataSchema, 'Data');
|
|
1740
1848
|
}
|
|
1741
1849
|
if (dataErrors.length > 0) {
|
|
@@ -2115,14 +2223,15 @@ export async function* runModuleStream(module, provider, options = {}) {
|
|
|
2115
2223
|
}
|
|
2116
2224
|
// Convert to v2.2 envelope
|
|
2117
2225
|
let response;
|
|
2118
|
-
|
|
2119
|
-
|
|
2226
|
+
const normalizedParsed = normalizeEnvelopeLikeObject(parsed);
|
|
2227
|
+
if (isV22Envelope(normalizedParsed)) {
|
|
2228
|
+
response = normalizedParsed;
|
|
2120
2229
|
}
|
|
2121
|
-
else if (isEnvelopeResponse(
|
|
2122
|
-
response = wrapV21ToV22(
|
|
2230
|
+
else if (isEnvelopeResponse(normalizedParsed)) {
|
|
2231
|
+
response = wrapV21ToV22(normalizedParsed, riskRule);
|
|
2123
2232
|
}
|
|
2124
2233
|
else {
|
|
2125
|
-
response = convertLegacyToEnvelope(
|
|
2234
|
+
response = convertLegacyToEnvelope(normalizedParsed);
|
|
2126
2235
|
}
|
|
2127
2236
|
// Add version and meta
|
|
2128
2237
|
response.version = ENVELOPE_VERSION;
|
|
@@ -2172,12 +2281,15 @@ export async function* runModuleStream(module, provider, options = {}) {
|
|
|
2172
2281
|
const metaSchema = module.metaSchema;
|
|
2173
2282
|
if (dataSchema && Object.keys(dataSchema).length > 0) {
|
|
2174
2283
|
let dataToValidate = response.data ?? {};
|
|
2284
|
+
dataToValidate = canonicalizeDataBySchema(dataToValidate, dataSchema);
|
|
2285
|
+
response.data = dataToValidate;
|
|
2175
2286
|
let dataErrors = validateData(dataToValidate, dataSchema, 'Data');
|
|
2176
2287
|
if (dataErrors.length > 0 && enableRepair) {
|
|
2177
2288
|
response = repairEnvelope(response, riskRule);
|
|
2178
2289
|
response.version = ENVELOPE_VERSION;
|
|
2179
2290
|
// Re-validate after repair
|
|
2180
|
-
const repairedData = response.data ?? {};
|
|
2291
|
+
const repairedData = canonicalizeDataBySchema(response.data ?? {}, dataSchema);
|
|
2292
|
+
response.data = repairedData;
|
|
2181
2293
|
dataToValidate = repairedData;
|
|
2182
2294
|
dataErrors = validateData(repairedData, dataSchema, 'Data');
|
|
2183
2295
|
}
|
|
@@ -19,6 +19,8 @@ export interface CallDirective {
|
|
|
19
19
|
module: string;
|
|
20
20
|
args: string;
|
|
21
21
|
match: string;
|
|
22
|
+
start: number;
|
|
23
|
+
end: number;
|
|
22
24
|
}
|
|
23
25
|
export interface SubagentRunOptions {
|
|
24
26
|
input?: ModuleInput;
|
|
@@ -47,7 +49,9 @@ export declare function parseCalls(text: string): CallDirective[];
|
|
|
47
49
|
* Replace @call directives with their results
|
|
48
50
|
*/
|
|
49
51
|
export declare function substituteCallResults(text: string, callResults: Array<{
|
|
50
|
-
match
|
|
52
|
+
match?: string;
|
|
53
|
+
start?: number;
|
|
54
|
+
end?: number;
|
|
51
55
|
module: string;
|
|
52
56
|
result: unknown;
|
|
53
57
|
}>): string;
|
package/dist/modules/subagent.js
CHANGED
|
@@ -65,7 +65,9 @@ export function parseCalls(text) {
|
|
|
65
65
|
calls.push({
|
|
66
66
|
module: match[1],
|
|
67
67
|
args: match[2] || '',
|
|
68
|
-
match: match[0]
|
|
68
|
+
match: match[0],
|
|
69
|
+
start: match.index,
|
|
70
|
+
end: match.index + match[0].length
|
|
69
71
|
});
|
|
70
72
|
}
|
|
71
73
|
return calls;
|
|
@@ -74,14 +76,38 @@ export function parseCalls(text) {
|
|
|
74
76
|
* Replace @call directives with their results
|
|
75
77
|
*/
|
|
76
78
|
export function substituteCallResults(text, callResults) {
|
|
77
|
-
|
|
78
|
-
for (const entry of callResults) {
|
|
79
|
+
const buildReplacement = (entry) => {
|
|
79
80
|
const resultStr = typeof entry.result === 'object'
|
|
80
81
|
? JSON.stringify(entry.result, null, 2)
|
|
81
82
|
: String(entry.result);
|
|
82
|
-
|
|
83
|
+
return `[Result from ${entry.module}]:\n${resultStr}`;
|
|
84
|
+
};
|
|
85
|
+
const hasRanges = callResults.every((entry) => typeof entry.start === 'number' && typeof entry.end === 'number');
|
|
86
|
+
if (hasRanges) {
|
|
87
|
+
const sorted = [...callResults].sort((a, b) => (a.start ?? 0) - (b.start ?? 0));
|
|
88
|
+
let cursor = 0;
|
|
89
|
+
let result = '';
|
|
90
|
+
for (const entry of sorted) {
|
|
91
|
+
const start = entry.start;
|
|
92
|
+
const end = entry.end;
|
|
93
|
+
if (start < cursor) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
result += text.slice(cursor, start);
|
|
97
|
+
result += buildReplacement(entry);
|
|
98
|
+
cursor = end;
|
|
99
|
+
}
|
|
100
|
+
result += text.slice(cursor);
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
let result = text;
|
|
104
|
+
for (const entry of callResults) {
|
|
105
|
+
if (!entry.match) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
83
108
|
const idx = result.indexOf(entry.match);
|
|
84
109
|
if (idx !== -1) {
|
|
110
|
+
const replacement = buildReplacement(entry);
|
|
85
111
|
result = result.slice(0, idx) + replacement + result.slice(idx + entry.match.length);
|
|
86
112
|
}
|
|
87
113
|
}
|
|
@@ -147,10 +173,22 @@ export class SubagentOrchestrator {
|
|
|
147
173
|
}, childContext);
|
|
148
174
|
// Store result
|
|
149
175
|
if (childResult.ok && 'data' in childResult) {
|
|
150
|
-
callResults.push({
|
|
176
|
+
callResults.push({
|
|
177
|
+
match: call.match,
|
|
178
|
+
start: call.start,
|
|
179
|
+
end: call.end,
|
|
180
|
+
module: call.module,
|
|
181
|
+
result: childResult.data
|
|
182
|
+
});
|
|
151
183
|
}
|
|
152
184
|
else if ('error' in childResult) {
|
|
153
|
-
callResults.push({
|
|
185
|
+
callResults.push({
|
|
186
|
+
match: call.match,
|
|
187
|
+
start: call.start,
|
|
188
|
+
end: call.end,
|
|
189
|
+
module: call.module,
|
|
190
|
+
result: { error: childResult.error }
|
|
191
|
+
});
|
|
154
192
|
}
|
|
155
193
|
}
|
|
156
194
|
// Substitute call results into prompt
|
|
@@ -20,6 +20,7 @@ export declare class AnthropicProvider extends BaseProvider {
|
|
|
20
20
|
* Build request body for Anthropic API
|
|
21
21
|
*/
|
|
22
22
|
private buildRequestBody;
|
|
23
|
+
private mergeUsage;
|
|
23
24
|
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
24
25
|
/**
|
|
25
26
|
* Stream-based invoke using Anthropic's streaming API.
|
|
@@ -53,6 +53,17 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
53
53
|
}
|
|
54
54
|
return { body, systemContent: systemMessage?.content };
|
|
55
55
|
}
|
|
56
|
+
mergeUsage(current, next) {
|
|
57
|
+
if (!next)
|
|
58
|
+
return current;
|
|
59
|
+
const promptTokens = next.input_tokens ?? current?.promptTokens ?? 0;
|
|
60
|
+
const completionTokens = next.output_tokens ?? current?.completionTokens ?? 0;
|
|
61
|
+
return {
|
|
62
|
+
promptTokens,
|
|
63
|
+
completionTokens,
|
|
64
|
+
totalTokens: promptTokens + completionTokens,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
56
67
|
async invoke(params) {
|
|
57
68
|
if (!this.isConfigured()) {
|
|
58
69
|
throw new Error('Anthropic API key not configured. Set ANTHROPIC_API_KEY environment variable.');
|
|
@@ -140,20 +151,11 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
140
151
|
}
|
|
141
152
|
// Extract usage info (message_delta or message_stop event)
|
|
142
153
|
if (data.type === 'message_delta' && data.usage) {
|
|
143
|
-
usage =
|
|
144
|
-
promptTokens: data.usage.input_tokens || 0,
|
|
145
|
-
completionTokens: data.usage.output_tokens || 0,
|
|
146
|
-
totalTokens: (data.usage.input_tokens || 0) + (data.usage.output_tokens || 0),
|
|
147
|
-
};
|
|
154
|
+
usage = this.mergeUsage(usage, data.usage);
|
|
148
155
|
}
|
|
149
156
|
// Also check message_start for input tokens
|
|
150
157
|
if (data.type === 'message_start' && data.message?.usage) {
|
|
151
|
-
|
|
152
|
-
usage = {
|
|
153
|
-
promptTokens: inputTokens,
|
|
154
|
-
completionTokens: usage?.completionTokens || 0,
|
|
155
|
-
totalTokens: inputTokens + (usage?.completionTokens || 0),
|
|
156
|
-
};
|
|
158
|
+
usage = this.mergeUsage(usage, data.message.usage);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
catch {
|
|
@@ -179,19 +181,10 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
if (data.type === 'message_delta' && data.usage) {
|
|
182
|
-
usage =
|
|
183
|
-
promptTokens: data.usage.input_tokens || 0,
|
|
184
|
-
completionTokens: data.usage.output_tokens || 0,
|
|
185
|
-
totalTokens: (data.usage.input_tokens || 0) + (data.usage.output_tokens || 0),
|
|
186
|
-
};
|
|
184
|
+
usage = this.mergeUsage(usage, data.usage);
|
|
187
185
|
}
|
|
188
186
|
if (data.type === 'message_start' && data.message?.usage) {
|
|
189
|
-
|
|
190
|
-
usage = {
|
|
191
|
-
promptTokens: inputTokens,
|
|
192
|
-
completionTokens: usage?.completionTokens || 0,
|
|
193
|
-
totalTokens: inputTokens + (usage?.completionTokens || 0),
|
|
194
|
-
};
|
|
187
|
+
usage = this.mergeUsage(usage, data.message.usage);
|
|
195
188
|
}
|
|
196
189
|
}
|
|
197
190
|
catch {
|
|
@@ -15,9 +15,13 @@ export { DeepSeekProvider } from './deepseek.js';
|
|
|
15
15
|
export { MoonshotProvider } from './moonshot.js';
|
|
16
16
|
export { QwenProvider } from './qwen.js';
|
|
17
17
|
export { OllamaProvider } from './ollama.js';
|
|
18
|
+
export type ProviderSupportTier = 'stable' | 'experimental' | 'community';
|
|
18
19
|
export declare function getProvider(name?: string, model?: string): Provider;
|
|
19
|
-
export declare function listProviders(
|
|
20
|
+
export declare function listProviders(opts?: {
|
|
21
|
+
all?: boolean;
|
|
22
|
+
}): Array<{
|
|
20
23
|
name: string;
|
|
24
|
+
support: ProviderSupportTier;
|
|
21
25
|
configured: boolean;
|
|
22
26
|
model: string;
|
|
23
27
|
structuredOutput: StructuredOutputMode;
|
package/dist/providers/index.js
CHANGED
|
@@ -22,6 +22,14 @@ export { DeepSeekProvider } from './deepseek.js';
|
|
|
22
22
|
export { MoonshotProvider } from './moonshot.js';
|
|
23
23
|
export { QwenProvider } from './qwen.js';
|
|
24
24
|
export { OllamaProvider } from './ollama.js';
|
|
25
|
+
// Public promise: these providers are the "supported surface" for the open-source npm runtime.
|
|
26
|
+
// Everything else is intentionally treated as experimental/community to reduce user confusion and
|
|
27
|
+
// avoid over-promising stability across a long tail of provider quirks.
|
|
28
|
+
const STABLE_PROVIDERS = ['openai', 'anthropic', 'gemini', 'minimax', 'deepseek', 'qwen'];
|
|
29
|
+
const EXPERIMENTAL_PROVIDERS = ['moonshot', 'ollama'];
|
|
30
|
+
function isStableProvider(name) {
|
|
31
|
+
return STABLE_PROVIDERS.includes(name);
|
|
32
|
+
}
|
|
25
33
|
const providers = {
|
|
26
34
|
gemini: (model) => new GeminiProvider(undefined, model),
|
|
27
35
|
openai: (model) => new OpenAIProvider(undefined, model),
|
|
@@ -39,24 +47,27 @@ const providers = {
|
|
|
39
47
|
export function getProvider(name, model) {
|
|
40
48
|
// Check for model override from environment
|
|
41
49
|
const modelOverride = model || process.env.COG_MODEL;
|
|
42
|
-
// Auto-detect if not specified
|
|
50
|
+
// Auto-detect if not specified.
|
|
51
|
+
//
|
|
52
|
+
// Important: we only auto-select within the "stable" provider set.
|
|
53
|
+
// Experimental/community providers can still be used, but require explicit `--provider`,
|
|
54
|
+
// so users don't accidentally end up on a provider they didn't intend (or that isn't
|
|
55
|
+
// part of the stable support promise).
|
|
43
56
|
if (!name) {
|
|
44
|
-
if (process.env.GEMINI_API_KEY)
|
|
45
|
-
return new GeminiProvider(undefined, modelOverride);
|
|
46
57
|
if (process.env.OPENAI_API_KEY)
|
|
47
58
|
return new OpenAIProvider(undefined, modelOverride);
|
|
48
59
|
if (process.env.ANTHROPIC_API_KEY)
|
|
49
60
|
return new AnthropicProvider(undefined, modelOverride);
|
|
50
|
-
if (process.env.
|
|
51
|
-
return new
|
|
61
|
+
if (process.env.GEMINI_API_KEY)
|
|
62
|
+
return new GeminiProvider(undefined, modelOverride);
|
|
52
63
|
if (process.env.MINIMAX_API_KEY)
|
|
53
64
|
return new MiniMaxProvider(undefined, modelOverride);
|
|
54
|
-
if (process.env.
|
|
55
|
-
return new
|
|
65
|
+
if (process.env.DEEPSEEK_API_KEY)
|
|
66
|
+
return new DeepSeekProvider(undefined, modelOverride);
|
|
56
67
|
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY)
|
|
57
68
|
return new QwenProvider(undefined, modelOverride);
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
throw new Error(`No stable provider configured. Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, MINIMAX_API_KEY, DEEPSEEK_API_KEY, DASHSCOPE_API_KEY/QWEN_API_KEY. ` +
|
|
70
|
+
`To use experimental providers, pass --provider (e.g. --provider moonshot).`);
|
|
60
71
|
}
|
|
61
72
|
const factory = providers[name.toLowerCase()];
|
|
62
73
|
if (!factory) {
|
|
@@ -70,13 +81,14 @@ function safeCapabilities(p) {
|
|
|
70
81
|
return caps;
|
|
71
82
|
return { structuredOutput: 'prompt', streaming: p.supportsStreaming?.() ?? false };
|
|
72
83
|
}
|
|
73
|
-
function providerRow(name, configured, model, make) {
|
|
84
|
+
function providerRow(name, configured, model, make, support) {
|
|
74
85
|
const caps = safeCapabilities(make());
|
|
75
86
|
const structuredJsonSchema = caps.structuredOutput === 'native' && (caps.nativeSchemaDialect ?? 'json-schema') !== 'json-schema'
|
|
76
87
|
? 'prompt'
|
|
77
88
|
: caps.structuredOutput;
|
|
78
89
|
return {
|
|
79
90
|
name,
|
|
91
|
+
support,
|
|
80
92
|
configured,
|
|
81
93
|
model,
|
|
82
94
|
structuredOutput: caps.structuredOutput,
|
|
@@ -86,15 +98,20 @@ function providerRow(name, configured, model, make) {
|
|
|
86
98
|
streaming: caps.streaming,
|
|
87
99
|
};
|
|
88
100
|
}
|
|
89
|
-
export function listProviders() {
|
|
90
|
-
|
|
91
|
-
providerRow('
|
|
92
|
-
providerRow('
|
|
93
|
-
providerRow('
|
|
94
|
-
providerRow('
|
|
95
|
-
providerRow('
|
|
96
|
-
providerRow('
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
export function listProviders(opts) {
|
|
102
|
+
const stable = [
|
|
103
|
+
providerRow('openai', !!process.env.OPENAI_API_KEY, 'gpt-5.2', () => new OpenAIProvider(''), 'stable'),
|
|
104
|
+
providerRow('anthropic', !!process.env.ANTHROPIC_API_KEY, 'claude-sonnet-4.5', () => new AnthropicProvider(''), 'stable'),
|
|
105
|
+
providerRow('gemini', !!process.env.GEMINI_API_KEY, 'gemini-3-pro-preview', () => new GeminiProvider(''), 'stable'),
|
|
106
|
+
providerRow('minimax', !!process.env.MINIMAX_API_KEY, 'MiniMax-M2.1', () => new MiniMaxProvider(''), 'stable'),
|
|
107
|
+
providerRow('deepseek', !!process.env.DEEPSEEK_API_KEY, 'deepseek-chat', () => new DeepSeekProvider(''), 'stable'),
|
|
108
|
+
providerRow('qwen', !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), 'qwen3-max', () => new QwenProvider(''), 'stable'),
|
|
109
|
+
];
|
|
110
|
+
if (!opts?.all)
|
|
111
|
+
return stable;
|
|
112
|
+
const experimental = [
|
|
113
|
+
providerRow('moonshot', !!process.env.MOONSHOT_API_KEY, 'kimi-k2.5', () => new MoonshotProvider(''), 'experimental'),
|
|
114
|
+
providerRow('ollama', !!process.env.OLLAMA_HOST, 'llama4 (local)', () => new OllamaProvider(), 'community'),
|
|
99
115
|
];
|
|
116
|
+
return [...stable, ...experimental];
|
|
100
117
|
}
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
* MiniMax Provider - MiniMax API
|
|
3
3
|
*/
|
|
4
4
|
import { BaseProvider } from './base.js';
|
|
5
|
+
const DEFAULT_MINIMAX_BASE_URL = 'https://api.minimax.io/v1';
|
|
5
6
|
export class MiniMaxProvider extends BaseProvider {
|
|
6
7
|
name = 'minimax';
|
|
7
8
|
apiKey;
|
|
8
9
|
model;
|
|
9
|
-
baseUrl
|
|
10
|
+
baseUrl;
|
|
10
11
|
constructor(apiKey, model = 'MiniMax-M2.1') {
|
|
11
12
|
super();
|
|
12
13
|
this.apiKey = apiKey || process.env.MINIMAX_API_KEY || '';
|
|
13
14
|
this.model = model;
|
|
15
|
+
this.baseUrl = (process.env.MINIMAX_BASE_URL || DEFAULT_MINIMAX_BASE_URL).replace(/\/+$/, '');
|
|
14
16
|
}
|
|
15
17
|
isConfigured() {
|
|
16
18
|
return !!this.apiKey;
|
package/dist/types.js
CHANGED
|
@@ -7,7 +7,17 @@
|
|
|
7
7
|
// =============================================================================
|
|
8
8
|
/** Check if response is v2.2 format */
|
|
9
9
|
export function isV22Envelope(response) {
|
|
10
|
-
|
|
10
|
+
if (typeof response !== 'object' || response === null)
|
|
11
|
+
return false;
|
|
12
|
+
const envelope = response;
|
|
13
|
+
if (typeof envelope.ok !== 'boolean')
|
|
14
|
+
return false;
|
|
15
|
+
if (typeof envelope.meta !== 'object' || envelope.meta === null || Array.isArray(envelope.meta))
|
|
16
|
+
return false;
|
|
17
|
+
if (envelope.ok === true) {
|
|
18
|
+
return 'data' in envelope;
|
|
19
|
+
}
|
|
20
|
+
return typeof envelope.error === 'object' && envelope.error !== null && !Array.isArray(envelope.error);
|
|
11
21
|
}
|
|
12
22
|
/** Check if response is successful */
|
|
13
23
|
export function isEnvelopeSuccess(response) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognitive-modules-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.15",
|
|
4
4
|
"description": "Cognitive Modules - Structured AI Task Execution with version management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"test": "vitest run",
|
|
25
25
|
"test:watch": "vitest",
|
|
26
26
|
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"bench:cognitive-vs-raw": "node benchmarks/cognitive-vs-raw/run.mjs",
|
|
27
28
|
"pack:check": "npm pack --dry-run --json --cache ../../.npm-cache",
|
|
28
|
-
"release:check": "npm run build && npm test && npm run pack:check",
|
|
29
|
+
"release:check": "node ../../scripts/release/check-docs-build.js && npm run build && npm test && npm run pack:check",
|
|
29
30
|
"prepublishOnly": "npm run release:check"
|
|
30
31
|
},
|
|
31
32
|
"keywords": [
|