jinzd-ai-cli 0.4.276 → 0.4.278
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/{batch-F7SKIBI3.js → batch-7Y4NAA6N.js} +2 -2
- package/dist/{chunk-VF7DMSKF.js → chunk-2BYQP6JX.js} +1 -1
- package/dist/{chunk-GLSR2NR3.js → chunk-4DEMGCK4.js} +4 -3
- package/dist/{chunk-MXPUOF5K.js → chunk-4Z6GAYB5.js} +4 -4
- package/dist/{chunk-NXAHXWPF.js → chunk-H7QC5VMQ.js} +1 -1
- package/dist/{chunk-MCKUGWOY.js → chunk-JJCNNQTU.js} +183 -3
- package/dist/{chunk-AOISQVXS.js → chunk-L3CPM472.js} +1 -1
- package/dist/{chunk-ATD2Y3PB.js → chunk-LOXEYWC3.js} +1 -1
- package/dist/{chunk-WYDP2WEQ.js → chunk-LTZFUYBT.js} +4 -4
- package/dist/{chunk-4NH4Q7QK.js → chunk-LXXQTWT2.js} +1 -1
- package/dist/{chunk-QCG6UU2S.js → chunk-PPIISCFN.js} +4 -3
- package/dist/{chunk-KWSGSSWN.js → chunk-RFSKPCYC.js} +1 -1
- package/dist/{chunk-OMBKLWLJ.js → chunk-SB4XONS4.js} +1 -1
- package/dist/{chunk-UNL7NR55.js → chunk-SJCOE4C7.js} +1 -1
- package/dist/{chunk-4KTK2OYU.js → chunk-UGY5SVNX.js} +46 -20
- package/dist/{chunk-NXNXWM6H.js → chunk-WOXIKQRI.js} +16 -7
- package/dist/{chunk-FLDYAXJR.js → chunk-X3722RFA.js} +1 -1
- package/dist/{ci-VURFRMWQ.js → ci-WDF2CYR4.js} +5 -5
- package/dist/{ci-format-AFJJIURF.js → ci-format-7B5VAZGA.js} +2 -2
- package/dist/{constants-WO7ERENL.js → constants-DC3TNLTL.js} +1 -1
- package/dist/{doctor-cli-BGGILTHE.js → doctor-cli-CDDG5XOY.js} +5 -5
- package/dist/electron-server.js +378 -78
- package/dist/{hub-Z73RLNJI.js → hub-TO7ODOIA.js} +1 -1
- package/dist/index.js +175 -188
- package/dist/{indexer-M2LJGTY2.js → indexer-RBEKMFT4.js} +1 -1
- package/dist/{indexer-VZETNE4H.js → indexer-TNDWGFRI.js} +1 -1
- package/dist/{persistent-memory-5QMARQJF.js → persistent-memory-BU63CSGK.js} +2 -2
- package/dist/{persistent-memory-CI544RLT.js → persistent-memory-JCENBMCJ.js} +2 -2
- package/dist/{pr-24REDMGV.js → pr-GVSZYX6I.js} +5 -5
- package/dist/{run-tests-72VNZXNT.js → run-tests-2Q5MJHXB.js} +2 -2
- package/dist/{run-tests-JJ3KOY5E.js → run-tests-R4IQS6IF.js} +2 -2
- package/dist/{server-JMXC2N3F.js → server-KAURBKFI.js} +167 -62
- package/dist/{server-QJHCJDA7.js → server-WXLE53HG.js} +6 -6
- package/dist/{task-orchestrator-DJN36HP5.js → task-orchestrator-RJ6FXYY5.js} +8 -8
- package/dist/{usage-HXJVDETY.js → usage-V3R7DA7M.js} +2 -2
- package/dist/web/client/app.js +4 -1
- package/dist/web/client/app.js.br +0 -0
- package/dist/web/client/app.js.gz +0 -0
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-SKET65WZ.js";
|
|
14
14
|
import {
|
|
15
15
|
indexProject
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-PPIISCFN.js";
|
|
17
17
|
import {
|
|
18
18
|
addMemoryEntry,
|
|
19
19
|
backupMemoryStore,
|
|
@@ -40,14 +40,14 @@ import {
|
|
|
40
40
|
touchMemoryReferences,
|
|
41
41
|
updateMemoryApproval,
|
|
42
42
|
updateMemoryEntry
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-LXXQTWT2.js";
|
|
44
44
|
import {
|
|
45
45
|
redactJson,
|
|
46
46
|
scanString
|
|
47
47
|
} from "./chunk-FSC6KEWU.js";
|
|
48
48
|
import {
|
|
49
49
|
runTestsTool
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-LOXEYWC3.js";
|
|
51
51
|
import {
|
|
52
52
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
53
53
|
APP_NAME,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
81
81
|
VERSION,
|
|
82
82
|
buildUserIdentityPrompt
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-SJCOE4C7.js";
|
|
84
84
|
import {
|
|
85
85
|
hasSemanticIndex,
|
|
86
86
|
semanticSearch
|
|
@@ -1155,7 +1155,8 @@ var BaseProvider = class {
|
|
|
1155
1155
|
return this.reliableContentOnlyTee;
|
|
1156
1156
|
}
|
|
1157
1157
|
/**
|
|
1158
|
-
* 该 provider 是否支持 `reasoning_effort` 档位(low/high/max
|
|
1158
|
+
* 该 provider 是否支持 `reasoning_effort` 档位(low/high/max)。目前是 DeepSeek V4
|
|
1159
|
+
* 与智谱 GLM-5 系列(v0.4.276 接入 GLM-5.3 时补上——它关不掉思考,档位是唯一的成本闸门)。
|
|
1159
1160
|
*
|
|
1160
1161
|
* 2026-08-14 审计 P2-03:提示符标签此前无条件渲染成 `[THINK:high]`,于是 Claude
|
|
1161
1162
|
* (用 thinkingBudget 计 token 预算)和 GLM(根本没有 effort 概念)也显示一个
|
|
@@ -2136,6 +2137,16 @@ Node.js does not automatically use system proxies. Try one of the following:
|
|
|
2136
2137
|
// src/providers/openai-compatible.ts
|
|
2137
2138
|
import OpenAI from "openai";
|
|
2138
2139
|
|
|
2140
|
+
// src/providers/openai-extensions.ts
|
|
2141
|
+
function toSdkParams(params) {
|
|
2142
|
+
return params;
|
|
2143
|
+
}
|
|
2144
|
+
function readReasoningContent(message) {
|
|
2145
|
+
if (!message || typeof message !== "object") return void 0;
|
|
2146
|
+
const value = message.reasoning_content;
|
|
2147
|
+
return typeof value === "string" ? value : void 0;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2139
2150
|
// src/tools/hallucination.ts
|
|
2140
2151
|
var HALLUCINATION_PATTERNS = [
|
|
2141
2152
|
/文件路径[::]\s*`?[^\s`]+\.\w{1,5}/,
|
|
@@ -3377,7 +3388,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3377
3388
|
continue;
|
|
3378
3389
|
}
|
|
3379
3390
|
if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
|
|
3380
|
-
|
|
3391
|
+
msgs.push({
|
|
3381
3392
|
role: "assistant",
|
|
3382
3393
|
content: typeof m.content === "string" && m.content ? m.content : null,
|
|
3383
3394
|
tool_calls: m.toolCalls.map((tc) => ({
|
|
@@ -3386,15 +3397,22 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3386
3397
|
function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
|
|
3387
3398
|
})),
|
|
3388
3399
|
reasoning_content: m.reasoningContent ?? ""
|
|
3389
|
-
};
|
|
3390
|
-
msgs.push(assistantMsg);
|
|
3400
|
+
});
|
|
3391
3401
|
continue;
|
|
3392
3402
|
}
|
|
3393
|
-
const base = { role: m.role, content: m.content };
|
|
3394
3403
|
if (m.role === "assistant") {
|
|
3395
|
-
|
|
3404
|
+
msgs.push({
|
|
3405
|
+
role: "assistant",
|
|
3406
|
+
content: typeof m.content === "string" ? m.content : "",
|
|
3407
|
+
reasoning_content: m.reasoningContent ?? ""
|
|
3408
|
+
});
|
|
3409
|
+
continue;
|
|
3410
|
+
}
|
|
3411
|
+
if (m.role === "system") {
|
|
3412
|
+
msgs.push({ role: "system", content: typeof m.content === "string" ? m.content : "" });
|
|
3413
|
+
continue;
|
|
3396
3414
|
}
|
|
3397
|
-
msgs.push(
|
|
3415
|
+
msgs.push({ role: "user", content: m.content });
|
|
3398
3416
|
}
|
|
3399
3417
|
const systemContent = [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n");
|
|
3400
3418
|
if (systemContent) {
|
|
@@ -3423,12 +3441,13 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3423
3441
|
*/
|
|
3424
3442
|
async chat(request) {
|
|
3425
3443
|
try {
|
|
3426
|
-
const
|
|
3444
|
+
const params = {
|
|
3427
3445
|
model: request.model,
|
|
3428
3446
|
messages: this.buildMessages(request),
|
|
3429
3447
|
...this.buildChatRequestParams(request),
|
|
3430
3448
|
stream: false
|
|
3431
|
-
}
|
|
3449
|
+
};
|
|
3450
|
+
const response = await this.client.chat.completions.create(toSdkParams(params), {
|
|
3432
3451
|
timeout: request.timeout ?? this.defaultTimeout,
|
|
3433
3452
|
signal: request.signal
|
|
3434
3453
|
});
|
|
@@ -3436,7 +3455,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3436
3455
|
if (!firstChoice) {
|
|
3437
3456
|
return { content: "", model: response.model, usage: void 0 };
|
|
3438
3457
|
}
|
|
3439
|
-
const reasoningContent = firstChoice.message
|
|
3458
|
+
const reasoningContent = readReasoningContent(firstChoice.message);
|
|
3440
3459
|
return {
|
|
3441
3460
|
content: firstChoice.message.content ?? "",
|
|
3442
3461
|
model: response.model,
|
|
@@ -3449,14 +3468,15 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3449
3468
|
}
|
|
3450
3469
|
async *chatStream(request) {
|
|
3451
3470
|
try {
|
|
3452
|
-
const
|
|
3471
|
+
const params = {
|
|
3453
3472
|
model: request.model,
|
|
3454
3473
|
messages: this.buildMessages(request),
|
|
3455
3474
|
...this.buildChatRequestParams(request),
|
|
3456
3475
|
stream: true,
|
|
3457
3476
|
// 请求末尾 usage chunk,供 token 统计使用
|
|
3458
3477
|
stream_options: { include_usage: true }
|
|
3459
|
-
}
|
|
3478
|
+
};
|
|
3479
|
+
const stream = await this.client.chat.completions.create(toSdkParams(params), {
|
|
3460
3480
|
timeout: request.timeout ?? this.defaultTimeout,
|
|
3461
3481
|
signal: request.signal
|
|
3462
3482
|
});
|
|
@@ -3526,14 +3546,15 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3526
3546
|
const baseMessages = this.buildMessages(request);
|
|
3527
3547
|
const extraMessages = request._extraMessages ?? [];
|
|
3528
3548
|
const allMessages = [...baseMessages, ...extraMessages];
|
|
3529
|
-
const
|
|
3549
|
+
const params = {
|
|
3530
3550
|
model: request.model,
|
|
3531
3551
|
messages: allMessages,
|
|
3532
3552
|
tools: openaiTools,
|
|
3533
3553
|
tool_choice: "auto",
|
|
3534
3554
|
...this.buildChatRequestParams(request),
|
|
3535
3555
|
stream: false
|
|
3536
|
-
}
|
|
3556
|
+
};
|
|
3557
|
+
const response = await this.client.chat.completions.create(toSdkParams(params), {
|
|
3537
3558
|
timeout: request.timeout ?? this.defaultTimeout,
|
|
3538
3559
|
// 非流式路径同样必须收 signal,否则 Ctrl+C 期间整个请求不可中断(见 chat() 上方注释)
|
|
3539
3560
|
signal: request.signal
|
|
@@ -3546,7 +3567,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3546
3567
|
const finishReason = firstChoice.finish_reason;
|
|
3547
3568
|
const usage = toUsage(response.usage);
|
|
3548
3569
|
const hasToolCalls = !!(message.tool_calls && message.tool_calls.length > 0);
|
|
3549
|
-
const reasoningContent = message
|
|
3570
|
+
const reasoningContent = readReasoningContent(message);
|
|
3550
3571
|
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
3551
3572
|
const toolCalls = message.tool_calls.map((tc) => {
|
|
3552
3573
|
const parsed = parseToolCallArguments(
|
|
@@ -3625,7 +3646,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3625
3646
|
const extraMessages = request._extraMessages ?? [];
|
|
3626
3647
|
const allMessages = [...baseMessages, ...extraMessages];
|
|
3627
3648
|
try {
|
|
3628
|
-
const
|
|
3649
|
+
const params = {
|
|
3629
3650
|
model: request.model,
|
|
3630
3651
|
messages: allMessages,
|
|
3631
3652
|
tools: openaiTools,
|
|
@@ -3633,7 +3654,8 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3633
3654
|
...this.buildChatRequestParams(request),
|
|
3634
3655
|
stream: true,
|
|
3635
3656
|
stream_options: { include_usage: true }
|
|
3636
|
-
}
|
|
3657
|
+
};
|
|
3658
|
+
const stream = await this.client.chat.completions.create(toSdkParams(params), {
|
|
3637
3659
|
timeout: request.timeout ?? this.defaultTimeout,
|
|
3638
3660
|
signal: request.signal
|
|
3639
3661
|
});
|
|
@@ -3751,9 +3773,11 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
3751
3773
|
name: tc.name,
|
|
3752
3774
|
arguments: JSON.stringify(tc.arguments)
|
|
3753
3775
|
}
|
|
3754
|
-
}))
|
|
3776
|
+
})),
|
|
3777
|
+
// DeepSeek V4 thinking 模式严格校验:assistant 消息必须包含 reasoning_content
|
|
3778
|
+
// 字段(即使为空字符串也必须存在,否则 API 返回 400)
|
|
3779
|
+
reasoning_content: reasoningContent ?? ""
|
|
3755
3780
|
};
|
|
3756
|
-
assistantMsg.reasoning_content = reasoningContent ?? "";
|
|
3757
3781
|
const resultMsgs = results.map((r) => ({
|
|
3758
3782
|
role: "tool",
|
|
3759
3783
|
tool_call_id: r.callId,
|
|
@@ -3966,7 +3990,7 @@ var ZhipuProvider = class extends OpenAICompatibleProvider {
|
|
|
3966
3990
|
buildChatRequestParams(request) {
|
|
3967
3991
|
const params = super.buildChatRequestParams(request);
|
|
3968
3992
|
if (request.thinkingEffort && REASONING_EFFORT_MODELS.test(request.model)) {
|
|
3969
|
-
params
|
|
3993
|
+
params.reasoning_effort = request.thinkingEffort;
|
|
3970
3994
|
}
|
|
3971
3995
|
return params;
|
|
3972
3996
|
}
|
|
@@ -8525,7 +8549,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
|
|
|
8525
8549
|
const mode = appendMode ? "appended" : "written";
|
|
8526
8550
|
void (async () => {
|
|
8527
8551
|
try {
|
|
8528
|
-
const { updateFile } = await import("./indexer-
|
|
8552
|
+
const { updateFile } = await import("./indexer-RBEKMFT4.js");
|
|
8529
8553
|
await updateFile(process.cwd(), filePath);
|
|
8530
8554
|
} catch {
|
|
8531
8555
|
}
|
|
@@ -10733,6 +10757,13 @@ function formatResults2(query, data, _requested) {
|
|
|
10733
10757
|
return header + "\n" + results.join("\n\n");
|
|
10734
10758
|
}
|
|
10735
10759
|
|
|
10760
|
+
// src/core/tool-capable-provider.ts
|
|
10761
|
+
function isToolCapableProvider(provider) {
|
|
10762
|
+
if (!provider) return false;
|
|
10763
|
+
const candidate = provider;
|
|
10764
|
+
return typeof candidate.chatWithTools === "function" && typeof candidate.buildToolResultMessages === "function";
|
|
10765
|
+
}
|
|
10766
|
+
|
|
10736
10767
|
// src/agents/agent-config.ts
|
|
10737
10768
|
import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync15 } from "fs";
|
|
10738
10769
|
import { join as join13 } from "path";
|
|
@@ -11544,7 +11575,8 @@ async function runSubAgent(task, maxRounds, agentIndex, ctx, agent) {
|
|
|
11544
11575
|
if (!ctx.provider) {
|
|
11545
11576
|
throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
|
|
11546
11577
|
}
|
|
11547
|
-
const
|
|
11578
|
+
const configured = agent.provider && ctx.providers?.has(agent.provider) ? ctx.providers.get(agent.provider) : void 0;
|
|
11579
|
+
const providerFromConfig = isToolCapableProvider(configured) ? configured : void 0;
|
|
11548
11580
|
const provider = providerFromConfig ?? ctx.provider;
|
|
11549
11581
|
const model = agent.model ?? (providerFromConfig?.info?.defaultModel ?? ctx.model);
|
|
11550
11582
|
const run = startAgentRun({
|
|
@@ -15511,48 +15543,259 @@ function applyThinkCommand(sub, state2) {
|
|
|
15511
15543
|
return stay(`Unknown /think option "${sub}" \u2014 expected on | off | low | high | max | status`);
|
|
15512
15544
|
}
|
|
15513
15545
|
|
|
15546
|
+
// src/core/provider-command.ts
|
|
15547
|
+
function preservedSuffix(messageCount) {
|
|
15548
|
+
return messageCount > 0 ? ` (conversation preserved: ${messageCount} messages)` : "";
|
|
15549
|
+
}
|
|
15550
|
+
function resolveProviderSwitch(input) {
|
|
15551
|
+
const id = input.targetId;
|
|
15552
|
+
if (!input.configured) {
|
|
15553
|
+
return {
|
|
15554
|
+
status: "not-configured",
|
|
15555
|
+
message: `Provider '${id}' is not configured. Run: aicli config`
|
|
15556
|
+
};
|
|
15557
|
+
}
|
|
15558
|
+
if (input.availableModels.length === 0) {
|
|
15559
|
+
return {
|
|
15560
|
+
status: "no-models",
|
|
15561
|
+
message: `Provider '${id}' has no available models. ` + (id === "ollama" ? "Make sure Ollama is running (`ollama serve`) and has models (`ollama pull <model>`)." : "Check provider configuration.")
|
|
15562
|
+
};
|
|
15563
|
+
}
|
|
15564
|
+
if (id === input.currentProvider) {
|
|
15565
|
+
return { status: "unchanged", message: `Already using provider: ${id}` };
|
|
15566
|
+
}
|
|
15567
|
+
const model = input.configuredDefaultModel || input.providerDefaultModel;
|
|
15568
|
+
return {
|
|
15569
|
+
status: "switched",
|
|
15570
|
+
providerId: id,
|
|
15571
|
+
model,
|
|
15572
|
+
message: `Switched to provider: ${id} (${model})` + preservedSuffix(input.messageCount)
|
|
15573
|
+
};
|
|
15574
|
+
}
|
|
15575
|
+
function parseModelCommand(args, currentProvider) {
|
|
15576
|
+
const raw = args[0];
|
|
15577
|
+
if (!raw) return { kind: "select" };
|
|
15578
|
+
const sub = raw.toLowerCase();
|
|
15579
|
+
if (sub === "refresh") return { kind: "refresh", providerId: args[1] ?? currentProvider };
|
|
15580
|
+
if (sub === "cache") {
|
|
15581
|
+
if ((args[1] ?? "").toLowerCase() === "clear") return { kind: "cache-clear", providerId: args[2] };
|
|
15582
|
+
return { kind: "cache-show" };
|
|
15583
|
+
}
|
|
15584
|
+
return { kind: "set", modelId: raw };
|
|
15585
|
+
}
|
|
15586
|
+
function resolveModelSwitch(input) {
|
|
15587
|
+
if (input.targetModel === input.currentModel) {
|
|
15588
|
+
return { status: "unchanged", message: `Already using model: ${input.targetModel}` };
|
|
15589
|
+
}
|
|
15590
|
+
const known = input.knownModels.includes(input.targetModel);
|
|
15591
|
+
return {
|
|
15592
|
+
status: "switched",
|
|
15593
|
+
model: input.targetModel,
|
|
15594
|
+
message: `Switched to model: ${input.targetModel}` + preservedSuffix(input.messageCount),
|
|
15595
|
+
warning: known ? void 0 : `'${input.targetModel}' is not in the known model list for ${input.provider}. If the provider rejects it, check the spelling or run /model refresh ${input.provider}.`
|
|
15596
|
+
};
|
|
15597
|
+
}
|
|
15598
|
+
var PLAN_MODE_TOOL_HINT = [
|
|
15599
|
+
"read_file \xB7 list_dir \xB7 grep_files \xB7 glob_files",
|
|
15600
|
+
"web_fetch \xB7 google_search \xB7 ask_user \xB7 write_todos"
|
|
15601
|
+
];
|
|
15602
|
+
function applyPlanCommand(sub, active) {
|
|
15603
|
+
const key = (sub ?? "enter").toLowerCase() === "toggle" ? active ? "exit" : "enter" : (sub ?? "enter").toLowerCase();
|
|
15604
|
+
if (key === "enter") {
|
|
15605
|
+
if (active) {
|
|
15606
|
+
return {
|
|
15607
|
+
planMode: true,
|
|
15608
|
+
changed: false,
|
|
15609
|
+
status: "already-active",
|
|
15610
|
+
summary: "Already in Plan Mode. Use /plan execute to start executing, or /plan exit to leave.",
|
|
15611
|
+
details: []
|
|
15612
|
+
};
|
|
15613
|
+
}
|
|
15614
|
+
return {
|
|
15615
|
+
planMode: true,
|
|
15616
|
+
changed: true,
|
|
15617
|
+
status: "entered",
|
|
15618
|
+
summary: "\u{1F4CB} Plan Mode activated",
|
|
15619
|
+
details: [
|
|
15620
|
+
"AI can now ONLY use read-only tools:",
|
|
15621
|
+
...PLAN_MODE_TOOL_HINT,
|
|
15622
|
+
"Describe your task and the AI will analyze the codebase and create a detailed implementation plan.",
|
|
15623
|
+
"When ready: /plan execute | To cancel: /plan exit"
|
|
15624
|
+
]
|
|
15625
|
+
};
|
|
15626
|
+
}
|
|
15627
|
+
if (key === "execute") {
|
|
15628
|
+
if (!active) {
|
|
15629
|
+
return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode. Enter first with /plan", details: [] };
|
|
15630
|
+
}
|
|
15631
|
+
return {
|
|
15632
|
+
planMode: false,
|
|
15633
|
+
changed: true,
|
|
15634
|
+
status: "executing",
|
|
15635
|
+
summary: "Plan Mode ended \u2014 switching to execute mode. AI now has full tool access.",
|
|
15636
|
+
details: []
|
|
15637
|
+
};
|
|
15638
|
+
}
|
|
15639
|
+
if (key === "exit" || key === "cancel") {
|
|
15640
|
+
if (!active) {
|
|
15641
|
+
return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode.", details: [] };
|
|
15642
|
+
}
|
|
15643
|
+
return { planMode: false, changed: true, status: "cancelled", summary: "Plan Mode cancelled. Returning to normal mode.", details: [] };
|
|
15644
|
+
}
|
|
15645
|
+
if (key === "status" || key === "show") {
|
|
15646
|
+
return {
|
|
15647
|
+
planMode: active,
|
|
15648
|
+
changed: false,
|
|
15649
|
+
status: "status",
|
|
15650
|
+
summary: `Plan Mode: ${active ? "ACTIVE \u{1F4CB}" : "inactive"}`,
|
|
15651
|
+
details: active ? ["Tools restricted to read-only set.", "Use /plan execute to start executing, or /plan exit to cancel."] : []
|
|
15652
|
+
};
|
|
15653
|
+
}
|
|
15654
|
+
return {
|
|
15655
|
+
planMode: active,
|
|
15656
|
+
changed: false,
|
|
15657
|
+
status: "unknown",
|
|
15658
|
+
summary: `Unknown /plan option "${sub}" \u2014 expected execute | exit | status`,
|
|
15659
|
+
details: []
|
|
15660
|
+
};
|
|
15661
|
+
}
|
|
15662
|
+
function parseRouteCommand(args) {
|
|
15663
|
+
const sub = (args[0] ?? "show").toLowerCase();
|
|
15664
|
+
if (sub === "on" || sub === "enable") return { kind: "enable" };
|
|
15665
|
+
if (sub === "off" || sub === "disable") return { kind: "disable" };
|
|
15666
|
+
if (sub === "test") {
|
|
15667
|
+
const message = args.slice(1).join(" ").trim();
|
|
15668
|
+
return message ? { kind: "test", message } : { kind: "usage", message: "Usage: /route test <message>" };
|
|
15669
|
+
}
|
|
15670
|
+
if (sub === "show" || sub === "status") return { kind: "show" };
|
|
15671
|
+
return { kind: "unknown", message: `Unknown subcommand: ${sub}. Usage: /route [on|off|show|test <message>]` };
|
|
15672
|
+
}
|
|
15673
|
+
function routingEmptyRulesNote(routing) {
|
|
15674
|
+
if (routing && routing.rules.length > 0) return null;
|
|
15675
|
+
return 'No rules configured yet. Add rules under `routing.rules` in ~/.aicli/config.json.\nExample: { match: { tag: "fast" }, model: "claude-haiku-4-5" }';
|
|
15676
|
+
}
|
|
15677
|
+
function describeRoutingRule(rule, index) {
|
|
15678
|
+
const parts = [];
|
|
15679
|
+
if (rule.match.tag) parts.push(`tag=#${rule.match.tag}`);
|
|
15680
|
+
if (rule.match.contains && rule.match.contains.length > 0) {
|
|
15681
|
+
const shown = rule.match.contains.slice(0, 3).join(", ");
|
|
15682
|
+
parts.push(`contains=[${shown}${rule.match.contains.length > 3 ? ", \u2026" : ""}]`);
|
|
15683
|
+
}
|
|
15684
|
+
if (typeof rule.match.maxLength === "number") parts.push(`maxLen=${rule.match.maxLength}`);
|
|
15685
|
+
if (typeof rule.match.minLength === "number") parts.push(`minLen=${rule.match.minLength}`);
|
|
15686
|
+
const empty = parts.length === 0;
|
|
15687
|
+
return {
|
|
15688
|
+
index,
|
|
15689
|
+
name: rule.name,
|
|
15690
|
+
condition: empty ? "(empty \u2014 never matches)" : parts.join(" & "),
|
|
15691
|
+
model: rule.model,
|
|
15692
|
+
empty
|
|
15693
|
+
};
|
|
15694
|
+
}
|
|
15695
|
+
function describeRouting(input) {
|
|
15696
|
+
const rules = input.routing?.rules ?? [];
|
|
15697
|
+
return {
|
|
15698
|
+
enabled: input.routing?.enabled === true,
|
|
15699
|
+
provider: input.provider,
|
|
15700
|
+
currentModel: input.currentModel,
|
|
15701
|
+
fallback: input.routing?.fallback,
|
|
15702
|
+
rules: rules.map((r, i) => describeRoutingRule(r, i)),
|
|
15703
|
+
noRulesNote: rules.length === 0 ? "(no rules configured \u2014 edit ~/.aicli/config.json `routing.rules`)" : null,
|
|
15704
|
+
hint: "Commands: /route on | off | test <msg> | show"
|
|
15705
|
+
};
|
|
15706
|
+
}
|
|
15707
|
+
function routeDecisionRows(message, currentModel, decision) {
|
|
15708
|
+
const rows = [
|
|
15709
|
+
{ label: "Input", value: message },
|
|
15710
|
+
{ label: "Current", value: currentModel },
|
|
15711
|
+
{ label: "Decision", value: `${decision.model} ${decision.overridden ? "\u2192 ROUTED" : "(unchanged)"}` },
|
|
15712
|
+
{ label: "Reason", value: decision.reason }
|
|
15713
|
+
];
|
|
15714
|
+
if (typeof decision.ruleIdx === "number") rows.push({ label: "Rule", value: `#${decision.ruleIdx}` });
|
|
15715
|
+
return rows;
|
|
15716
|
+
}
|
|
15717
|
+
|
|
15514
15718
|
// src/web/commands/provider-commands.ts
|
|
15719
|
+
function syncSessionProvider(ctx) {
|
|
15720
|
+
const session = ctx.sessions.current;
|
|
15721
|
+
if (!session) return;
|
|
15722
|
+
session.updateProvider(ctx.currentProvider, ctx.currentModel);
|
|
15723
|
+
ctx.saveIfNeeded();
|
|
15724
|
+
}
|
|
15515
15725
|
async function handleProvider(args, ctx) {
|
|
15516
15726
|
const id = args[0];
|
|
15517
15727
|
if (!id) {
|
|
15518
15728
|
ctx.send({ type: "error", message: "Usage: /provider <id>" });
|
|
15519
15729
|
return;
|
|
15520
15730
|
}
|
|
15521
|
-
const
|
|
15522
|
-
|
|
15523
|
-
|
|
15731
|
+
const configured = ctx.providers.has(id);
|
|
15732
|
+
let availableModels = [];
|
|
15733
|
+
let providerDefaultModel = "";
|
|
15734
|
+
if (configured) {
|
|
15735
|
+
const target = ctx.providers.get(id);
|
|
15736
|
+
providerDefaultModel = target.info.defaultModel;
|
|
15737
|
+
availableModels = target.info.models.map((m) => m.id);
|
|
15738
|
+
if (availableModels.length === 0) {
|
|
15739
|
+
try {
|
|
15740
|
+
availableModels = (await target.listModels()).map((m) => m.id);
|
|
15741
|
+
} catch {
|
|
15742
|
+
availableModels = [];
|
|
15743
|
+
}
|
|
15744
|
+
}
|
|
15745
|
+
}
|
|
15746
|
+
const outcome = resolveProviderSwitch({
|
|
15747
|
+
targetId: id,
|
|
15748
|
+
currentProvider: ctx.currentProvider,
|
|
15749
|
+
configured,
|
|
15750
|
+
availableModels,
|
|
15751
|
+
configuredDefaultModel: ctx.config.get("defaultModels")[id],
|
|
15752
|
+
providerDefaultModel,
|
|
15753
|
+
messageCount: ctx.sessions.current?.messages.length ?? 0
|
|
15754
|
+
});
|
|
15755
|
+
if (outcome.status === "not-configured" || outcome.status === "no-models") {
|
|
15756
|
+
ctx.send({ type: "error", message: outcome.message });
|
|
15757
|
+
return;
|
|
15758
|
+
}
|
|
15759
|
+
if (outcome.status === "unchanged") {
|
|
15760
|
+
ctx.send({ type: "info", message: outcome.message });
|
|
15524
15761
|
return;
|
|
15525
15762
|
}
|
|
15526
|
-
ctx.currentProvider =
|
|
15527
|
-
ctx.currentModel =
|
|
15763
|
+
ctx.currentProvider = outcome.providerId;
|
|
15764
|
+
ctx.currentModel = outcome.model;
|
|
15528
15765
|
ctx.updateContextWindow();
|
|
15529
|
-
|
|
15766
|
+
syncSessionProvider(ctx);
|
|
15767
|
+
ctx.send({ type: "info", message: outcome.message });
|
|
15530
15768
|
ctx.sendStatus();
|
|
15531
15769
|
}
|
|
15532
15770
|
async function handleModel(args, ctx) {
|
|
15533
|
-
const
|
|
15534
|
-
if (
|
|
15771
|
+
const intent = parseModelCommand(args, ctx.currentProvider);
|
|
15772
|
+
if (intent.kind === "select") {
|
|
15535
15773
|
ctx.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
|
|
15536
15774
|
return;
|
|
15537
15775
|
}
|
|
15538
|
-
if (
|
|
15539
|
-
|
|
15776
|
+
if (intent.kind === "refresh") {
|
|
15777
|
+
if (!ctx.providers.has(intent.providerId)) {
|
|
15778
|
+
ctx.send({ type: "error", message: `Provider is not configured: ${intent.providerId}` });
|
|
15779
|
+
return;
|
|
15780
|
+
}
|
|
15540
15781
|
try {
|
|
15541
|
-
const models2 = await ctx.providers.refreshModels(providerId);
|
|
15542
|
-
ctx.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
|
|
15782
|
+
const models2 = await ctx.providers.refreshModels(intent.providerId);
|
|
15783
|
+
ctx.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${intent.providerId}.` });
|
|
15543
15784
|
ctx.sendStatus();
|
|
15544
15785
|
} catch (err) {
|
|
15545
15786
|
ctx.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
15546
15787
|
}
|
|
15547
15788
|
return;
|
|
15548
15789
|
}
|
|
15549
|
-
if (
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
|
|
15790
|
+
if (intent.kind === "cache-clear") {
|
|
15791
|
+
ctx.providers.clearModelCache(intent.providerId);
|
|
15792
|
+
ctx.send({
|
|
15793
|
+
type: "info",
|
|
15794
|
+
message: intent.providerId ? `Cleared model cache for ${intent.providerId}.` : "Cleared model cache."
|
|
15795
|
+
});
|
|
15796
|
+
return;
|
|
15797
|
+
}
|
|
15798
|
+
if (intent.kind === "cache-show") {
|
|
15556
15799
|
const rows = ctx.providers.getModelCacheStatus();
|
|
15557
15800
|
ctx.send({
|
|
15558
15801
|
type: "info",
|
|
@@ -15560,16 +15803,25 @@ async function handleModel(args, ctx) {
|
|
|
15560
15803
|
});
|
|
15561
15804
|
return;
|
|
15562
15805
|
}
|
|
15563
|
-
const modelId = sub;
|
|
15564
15806
|
const models = await ctx.providers.listModels(ctx.currentProvider);
|
|
15565
|
-
const
|
|
15566
|
-
|
|
15567
|
-
ctx.
|
|
15807
|
+
const outcome = resolveModelSwitch({
|
|
15808
|
+
targetModel: intent.modelId,
|
|
15809
|
+
currentModel: ctx.currentModel,
|
|
15810
|
+
provider: ctx.currentProvider,
|
|
15811
|
+
knownModels: models.map((m) => m.id),
|
|
15812
|
+
messageCount: ctx.sessions.current?.messages.length ?? 0
|
|
15813
|
+
});
|
|
15814
|
+
if (outcome.status === "unchanged") {
|
|
15815
|
+
ctx.send({ type: "info", message: outcome.message });
|
|
15568
15816
|
return;
|
|
15569
15817
|
}
|
|
15570
|
-
ctx.currentModel =
|
|
15818
|
+
ctx.currentModel = outcome.model;
|
|
15571
15819
|
ctx.updateContextWindow();
|
|
15572
|
-
ctx
|
|
15820
|
+
syncSessionProvider(ctx);
|
|
15821
|
+
ctx.send({
|
|
15822
|
+
type: "info",
|
|
15823
|
+
message: outcome.warning ? `${outcome.message} \u2014 ${outcome.warning}` : outcome.message
|
|
15824
|
+
});
|
|
15573
15825
|
ctx.sendStatus();
|
|
15574
15826
|
}
|
|
15575
15827
|
async function handleThink(args, ctx) {
|
|
@@ -15579,7 +15831,7 @@ async function handleThink(args, ctx) {
|
|
|
15579
15831
|
runtimeEffort: ctx.runtimeThinkingEffort,
|
|
15580
15832
|
configuredThinking: modelParams?.thinking,
|
|
15581
15833
|
configuredEffort: modelParams?.thinkingEffort,
|
|
15582
|
-
supportsEffort: ctx.providers.get(ctx.currentProvider)
|
|
15834
|
+
supportsEffort: ctx.providers.has(ctx.currentProvider) ? ctx.providers.get(ctx.currentProvider).supportsReasoningEffort : false,
|
|
15583
15835
|
provider: ctx.currentProvider
|
|
15584
15836
|
});
|
|
15585
15837
|
if (outcome.changed) {
|
|
@@ -15593,24 +15845,59 @@ async function handleThink(args, ctx) {
|
|
|
15593
15845
|
if (outcome.changed) ctx.sendStatus();
|
|
15594
15846
|
}
|
|
15595
15847
|
async function handlePlan(args, ctx) {
|
|
15596
|
-
const
|
|
15597
|
-
if (
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15848
|
+
const outcome = applyPlanCommand(args[0], ctx.planMode);
|
|
15849
|
+
if (outcome.changed) ctx.planMode = outcome.planMode;
|
|
15850
|
+
ctx.send({
|
|
15851
|
+
type: outcome.status === "unknown" ? "error" : "info",
|
|
15852
|
+
message: [outcome.summary, ...outcome.details].join(" \u2014 ")
|
|
15853
|
+
});
|
|
15854
|
+
if (outcome.changed) ctx.sendStatus();
|
|
15855
|
+
}
|
|
15856
|
+
async function handleRoute(args, ctx) {
|
|
15857
|
+
const intent = parseRouteCommand(args);
|
|
15858
|
+
const routing = ctx.config.get("routing");
|
|
15859
|
+
if (intent.kind === "enable" || intent.kind === "disable") {
|
|
15860
|
+
const on = intent.kind === "enable";
|
|
15861
|
+
ctx.config.setByPath("routing.enabled", on ? "true" : "false");
|
|
15862
|
+
const note = on ? routingEmptyRulesNote(routing) : null;
|
|
15863
|
+
ctx.send({
|
|
15864
|
+
type: "info",
|
|
15865
|
+
message: `Smart model routing ${on ? "enabled" : "disabled"}.` + (note ? `
|
|
15866
|
+
${note}` : "")
|
|
15867
|
+
});
|
|
15868
|
+
ctx.sendStatus();
|
|
15869
|
+
return;
|
|
15870
|
+
}
|
|
15871
|
+
if (intent.kind === "usage" || intent.kind === "unknown") {
|
|
15872
|
+
ctx.send({ type: "error", message: intent.message });
|
|
15605
15873
|
return;
|
|
15606
|
-
}
|
|
15607
|
-
|
|
15874
|
+
}
|
|
15875
|
+
if (intent.kind === "test") {
|
|
15876
|
+
const decision = ctx.computeRoutingDecision(intent.message);
|
|
15877
|
+
ctx.send({
|
|
15878
|
+
type: "info",
|
|
15879
|
+
message: routeDecisionRows(intent.message, ctx.currentModel, decision).map((row) => `${row.label}: ${row.value}`).join("\n")
|
|
15880
|
+
});
|
|
15608
15881
|
return;
|
|
15882
|
+
}
|
|
15883
|
+
const view = describeRouting({ routing, provider: ctx.currentProvider, currentModel: ctx.currentModel });
|
|
15884
|
+
const lines = [
|
|
15885
|
+
"Smart Model Routing",
|
|
15886
|
+
`Status: ${view.enabled ? "enabled" : "disabled"}`,
|
|
15887
|
+
`Provider: ${view.provider}`,
|
|
15888
|
+
`Current: ${view.currentModel}`
|
|
15889
|
+
];
|
|
15890
|
+
if (view.fallback) lines.push(`Fallback: ${view.fallback}`);
|
|
15891
|
+
if (view.noRulesNote) {
|
|
15892
|
+
lines.push(view.noRulesNote);
|
|
15609
15893
|
} else {
|
|
15610
|
-
|
|
15611
|
-
|
|
15894
|
+
lines.push("Rules (evaluated top-to-bottom):");
|
|
15895
|
+
for (const rule of view.rules) {
|
|
15896
|
+
lines.push(` #${rule.index} ${rule.name ? `${rule.name} ` : ""}${rule.condition} \u2192 ${rule.model}`);
|
|
15897
|
+
}
|
|
15612
15898
|
}
|
|
15613
|
-
|
|
15899
|
+
lines.push(view.hint);
|
|
15900
|
+
ctx.send({ type: "info", message: lines.join("\n") });
|
|
15614
15901
|
}
|
|
15615
15902
|
|
|
15616
15903
|
// src/core/session-cost.ts
|
|
@@ -16143,7 +16430,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
|
16143
16430
|
Cost: ${formatCost(cost)}` : "";
|
|
16144
16431
|
let memoryLine = "";
|
|
16145
16432
|
try {
|
|
16146
|
-
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-
|
|
16433
|
+
const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-JCENBMCJ.js");
|
|
16147
16434
|
const pending = countPendingMemories2(ctx.config.getConfigDir());
|
|
16148
16435
|
if (pending > 0) memoryLine = `
|
|
16149
16436
|
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
@@ -16956,7 +17243,7 @@ async function handleTest(args, ctx) {
|
|
|
16956
17243
|
const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
|
|
16957
17244
|
testArgs = isCommand ? { command: argStr } : { filter: argStr };
|
|
16958
17245
|
}
|
|
16959
|
-
const runTests = ctx.runTests ?? (await import("./run-tests-
|
|
17246
|
+
const runTests = ctx.runTests ?? (await import("./run-tests-2Q5MJHXB.js")).executeTests;
|
|
16960
17247
|
const report = await runTests(testArgs);
|
|
16961
17248
|
ctx.send({ type: "info", message: report });
|
|
16962
17249
|
} catch (err) {
|
|
@@ -17134,7 +17421,7 @@ async function handleIndex(args, ctx) {
|
|
|
17134
17421
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
17135
17422
|
const root = process.cwd();
|
|
17136
17423
|
const { loadIndex: loadIndex2, clearIndex } = await import("./store-VO37H6LS.js");
|
|
17137
|
-
const { indexProject: indexProject2 } = await import("./indexer-
|
|
17424
|
+
const { indexProject: indexProject2 } = await import("./indexer-RBEKMFT4.js");
|
|
17138
17425
|
const { loadVectorStore, clearVectorStore } = await import("./vector-store-JBAE6PS4.js");
|
|
17139
17426
|
if (sub === "status") {
|
|
17140
17427
|
const idx = loadIndex2(root);
|
|
@@ -17292,7 +17579,7 @@ async function handleMemory(args, ctx) {
|
|
|
17292
17579
|
ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
|
|
17293
17580
|
} else if (sub === "export") {
|
|
17294
17581
|
if (args[1] === "md") {
|
|
17295
|
-
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-
|
|
17582
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-JCENBMCJ.js");
|
|
17296
17583
|
ctx.send({
|
|
17297
17584
|
type: "export_data",
|
|
17298
17585
|
format: "md",
|
|
@@ -17734,7 +18021,8 @@ async function handleHelp(_args, ctx) {
|
|
|
17734
18021
|
" /clear \u2014 Clear conversation & start new session",
|
|
17735
18022
|
" /compact [hint] \u2014 Compress conversation history",
|
|
17736
18023
|
" /think [on|off] \u2014 Toggle extended thinking mode",
|
|
17737
|
-
" /plan [
|
|
18024
|
+
" /plan [execute|exit|status] \u2014 Read-only planning mode (bare /plan enters it)",
|
|
18025
|
+
" /route [on|off|show|test <msg>] \u2014 Smart model routing",
|
|
17738
18026
|
" /session new|list|load|delete <id> \u2014 Session management",
|
|
17739
18027
|
" /system [prompt|clear] \u2014 Set/view/reset system prompt",
|
|
17740
18028
|
" /context [status|reload] \u2014 Show/reload context layers",
|
|
@@ -17871,8 +18159,9 @@ var CLI_ONLY_WEB_COMMANDS = /* @__PURE__ */ new Map([
|
|
|
17871
18159
|
["snapshot", "Dev-state snapshots are written when the terminal REPL exits."],
|
|
17872
18160
|
["hooks", "Hook trust is granted per machine from the terminal."],
|
|
17873
18161
|
["hook", "Hook trust is granted per machine from the terminal."],
|
|
17874
|
-
//
|
|
17875
|
-
|
|
18162
|
+
// 以下一条并非「终端才有意义」,而是 Web 侧尚未实现——如实说明,别假装是设计。
|
|
18163
|
+
// (`route` 曾在这里,理由写的是「Web 尚未应用智能路由」;v0.4.269 让 routing 在 Web
|
|
18164
|
+
// 上真正生效之后那句话就不再成立,命令已在 Web 实现,见 commands/provider-commands.ts。)
|
|
17876
18165
|
["security", "Redaction status and toggles are terminal-only for now; /status shows the current profile."]
|
|
17877
18166
|
]);
|
|
17878
18167
|
var webCommandHandlers = {
|
|
@@ -17880,6 +18169,7 @@ var webCommandHandlers = {
|
|
|
17880
18169
|
model: handleModel,
|
|
17881
18170
|
think: handleThink,
|
|
17882
18171
|
plan: handlePlan,
|
|
18172
|
+
route: handleRoute,
|
|
17883
18173
|
clear: handleClear,
|
|
17884
18174
|
compact: handleCompact,
|
|
17885
18175
|
status: handleStatus,
|
|
@@ -18052,9 +18342,19 @@ var SessionHandler = class {
|
|
|
18052
18342
|
}
|
|
18053
18343
|
runLifecycleHooks(hooks, "SessionStart", { source: "web" }, { configDir: this.config.getConfigDir() });
|
|
18054
18344
|
this.sendStatus();
|
|
18055
|
-
askUserContext.rl =
|
|
18345
|
+
askUserContext.rl = void 0;
|
|
18056
18346
|
askUserContext.prompting = false;
|
|
18057
18347
|
}
|
|
18348
|
+
/**
|
|
18349
|
+
* P3-03:本方法与 `WebCommandContext` 列出的其余 36 个成员一并由 private 放开为
|
|
18350
|
+
* public——它们本就经由 ctx 门面暴露给每一个 web 命令 handler,`private` 只是名义上的。
|
|
18351
|
+
*
|
|
18352
|
+
* `web-command-context.ts` 的文件注释明明白白写着「`ctx: WebCommandContext = this`
|
|
18353
|
+
* 这个赋值保证 handler 只能碰到这些成员」,而 `handleCommand` 实际写的是
|
|
18354
|
+
* `this as unknown as WebCommandContext`——那道保证被这一次 cast 整个绕过了,
|
|
18355
|
+
* 唯一拦着真赋值的就是这些名义上的 private。去掉 cast 之后,SessionHandler 与门面
|
|
18356
|
+
* 接口之间的任何漂移(少一个成员、签名改了)都会当场编译失败,而不是等到运行时。
|
|
18357
|
+
*/
|
|
18058
18358
|
send(msg) {
|
|
18059
18359
|
if (this.ws.readyState === this.ws.OPEN) {
|
|
18060
18360
|
this.ws.send(JSON.stringify(msg));
|
|
@@ -18423,10 +18723,10 @@ var SessionHandler = class {
|
|
|
18423
18723
|
this.processing = false;
|
|
18424
18724
|
return;
|
|
18425
18725
|
}
|
|
18426
|
-
const
|
|
18427
|
-
const { toolDefs, mcpBudgetNote } =
|
|
18428
|
-
if (
|
|
18429
|
-
await this.handleChatWithTools(
|
|
18726
|
+
const toolCapable = isToolCapableProvider(provider) ? provider : null;
|
|
18727
|
+
const { toolDefs, mcpBudgetNote } = toolCapable ? this.getFilteredToolDefs() : { toolDefs: [], mcpBudgetNote: null };
|
|
18728
|
+
if (toolCapable && toolDefs.length > 0) {
|
|
18729
|
+
await this.handleChatWithTools(toolCapable, session.messages, toolDefs, mcpBudgetNote, routingDecision.model);
|
|
18430
18730
|
} else {
|
|
18431
18731
|
await this.handleChatSimple(provider, session.messages, routingDecision.model);
|
|
18432
18732
|
}
|