chatccc 0.2.258 → 0.2.260
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 +20 -11
- package/config.sample.json +23 -8
- package/deepccc-agent/package.json +1 -1
- package/dist/deepccc-agent/src/context.js +12 -3
- package/dist/deepccc-agent/src/index.js +116 -73
- package/dist/deepccc-agent/src/progress/reducer.js +4 -0
- package/dist/deepccc-agent/src/tool-protocol.js +17 -0
- package/dist/src/adapters/ccc-adapter.js +12 -0
- package/dist/src/adapters/claude-adapter.js +3 -2
- package/dist/src/adapters/dsh-adapter.js +230 -0
- package/dist/src/agent-activity.js +4 -0
- package/dist/src/agent-tool.js +2 -1
- package/dist/src/card-action-parser.js +1 -0
- package/dist/src/cards.js +11 -3
- package/dist/src/config.js +34 -2
- package/dist/src/engines/engine-manager.js +408 -0
- package/dist/src/engines/engine-specs.js +157 -0
- package/dist/src/feishu-api.js +2 -1
- package/dist/src/orchestrator.js +21 -10
- package/dist/src/progress/reducer.js +4 -0
- package/dist/src/response-stall.js +2 -2
- package/dist/src/session.js +43 -4
- package/dist/src/web-ui.js +286 -200
- package/package.json +1 -1
- package/dist/src/claude-sdk-installer.js +0 -249
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { mkdir } from "node:fs/promises";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { pathToFileURL } from "node:url";
|
|
14
|
+
import { engineManager } from "../engines/engine-specs.js";
|
|
15
|
+
import { config, RAW_STREAM_LOGS_DIR } from "../config.js";
|
|
16
|
+
import { createRawStreamLog } from "./raw-stream-log.js";
|
|
17
|
+
let injectedSdk = null;
|
|
18
|
+
const knownSessions = new Map();
|
|
19
|
+
export function __setDshSdkModuleForTest(module) {
|
|
20
|
+
injectedSdk = module;
|
|
21
|
+
}
|
|
22
|
+
export function createDshAdapter(options = {}) {
|
|
23
|
+
return {
|
|
24
|
+
displayName: "DeepSeek Harness",
|
|
25
|
+
sessionDescPrefix: "DSH Session:",
|
|
26
|
+
responseStallDetectionEnabled: true,
|
|
27
|
+
async createSession(cwd) {
|
|
28
|
+
await engineManager.getEntryPath("dsh");
|
|
29
|
+
const sessionId = `dsh-${randomUUID()}`;
|
|
30
|
+
knownSessions.set(sessionId, { sessionId, cwd, lastModified: Date.now(), model: options.model ?? "deepseek-v4-flash" });
|
|
31
|
+
return { sessionId };
|
|
32
|
+
},
|
|
33
|
+
async *prompt(sessionId, userText, cwd, signal, promptOptions) {
|
|
34
|
+
const entryPath = await engineManager.getEntryPath("dsh");
|
|
35
|
+
const installationDir = resolve(dirname(entryPath), "../../../..");
|
|
36
|
+
const sdk = injectedSdk ?? await import(__rewriteRelativeImportExtension(pathToFileURL(entryPath).href));
|
|
37
|
+
const sessionRoot = join(homedir(), ".chatccc", "dsh-sessions");
|
|
38
|
+
await mkdir(sessionRoot, { recursive: true });
|
|
39
|
+
const runtime = new sdk.DeepSeekHarness({
|
|
40
|
+
launch: {
|
|
41
|
+
command: process.execPath,
|
|
42
|
+
args: [
|
|
43
|
+
join(installationDir, "node_modules", "@deepseek-ai", "dsh-sdk-jsonrpc-demo", "lib", "bin.js"),
|
|
44
|
+
join(installationDir, "dsh-runtime.cordis.yml"),
|
|
45
|
+
],
|
|
46
|
+
cwd,
|
|
47
|
+
env: {
|
|
48
|
+
...process.env,
|
|
49
|
+
DSH_CWD: cwd,
|
|
50
|
+
DSH_SESSION_ROOT: sessionRoot,
|
|
51
|
+
...(options.apiKey ? { DEEPSEEK_API_KEY: options.apiKey } : {}),
|
|
52
|
+
...(options.baseUrl ? { DEEPSEEK_BASE_URL: options.baseUrl } : {}),
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
cwd,
|
|
56
|
+
provider: options.provider ?? "deepseek-official",
|
|
57
|
+
model: options.model ?? "deepseek-v4-flash",
|
|
58
|
+
...(options.maxTokens ? { maxTokens: options.maxTokens } : {}),
|
|
59
|
+
});
|
|
60
|
+
const queue = new AsyncMessageQueue();
|
|
61
|
+
const rawLogConfig = config.rawStreamLogs.dsh;
|
|
62
|
+
let rawLog = null;
|
|
63
|
+
try {
|
|
64
|
+
rawLog = await createRawStreamLog({
|
|
65
|
+
enabled: rawLogConfig.enabled,
|
|
66
|
+
rootDir: RAW_STREAM_LOGS_DIR,
|
|
67
|
+
tool: "dsh",
|
|
68
|
+
sessionId,
|
|
69
|
+
label: "prompt",
|
|
70
|
+
maxBytesPerTurn: rawLogConfig.maxBytesPerTurn,
|
|
71
|
+
retentionDays: rawLogConfig.retentionDays,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`[DSH raw stream log] create failed: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
let closed = false;
|
|
78
|
+
let completed = false;
|
|
79
|
+
const closeRuntime = () => {
|
|
80
|
+
if (closed)
|
|
81
|
+
return;
|
|
82
|
+
closed = true;
|
|
83
|
+
void runtime.close();
|
|
84
|
+
};
|
|
85
|
+
promptOptions?.onSessionCreated?.(closeRuntime);
|
|
86
|
+
const onAbort = () => closeRuntime();
|
|
87
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
88
|
+
const task = (async () => {
|
|
89
|
+
try {
|
|
90
|
+
await runtime.start();
|
|
91
|
+
const result = await runtime.run(userText, {
|
|
92
|
+
sessionId,
|
|
93
|
+
onNotification: (notification) => {
|
|
94
|
+
rawLog?.writeLine(JSON.stringify(notification));
|
|
95
|
+
const message = notificationToMessage(notification);
|
|
96
|
+
if (message)
|
|
97
|
+
queue.push(message);
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
if (result.finalResponse) {
|
|
101
|
+
rawLog?.writeLine(JSON.stringify({ type: "run.result", result }));
|
|
102
|
+
queue.push({ type: "assistant", blocks: [{ type: "text_final", text: result.finalResponse }], isFinalResponse: true });
|
|
103
|
+
}
|
|
104
|
+
completed = true;
|
|
105
|
+
knownSessions.set(sessionId, { sessionId, cwd, lastModified: Date.now(), model: options.model ?? "deepseek-v4-flash" });
|
|
106
|
+
queue.end();
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
queue.fail(error instanceof Error ? error : new Error(String(error)));
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
signal?.removeEventListener("abort", onAbort);
|
|
113
|
+
await runtime.close().catch(() => { });
|
|
114
|
+
await rawLog?.close({ keep: rawLogConfig.keepCompleted || signal?.aborted === true || !completed });
|
|
115
|
+
closed = true;
|
|
116
|
+
}
|
|
117
|
+
})();
|
|
118
|
+
try {
|
|
119
|
+
for await (const message of queue)
|
|
120
|
+
yield message;
|
|
121
|
+
await task;
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
closeRuntime();
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
async getSessionInfo(sessionId) {
|
|
128
|
+
return knownSessions.get(sessionId) ?? { sessionId };
|
|
129
|
+
},
|
|
130
|
+
async closeSession() {
|
|
131
|
+
// Each prompt owns and closes its runtime. Durable state lives under ~/.chatccc/dsh-sessions.
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function notificationToMessage(notification) {
|
|
136
|
+
if (notification.method === "session.status") {
|
|
137
|
+
return notification.params.status === "running"
|
|
138
|
+
? { type: "system", blocks: [{ type: "agent_status", status: "responding" }] }
|
|
139
|
+
: null;
|
|
140
|
+
}
|
|
141
|
+
if (notification.method === "subagent.started") {
|
|
142
|
+
return { type: "system", blocks: [{ type: "agent_progress", phase: "reasoning" }] };
|
|
143
|
+
}
|
|
144
|
+
if (notification.method !== "session.event")
|
|
145
|
+
return null;
|
|
146
|
+
const event = asRecord(notification.params.event);
|
|
147
|
+
if (!event)
|
|
148
|
+
return null;
|
|
149
|
+
const data = asRecord(event.data);
|
|
150
|
+
if (!data)
|
|
151
|
+
return null;
|
|
152
|
+
const blocks = [];
|
|
153
|
+
if (event.type === "assistant/chunk") {
|
|
154
|
+
const chunk = asRecord(data.chunk);
|
|
155
|
+
if (chunk?.type === "text-delta" && typeof chunk.text === "string" && chunk.text) {
|
|
156
|
+
blocks.push({ type: "text", text: chunk.text });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (event.type === "tool/call") {
|
|
160
|
+
const raw = typeof data.arguments === "string" ? data.arguments : "{}";
|
|
161
|
+
let input = raw;
|
|
162
|
+
try {
|
|
163
|
+
input = JSON.parse(raw);
|
|
164
|
+
}
|
|
165
|
+
catch { /* retain raw provider arguments */ }
|
|
166
|
+
blocks.push({
|
|
167
|
+
type: "tool_use",
|
|
168
|
+
...(typeof data.callId === "string" ? { id: data.callId } : {}),
|
|
169
|
+
name: typeof data.name === "string" ? data.name : "tool",
|
|
170
|
+
input,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (event.type === "tool/result") {
|
|
174
|
+
const message = asRecord(data.message);
|
|
175
|
+
const callId = typeof message?.toolCallId === "string"
|
|
176
|
+
? message.toolCallId
|
|
177
|
+
: typeof message?.callId === "string"
|
|
178
|
+
? message.callId
|
|
179
|
+
: "";
|
|
180
|
+
blocks.push({
|
|
181
|
+
type: "tool_result",
|
|
182
|
+
tool_use_id: callId,
|
|
183
|
+
content: message?.content ?? data.message,
|
|
184
|
+
...(data.error ? { is_error: true } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
else if (event.type === "turn/start" || event.type === "step/start") {
|
|
188
|
+
blocks.push({ type: "agent_progress", phase: "reasoning" });
|
|
189
|
+
}
|
|
190
|
+
return blocks.length ? { type: "assistant", blocks } : null;
|
|
191
|
+
}
|
|
192
|
+
function asRecord(value) {
|
|
193
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
194
|
+
? value
|
|
195
|
+
: null;
|
|
196
|
+
}
|
|
197
|
+
class AsyncMessageQueue {
|
|
198
|
+
values = [];
|
|
199
|
+
waiters = [];
|
|
200
|
+
done = false;
|
|
201
|
+
error = null;
|
|
202
|
+
push(value) {
|
|
203
|
+
if (this.done)
|
|
204
|
+
return;
|
|
205
|
+
this.values.push(value);
|
|
206
|
+
this.waiters.shift()?.();
|
|
207
|
+
}
|
|
208
|
+
end() {
|
|
209
|
+
this.done = true;
|
|
210
|
+
while (this.waiters.length)
|
|
211
|
+
this.waiters.shift()?.();
|
|
212
|
+
}
|
|
213
|
+
fail(error) {
|
|
214
|
+
this.error = error;
|
|
215
|
+
this.end();
|
|
216
|
+
}
|
|
217
|
+
async *[Symbol.asyncIterator]() {
|
|
218
|
+
while (true) {
|
|
219
|
+
if (this.values.length) {
|
|
220
|
+
yield this.values.shift();
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (this.error)
|
|
224
|
+
throw this.error;
|
|
225
|
+
if (this.done)
|
|
226
|
+
return;
|
|
227
|
+
await new Promise((resolvePromise) => this.waiters.push(resolvePromise));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -67,6 +67,10 @@ export function updateAgentActivity(tracker, block, now = Date.now()) {
|
|
|
67
67
|
if (tracker.activeTools.size > 0)
|
|
68
68
|
return false;
|
|
69
69
|
switch (block.type) {
|
|
70
|
+
case "agent_progress":
|
|
71
|
+
return setActivity(tracker, { kind: "thinking", startedAt: now });
|
|
72
|
+
case "text_reset":
|
|
73
|
+
return setActivity(tracker, { kind: "responding", startedAt: now });
|
|
70
74
|
case "agent_status":
|
|
71
75
|
return setActivity(tracker, {
|
|
72
76
|
kind: block.status === "compacting" ? "compacting" : "responding",
|
package/dist/src/agent-tool.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export const AGENT_TOOLS = ["claude", "cursor", "codex", "ccc"];
|
|
1
|
+
export const AGENT_TOOLS = ["claude", "cursor", "codex", "ccc", "dsh"];
|
|
2
2
|
export const AGENT_TOOL_OPTIONS = [
|
|
3
3
|
{ id: "ccc", label: "CCC" },
|
|
4
4
|
{ id: "claude", label: "Claude" },
|
|
5
5
|
{ id: "cursor", label: "Cursor" },
|
|
6
6
|
{ id: "codex", label: "Codex" },
|
|
7
|
+
{ id: "dsh", label: "DeepSeek Harness" },
|
|
7
8
|
];
|
|
8
9
|
export function isAgentTool(value) {
|
|
9
10
|
return typeof value === "string" && AGENT_TOOLS.includes(value);
|
package/dist/src/cards.js
CHANGED
|
@@ -137,6 +137,7 @@ export function buildHelpCard(userText, opts = {}) {
|
|
|
137
137
|
"发送 **/new cursor** 创建新 Cursor 会话",
|
|
138
138
|
"发送 **/new codex** 创建新 Codex 会话",
|
|
139
139
|
"发送 **/new ccc** 创建新 CCC Agent 会话",
|
|
140
|
+
"发送 **/new dsh** 创建新 DeepSeek Harness 会话",
|
|
140
141
|
"发送 **/forget** 重置当前会话(忘掉上下文,沿用当前工作目录,不切换)",
|
|
141
142
|
"发送 **/plan** 以规划模式提问(只读,不执行写操作)",
|
|
142
143
|
"发送 **/ask** 以问答模式提问(只读,不执行写操作)",
|
|
@@ -157,6 +158,7 @@ export function buildHelpCard(userText, opts = {}) {
|
|
|
157
158
|
{ text: "新建 Cursor 会话(/new cursor)", value: JSON.stringify({ cmd: "new cursor" }), type: "primary" },
|
|
158
159
|
{ text: "新建 Codex 会话(/new codex)", value: JSON.stringify({ cmd: "new codex" }), type: "primary" },
|
|
159
160
|
{ text: "新建 CCC Agent 会话(/new ccc)", value: JSON.stringify({ cmd: "new ccc" }), type: "primary" },
|
|
161
|
+
{ text: "新建 DeepSeek Harness 会话(/new dsh)", value: JSON.stringify({ cmd: "new dsh" }), type: "primary" },
|
|
160
162
|
{ text: "重启 ChatCCC(/restart)", value: JSON.stringify({ cmd: "restart" }), type: "danger" },
|
|
161
163
|
{ text: "更新并重启(/update)", value: JSON.stringify({ cmd: "update" }), type: "danger" },
|
|
162
164
|
{ text: "切换工作路径(/cd)", value: JSON.stringify({ cmd: "cd" }), type: "default" },
|
|
@@ -253,6 +255,8 @@ function sessionToolLabel(tool) {
|
|
|
253
255
|
return "Codex";
|
|
254
256
|
if (tool === "ccc")
|
|
255
257
|
return "CCC Agent";
|
|
258
|
+
if (tool === "dsh")
|
|
259
|
+
return "DeepSeek Harness";
|
|
256
260
|
return "Claude Code";
|
|
257
261
|
}
|
|
258
262
|
function pushSessionGroup(lines, title, sessions, formatSession, index) {
|
|
@@ -271,22 +275,24 @@ export function buildSessionsCard(sessions, opts = {}) {
|
|
|
271
275
|
const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
|
|
272
276
|
const fixedPrivateSession = opts.fixedPrivateSession ?? false;
|
|
273
277
|
// 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
|
|
274
|
-
const claudeCodeSessions = sessions.filter(s => s.tool
|
|
278
|
+
const claudeCodeSessions = sessions.filter(s => s.tool === "claude");
|
|
275
279
|
const cursorSessions = sessions.filter(s => s.tool === "cursor");
|
|
276
280
|
const codexSessions = sessions.filter(s => s.tool === "codex");
|
|
277
281
|
const cccSessions = sessions.filter(s => s.tool === "ccc");
|
|
282
|
+
const dshSessions = sessions.filter(s => s.tool === "dsh");
|
|
278
283
|
const hasClaudeCode = claudeCodeSessions.length > 0;
|
|
279
284
|
const hasCursor = cursorSessions.length > 0;
|
|
280
285
|
const hasCodex = codexSessions.length > 0;
|
|
281
286
|
const hasCcc = cccSessions.length > 0;
|
|
287
|
+
const hasDsh = dshSessions.length > 0;
|
|
282
288
|
if (sessions.length === 0) {
|
|
283
289
|
return JSON.stringify({
|
|
284
290
|
config: { wide_screen_mode: true },
|
|
285
291
|
header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
|
|
286
292
|
elements: [
|
|
287
293
|
{ tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
|
|
288
|
-
? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;默认 Agent 变化后,下一条普通消息会创建对应 Agent 的新空会话。发送 **/new**、**/new claude**、**/new cursor**、**/new codex** 或 **/new
|
|
289
|
-
: `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor**、**/new codex** 或 **/new
|
|
294
|
+
? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;默认 Agent 变化后,下一条普通消息会创建对应 Agent 的新空会话。发送 **/new**、**/new claude**、**/new cursor**、**/new codex**、**/new ccc** 或 **/new dsh** 会另外创建会话群。`
|
|
295
|
+
: `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor**、**/new codex**、**/new ccc** 或 **/new dsh** 创建新会话。\n创建后可在任意会话群内发送 **/sessions** 查看列表,用 **/session 数字** 切换会话。` } },
|
|
290
296
|
{ tag: "hr" },
|
|
291
297
|
{ tag: "action", actions: [{ tag: "button", text: { tag: "plain_text", content: "收起" }, type: "default", value: { action: "close" } }] },
|
|
292
298
|
],
|
|
@@ -322,6 +328,8 @@ export function buildSessionsCard(sessions, opts = {}) {
|
|
|
322
328
|
pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
|
|
323
329
|
if (hasCcc)
|
|
324
330
|
pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
|
|
331
|
+
if (hasDsh)
|
|
332
|
+
pushSessionGroup(lines, "DeepSeek Harness 会话", dshSessions, formatSession, idx);
|
|
325
333
|
return JSON.stringify({
|
|
326
334
|
config: { wide_screen_mode: true },
|
|
327
335
|
header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
|
package/dist/src/config.js
CHANGED
|
@@ -58,6 +58,9 @@ export function getAllModelsForTool(tool, cfg = config) {
|
|
|
58
58
|
collect(cfg.ccc.model);
|
|
59
59
|
collect(cfg.ccc.alternativeModel);
|
|
60
60
|
}
|
|
61
|
+
else if (tool === "dsh") {
|
|
62
|
+
collect(cfg.dsh.model);
|
|
63
|
+
}
|
|
61
64
|
return Array.from(seen).slice(0, 100);
|
|
62
65
|
}
|
|
63
66
|
export const CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -291,6 +294,7 @@ function loadConfig() {
|
|
|
291
294
|
cursor: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
292
295
|
codex: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
293
296
|
ccc: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
297
|
+
dsh: { enabled: false, maxBytesPerTurn: 50 * 1024 * 1024, retentionDays: 7, keepCompleted: false },
|
|
294
298
|
},
|
|
295
299
|
claude: { enabled: false, defaultAgent: true, model: "", subagentModel: "", effort: "", apiKey: "", baseUrl: "", maxTurn: 0 },
|
|
296
300
|
cursor: {
|
|
@@ -316,6 +320,15 @@ function loadConfig() {
|
|
|
316
320
|
compactionTimeoutMs: DEFAULT_CCC_COMPACTION_TIMEOUT_MS,
|
|
317
321
|
contextWindow: DEFAULT_CCC_CONTEXT_WINDOW_TOKENS,
|
|
318
322
|
},
|
|
323
|
+
dsh: {
|
|
324
|
+
enabled: false,
|
|
325
|
+
defaultAgent: false,
|
|
326
|
+
apiKey: "",
|
|
327
|
+
baseUrl: DEFAULT_CCC_DEEPSEEK_BASE_URL,
|
|
328
|
+
model: "deepseek-v4-flash",
|
|
329
|
+
provider: "deepseek-official",
|
|
330
|
+
maxTokens: 49152,
|
|
331
|
+
},
|
|
319
332
|
};
|
|
320
333
|
if (!IS_TEST_ENV) {
|
|
321
334
|
migrateLegacyData();
|
|
@@ -368,6 +381,7 @@ function loadConfig() {
|
|
|
368
381
|
const cursorRaw = (parsed.cursor ?? {});
|
|
369
382
|
const codexRaw = (parsed.codex ?? {});
|
|
370
383
|
const cccRaw = (parsed.ccc ?? {});
|
|
384
|
+
const dshRaw = (parsed.dsh ?? {});
|
|
371
385
|
const webUiRaw = (parsed.webUi ?? {});
|
|
372
386
|
const chromeDevtoolsRaw = (parsed.chromeDevtools ?? {});
|
|
373
387
|
const rawStreamLogsRaw = typeof parsed.rawStreamLogs === "object" && parsed.rawStreamLogs !== null
|
|
@@ -408,17 +422,20 @@ function loadConfig() {
|
|
|
408
422
|
const cursorEnabled = resolveEnabled(cursorRaw.enabled, cursorNonEmpty);
|
|
409
423
|
const codexEnabled = resolveEnabled(codexRaw.enabled, codexNonEmpty);
|
|
410
424
|
const cccEnabled = resolveCccEnabled(cccRaw.enabled, cccRaw.DEEPSEEK_API_KEY);
|
|
425
|
+
const dshEnabled = resolveEnabled(dshRaw.enabled, () => Boolean(typeof dshRaw.apiKey === "string" && dshRaw.apiKey.trim()));
|
|
411
426
|
const chromeDevtoolsPort = Number(chromeDevtoolsRaw.port);
|
|
412
427
|
const explicitDefaultTool = typeof claude.defaultAgent === "boolean" && claude.defaultAgent && claudeEnabled ? "claude" :
|
|
413
428
|
typeof cursorRaw.defaultAgent === "boolean" && cursorRaw.defaultAgent && cursorEnabled ? "cursor" :
|
|
414
429
|
typeof codexRaw.defaultAgent === "boolean" && codexRaw.defaultAgent && codexEnabled ? "codex" :
|
|
415
430
|
typeof cccRaw.defaultAgent === "boolean" && cccRaw.defaultAgent && cccEnabled ? "ccc" :
|
|
416
|
-
|
|
431
|
+
typeof dshRaw.defaultAgent === "boolean" && dshRaw.defaultAgent && dshEnabled ? "dsh" :
|
|
432
|
+
null;
|
|
417
433
|
const fallbackDefaultTool = claudeEnabled ? "claude" :
|
|
418
434
|
cursorEnabled ? "cursor" :
|
|
419
435
|
codexEnabled ? "codex" :
|
|
420
436
|
cccEnabled ? "ccc" :
|
|
421
|
-
"
|
|
437
|
+
dshEnabled ? "dsh" :
|
|
438
|
+
"claude";
|
|
422
439
|
const defaultTool = explicitDefaultTool ?? fallbackDefaultTool;
|
|
423
440
|
return {
|
|
424
441
|
feishu: {
|
|
@@ -462,6 +479,7 @@ function loadConfig() {
|
|
|
462
479
|
cursor: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.cursor),
|
|
463
480
|
codex: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.codex),
|
|
464
481
|
ccc: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.ccc),
|
|
482
|
+
dsh: normalizeRawStreamAgentLogConfig(rawStreamLogsRaw.dsh),
|
|
465
483
|
},
|
|
466
484
|
claude: {
|
|
467
485
|
enabled: claudeEnabled,
|
|
@@ -509,6 +527,15 @@ function loadConfig() {
|
|
|
509
527
|
compactionTimeoutMs: normalizePositiveInteger(cccRaw.compactionTimeoutMs, DEFAULT_CCC_COMPACTION_TIMEOUT_MS),
|
|
510
528
|
contextWindow: normalizePositiveInteger(cccRaw.contextWindow, DEFAULT_CCC_CONTEXT_WINDOW_TOKENS),
|
|
511
529
|
},
|
|
530
|
+
dsh: {
|
|
531
|
+
enabled: dshEnabled,
|
|
532
|
+
defaultAgent: defaultTool === "dsh",
|
|
533
|
+
apiKey: normalizeOptionalConfigField(dshRaw.apiKey, { label: "dsh.apiKey" }),
|
|
534
|
+
baseUrl: normalizeOptionalConfigField(dshRaw.baseUrl, { label: "dsh.baseUrl", fallback: DEFAULT_CCC_DEEPSEEK_BASE_URL }),
|
|
535
|
+
model: normalizeOptionalConfigField(dshRaw.model, { label: "dsh.model", fallback: "deepseek-v4-flash" }),
|
|
536
|
+
provider: normalizeOptionalConfigField(dshRaw.provider, { label: "dsh.provider", fallback: "deepseek-official" }),
|
|
537
|
+
maxTokens: normalizePositiveInteger(dshRaw.maxTokens, 49152),
|
|
538
|
+
},
|
|
512
539
|
};
|
|
513
540
|
}
|
|
514
541
|
/**
|
|
@@ -781,6 +808,7 @@ export const CURSOR_SESSION_PREFIX = "Cursor Session:";
|
|
|
781
808
|
export const CODEX_SESSION_PREFIX = "Codex Session:";
|
|
782
809
|
/** 群描述中用于识别 CCC Agent 会话的前缀 */
|
|
783
810
|
export const CCC_SESSION_PREFIX = "CCC Session:";
|
|
811
|
+
export const DSH_SESSION_PREFIX = "DSH Session:";
|
|
784
812
|
/** 根据 tool 名称返回对应的群描述前缀 */
|
|
785
813
|
export function sessionPrefixForTool(tool) {
|
|
786
814
|
if (tool === "cursor")
|
|
@@ -789,6 +817,8 @@ export function sessionPrefixForTool(tool) {
|
|
|
789
817
|
return CODEX_SESSION_PREFIX;
|
|
790
818
|
if (tool === "ccc")
|
|
791
819
|
return CCC_SESSION_PREFIX;
|
|
820
|
+
if (tool === "dsh")
|
|
821
|
+
return DSH_SESSION_PREFIX;
|
|
792
822
|
return CLAUDE_SESSION_PREFIX;
|
|
793
823
|
}
|
|
794
824
|
/** 根据 tool 名称返回用于状态展示的标签 */
|
|
@@ -799,6 +829,8 @@ export function toolDisplayName(tool) {
|
|
|
799
829
|
return "Codex";
|
|
800
830
|
if (tool === "ccc")
|
|
801
831
|
return "CCC Agent";
|
|
832
|
+
if (tool === "dsh")
|
|
833
|
+
return "DeepSeek Harness";
|
|
802
834
|
return "Claude Code";
|
|
803
835
|
}
|
|
804
836
|
/** 解析 /new 未指定工具时使用的默认 Agent。旧配置缺省 defaultAgent 时保持 Claude 优先。 */
|