deepccc 0.1.15 → 0.1.17

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/README.md CHANGED
@@ -53,7 +53,8 @@ export DEEPCCC_STREAMING="true"
53
53
  Windows PowerShell:
54
54
 
55
55
  ```powershell
56
- $env:DEEPCCC_API_KEY="sk-..."
56
+ $env:DEEPCCC_API_KEY="sk-..."
57
+ $env:DEEPCCC_PROVIDER="openai"
57
58
  $env:DEEPCCC_BASE_URL="https://api.deepseek.com/v1"
58
59
  $env:DEEPCCC_MODEL="deepseek-v4-pro"
59
60
  $env:DEEPCCC_STREAMING="true"
@@ -69,8 +70,9 @@ $env:DEEPCCC_STREAMING="true"
69
70
  也可以创建 `~/.deepccc/config.json`:
70
71
 
71
72
  ```json
72
- {
73
- "apiKey": "sk-...",
73
+ {
74
+ "provider": "openai",
75
+ "apiKey": "sk-...",
74
76
  "baseURL": "https://api.deepseek.com/v1",
75
77
  "model": "deepseek-v4-pro",
76
78
  "effort": "",
@@ -84,6 +86,12 @@ $env:DEEPCCC_STREAMING="true"
84
86
  }
85
87
  ```
86
88
 
89
+ `provider` 可选 `openai` 或 `anthropic`,默认 `openai`,也可以通过
90
+ `DEEPCCC_PROVIDER` 或命令行 `--provider` 覆盖:
91
+
92
+ - `openai` 使用 OpenAI-compatible Chat Completions 协议,兼容 DeepSeek、OpenAI、LiteLLM、vLLM 等服务。
93
+ - `anthropic` 使用 Anthropic Messages 协议。配置中的 `baseURL` 可以继续填写同一个服务基址;DeepCCC 会为不带 `/v1` 的地址自动补上 `/v1`。`effort` 是 OpenAI/DeepSeek 扩展参数,在 Anthropic 模式下不会发送。
94
+
87
95
  `streaming` 控制主对话是否使用流式请求,默认 `true`;也可以通过
88
96
  `DEEPCCC_STREAMING=true|false` 覆盖。关闭后,终端会在整条模型响应完成后一次性显示结果。
89
97
 
@@ -95,11 +103,17 @@ $env:DEEPCCC_STREAMING="true"
95
103
  deepccc
96
104
  ```
97
105
 
98
- 指定其他模型或 OpenAI-compatible 接口:
106
+ 指定其他模型或 OpenAI-compatible 接口:
99
107
 
100
108
  ```bash
101
- deepccc --base-url https://api.openai.com/v1 --api-key "$OPENAI_API_KEY" --model gpt-4.1
102
- ```
109
+ deepccc --base-url https://api.openai.com/v1 --api-key "$OPENAI_API_KEY" --model gpt-4.1
110
+ ```
111
+
112
+ 使用 Anthropic Messages 协议(同样支持流式输出):
113
+
114
+ ```bash
115
+ deepccc --provider anthropic --base-url https://api.example.com --api-key "$API_KEY" --model claude-sonnet-4-6
116
+ ```
103
117
 
104
118
  指定工作目录:
105
119
 
package/dist/cli.js CHANGED
@@ -31,6 +31,12 @@ function parsePositiveIntegerOption(name, value) {
31
31
  }
32
32
  return parsed;
33
33
  }
34
+ function parseProviderOption(value) {
35
+ const normalized = value.trim().toLowerCase();
36
+ if (normalized === "openai" || normalized === "anthropic")
37
+ return normalized;
38
+ throw new Error(`--provider must be "openai" or "anthropic", received: ${value}`);
39
+ }
34
40
  function parseArgs(argv = process.argv.slice(2)) {
35
41
  const config = {};
36
42
  const options = {};
@@ -43,7 +49,11 @@ function parseArgs(argv = process.argv.slice(2)) {
43
49
  for (let i = 0; i < argv.length; i++) {
44
50
  const arg = argv[i];
45
51
  const next = argv[i + 1];
46
- if (arg === "--model" && next !== undefined) {
52
+ if (arg === "--provider" && next !== undefined) {
53
+ config.provider = parseProviderOption(next);
54
+ i++;
55
+ }
56
+ else if (arg === "--model" && next !== undefined) {
47
57
  config.model = next;
48
58
  i++;
49
59
  }
@@ -113,9 +123,10 @@ function printHelp(appConfig) {
113
123
  "Usage: deepccc [options]",
114
124
  "",
115
125
  "Options:",
126
+ ` --provider <name> API protocol: openai or anthropic (current default ${appConfig.provider})`,
116
127
  ` --model <name> Model name (current default ${appConfig.model})`,
117
128
  ` --effort <level> Reasoning effort: none/minimal/low/medium/high/xhigh/max (overrides config.effort)`,
118
- ` --base-url <url> OpenAI-compatible API base URL (current default ${appConfig.baseURL})`,
129
+ ` --base-url <url> Provider API base URL (current default ${appConfig.baseURL})`,
119
130
  " --api-key <key> API key",
120
131
  " --cwd <path> Working directory",
121
132
  " --max-steps <n> Optional tool-step limit. Omit for no step limit",
@@ -348,6 +359,7 @@ async function runRepl(args) {
348
359
  process.exit(1);
349
360
  }
350
361
  console.log(`${C.dim}DeepCCC agent${C.reset}`);
362
+ console.log(`${C.dim}Provider: ${args.config.provider ?? appConfig.provider}${C.reset}`);
351
363
  console.log(`${C.dim}Model: ${args.config.model ?? appConfig.model}${C.reset}`);
352
364
  console.log(`${C.dim}Directory: ${cwd}${C.reset}`);
353
365
  console.log(`${C.dim}Session: ${resolvedSession.sessionId} (${resolvedSession.mode === "new" ? "new" : "resumed"})${C.reset}`);
package/dist/config.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ export type DeepCccProvider = "openai" | "anthropic";
1
2
  export interface DeepCccConfig {
3
+ /** API protocol/provider. Defaults to OpenAI-compatible. */
4
+ provider: DeepCccProvider;
2
5
  apiKey: string;
3
6
  baseURL: string;
4
7
  model: string;
@@ -15,5 +18,6 @@ export interface DeepCccConfig {
15
18
  }
16
19
  export declare const DEEPCCC_HOME: string;
17
20
  export declare const RAW_STREAM_LOGS_DIR: string;
21
+ export declare function normalizeDeepCccProvider(value: unknown): DeepCccProvider;
18
22
  export declare function ensureConfigDir(): void;
19
23
  export declare const config: DeepCccConfig;
package/dist/config.js CHANGED
@@ -5,6 +5,7 @@ export const DEEPCCC_HOME = join(homedir(), ".deepccc");
5
5
  export const RAW_STREAM_LOGS_DIR = join(DEEPCCC_HOME, "raw-stream-logs");
6
6
  const CONFIG_PATH = join(DEEPCCC_HOME, "config.json");
7
7
  const DEFAULT_CONFIG = {
8
+ provider: "openai",
8
9
  apiKey: "",
9
10
  baseURL: "https://api.deepseek.com/v1",
10
11
  model: "deepseek-v4-pro",
@@ -41,12 +42,21 @@ function numberEnv(name) {
41
42
  const value = Number(env(name));
42
43
  return Number.isFinite(value) && value >= 0 ? value : undefined;
43
44
  }
45
+ export function normalizeDeepCccProvider(value) {
46
+ if (value === undefined || value === null || String(value).trim() === "")
47
+ return "openai";
48
+ const normalized = String(value).trim().toLowerCase();
49
+ if (normalized === "openai" || normalized === "anthropic")
50
+ return normalized;
51
+ throw new Error(`DEEPCCC_PROVIDER/provider must be "openai" or "anthropic", received: ${String(value)}`);
52
+ }
44
53
  function loadConfig() {
45
54
  const file = readConfigFile();
46
55
  const rawLogs = file.rawStreamLogs && typeof file.rawStreamLogs === "object"
47
56
  ? file.rawStreamLogs
48
57
  : {};
49
58
  return {
59
+ provider: normalizeDeepCccProvider(env("DEEPCCC_PROVIDER") ?? file.provider ?? DEFAULT_CONFIG.provider),
50
60
  apiKey: env("DEEPCCC_API_KEY") ?? env("DEEPSEEK_API_KEY") ?? file.apiKey ?? DEFAULT_CONFIG.apiKey,
51
61
  baseURL: env("DEEPCCC_BASE_URL") ?? env("DEEPSEEK_BASE_URL") ?? file.baseURL ?? DEFAULT_CONFIG.baseURL,
52
62
  model: env("DEEPCCC_MODEL") ?? env("DEEPSEEK_MODEL") ?? file.model ?? DEFAULT_CONFIG.model,
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * ChatSession 是程序化入口,既可以被 CLI 调用,也可以被其他模块调用。
5
5
  */
6
+ import { type DeepCccProvider } from "./config.js";
6
7
  import { type PermissionMode, type PermissionResolver } from "./permissions.js";
7
8
  export declare const DEFAULT_COMPACTION_TIMEOUT_MS: number;
8
9
  /**
@@ -19,7 +20,9 @@ export declare function loadPlatformCommandPrompt(platform?: string, dirs?: {
19
20
  userDir?: string;
20
21
  }): string;
21
22
  export interface ChatSessionConfig {
22
- /** OpenAI-compatible service base URL. Defaults to DEEPCCC_BASE_URL/config. */
23
+ /** API protocol/provider. Defaults to DEEPCCC_PROVIDER/config, then openai. */
24
+ provider?: DeepCccProvider;
25
+ /** Provider service base URL. Defaults to DEEPCCC_BASE_URL/config. */
23
26
  baseURL?: string;
24
27
  /** API key. Defaults to DEEPCCC_API_KEY/config. */
25
28
  apiKey?: string;
@@ -107,6 +110,7 @@ interface ChatMessage {
107
110
  }
108
111
  export declare class ChatSession {
109
112
  private model;
113
+ private provider;
110
114
  private cwd;
111
115
  private context;
112
116
  private compactionTimeoutMs;
package/dist/index.js CHANGED
@@ -4,12 +4,13 @@
4
4
  * ChatSession 是程序化入口,既可以被 CLI 调用,也可以被其他模块调用。
5
5
  */
6
6
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
7
+ import { createAnthropic } from "@ai-sdk/anthropic";
7
8
  import { generateText, isLoopFinished, stepCountIs, streamText } from "ai";
8
9
  import { existsSync, readFileSync } from "node:fs";
9
10
  import { homedir } from "node:os";
10
11
  import { join } from "node:path";
11
12
  import { fileURLToPath } from "node:url";
12
- import { config as appConfig, RAW_STREAM_LOGS_DIR } from "./config.js";
13
+ import { config as appConfig, normalizeDeepCccProvider, RAW_STREAM_LOGS_DIR, } from "./config.js";
13
14
  import { createRawStreamLog, } from "./raw-stream-log.js";
14
15
  import { buildPersistedAssistantMessage, buildSummaryPrompt, BuiltinContextManager, defaultBuiltinSessionId, } from "./context.js";
15
16
  import { createBuiltinFileTools } from "./file-tools.js";
@@ -147,8 +148,13 @@ function normalizeMaxSteps(value) {
147
148
  }
148
149
  return value;
149
150
  }
151
+ function normalizeAnthropicBaseURL(baseURL) {
152
+ const normalized = baseURL.trim().replace(/\/+$/, "");
153
+ return normalized.endsWith("/v1") ? normalized : `${normalized}/v1`;
154
+ }
150
155
  export class ChatSession {
151
156
  model;
157
+ provider;
152
158
  cwd;
153
159
  context;
154
160
  compactionTimeoutMs;
@@ -166,14 +172,24 @@ export class ChatSession {
166
172
  }
167
173
  const baseURL = overrides.baseURL ?? appConfig.baseURL;
168
174
  const modelId = overrides.model ?? appConfig.model;
175
+ this.provider = normalizeDeepCccProvider(overrides.provider ?? appConfig.provider);
169
176
  this.effort = (overrides.effort ?? appConfig.effort ?? "").trim();
170
- const provider = createOpenAICompatible({
171
- name: "deepccc",
172
- baseURL,
173
- apiKey,
174
- includeUsage: true,
175
- });
176
- this.model = provider(modelId);
177
+ if (this.provider === "anthropic") {
178
+ const provider = createAnthropic({
179
+ baseURL: normalizeAnthropicBaseURL(baseURL),
180
+ apiKey,
181
+ });
182
+ this.model = provider(modelId);
183
+ }
184
+ else {
185
+ const provider = createOpenAICompatible({
186
+ name: "deepccc",
187
+ baseURL,
188
+ apiKey,
189
+ includeUsage: true,
190
+ });
191
+ this.model = provider(modelId);
192
+ }
177
193
  this.cwd = options.cwd ?? process.cwd();
178
194
  this.maxSteps = normalizeMaxSteps(options.maxSteps);
179
195
  this.compactionTimeoutMs = Math.max(1, options.compactionTimeoutMs ?? DEFAULT_COMPACTION_TIMEOUT_MS);
@@ -260,6 +276,17 @@ export class ChatSession {
260
276
  const skills = await scanSkillsDirs(this.skillDirs);
261
277
  const system = this.buildSystemPrompt(skills);
262
278
  this.systemPrompt = system;
279
+ // effort 按协议映射:
280
+ // - OpenAI 兼容:providerOptions.deepseek.reasoningEffort 由 @ai-sdk/openai-compatible
281
+ // 自动映射为请求体 reasoning_effort 字段(DeepSeek 原生支持);
282
+ // - Anthropic:providerOptions.anthropic.effort 由 @ai-sdk/anthropic 组装为请求体
283
+ // output_config.effort(官方 Effort API,见 platform.claude.com/docs/en/build-with-claude/effort)
284
+ let effortProviderOptions;
285
+ if (this.effort) {
286
+ effortProviderOptions = this.provider === "openai"
287
+ ? { deepseek: { reasoningEffort: this.effort } }
288
+ : { anthropic: { effort: this.effort } };
289
+ }
263
290
  const generationOptions = {
264
291
  model: this.model,
265
292
  system,
@@ -267,9 +294,7 @@ export class ChatSession {
267
294
  tools: createBuiltinFileTools(this.cwd, { permissionGate: this.permissionGate }),
268
295
  stopWhen: maxSteps !== undefined ? stepCountIs(maxSteps) : isLoopFinished(),
269
296
  abortSignal: signal,
270
- // DeepSeek OpenAI 兼容接口:providerOptions.deepseek.reasoningEffort
271
- // 由 @ai-sdk/openai-compatible 自动映射为请求体 reasoning_effort 字段
272
- ...(this.effort ? { providerOptions: { deepseek: { reasoningEffort: this.effort } } } : {}),
297
+ ...(effortProviderOptions ? { providerOptions: effortProviderOptions } : {}),
273
298
  };
274
299
  let stream;
275
300
  if (appConfig.streaming) {
@@ -413,6 +438,11 @@ export class ChatSession {
413
438
  messages: [{ role: "user", content: buildSummaryPrompt(plan) }],
414
439
  abortSignal: compactionSignal,
415
440
  temperature: 0,
441
+ // 压缩是摘要类任务:显式锁低 effort(OpenAI reasoning_effort=none / Anthropic
442
+ // output_config.effort=low),避免继承主对话的高 effort 拖慢"压缩上下文中"阶段
443
+ providerOptions: this.provider === "openai"
444
+ ? { deepseek: { reasoningEffort: "none" } }
445
+ : { anthropic: { effort: "low" } },
416
446
  });
417
447
  if (!result.text.trim()) {
418
448
  throw new Error("Context compaction returned an empty summary");
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "deepccc",
3
- "version": "0.1.15",
4
- "description": "A lightweight DeepSeek-optimized coding agent with OpenAI-compatible model support.",
3
+ "version": "0.1.17",
4
+ "description": "A lightweight coding agent with OpenAI-compatible and Anthropic Messages API support.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
7
7
  "agent",
8
8
  "deepseek",
9
9
  "coding-agent",
10
10
  "openai-compatible",
11
+ "anthropic",
11
12
  "cli"
12
13
  ],
13
14
  "repository": {
@@ -47,6 +48,7 @@
47
48
  "typecheck": "node node_modules/typescript/bin/tsc --noEmit"
48
49
  },
49
50
  "dependencies": {
51
+ "@ai-sdk/anthropic": "^3.0.105",
50
52
  "@ai-sdk/openai-compatible": "^2.0.47",
51
53
  "@vscode/ripgrep": "^1.18.0",
52
54
  "ai": "^6.0.184"