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.
Files changed (39) hide show
  1. package/dist/{batch-F7SKIBI3.js → batch-7Y4NAA6N.js} +2 -2
  2. package/dist/{chunk-VF7DMSKF.js → chunk-2BYQP6JX.js} +1 -1
  3. package/dist/{chunk-GLSR2NR3.js → chunk-4DEMGCK4.js} +4 -3
  4. package/dist/{chunk-MXPUOF5K.js → chunk-4Z6GAYB5.js} +4 -4
  5. package/dist/{chunk-NXAHXWPF.js → chunk-H7QC5VMQ.js} +1 -1
  6. package/dist/{chunk-MCKUGWOY.js → chunk-JJCNNQTU.js} +183 -3
  7. package/dist/{chunk-AOISQVXS.js → chunk-L3CPM472.js} +1 -1
  8. package/dist/{chunk-ATD2Y3PB.js → chunk-LOXEYWC3.js} +1 -1
  9. package/dist/{chunk-WYDP2WEQ.js → chunk-LTZFUYBT.js} +4 -4
  10. package/dist/{chunk-4NH4Q7QK.js → chunk-LXXQTWT2.js} +1 -1
  11. package/dist/{chunk-QCG6UU2S.js → chunk-PPIISCFN.js} +4 -3
  12. package/dist/{chunk-KWSGSSWN.js → chunk-RFSKPCYC.js} +1 -1
  13. package/dist/{chunk-OMBKLWLJ.js → chunk-SB4XONS4.js} +1 -1
  14. package/dist/{chunk-UNL7NR55.js → chunk-SJCOE4C7.js} +1 -1
  15. package/dist/{chunk-4KTK2OYU.js → chunk-UGY5SVNX.js} +46 -20
  16. package/dist/{chunk-NXNXWM6H.js → chunk-WOXIKQRI.js} +16 -7
  17. package/dist/{chunk-FLDYAXJR.js → chunk-X3722RFA.js} +1 -1
  18. package/dist/{ci-VURFRMWQ.js → ci-WDF2CYR4.js} +5 -5
  19. package/dist/{ci-format-AFJJIURF.js → ci-format-7B5VAZGA.js} +2 -2
  20. package/dist/{constants-WO7ERENL.js → constants-DC3TNLTL.js} +1 -1
  21. package/dist/{doctor-cli-BGGILTHE.js → doctor-cli-CDDG5XOY.js} +5 -5
  22. package/dist/electron-server.js +378 -78
  23. package/dist/{hub-Z73RLNJI.js → hub-TO7ODOIA.js} +1 -1
  24. package/dist/index.js +175 -188
  25. package/dist/{indexer-M2LJGTY2.js → indexer-RBEKMFT4.js} +1 -1
  26. package/dist/{indexer-VZETNE4H.js → indexer-TNDWGFRI.js} +1 -1
  27. package/dist/{persistent-memory-5QMARQJF.js → persistent-memory-BU63CSGK.js} +2 -2
  28. package/dist/{persistent-memory-CI544RLT.js → persistent-memory-JCENBMCJ.js} +2 -2
  29. package/dist/{pr-24REDMGV.js → pr-GVSZYX6I.js} +5 -5
  30. package/dist/{run-tests-72VNZXNT.js → run-tests-2Q5MJHXB.js} +2 -2
  31. package/dist/{run-tests-JJ3KOY5E.js → run-tests-R4IQS6IF.js} +2 -2
  32. package/dist/{server-JMXC2N3F.js → server-KAURBKFI.js} +167 -62
  33. package/dist/{server-QJHCJDA7.js → server-WXLE53HG.js} +6 -6
  34. package/dist/{task-orchestrator-DJN36HP5.js → task-orchestrator-RJ6FXYY5.js} +8 -8
  35. package/dist/{usage-HXJVDETY.js → usage-V3R7DA7M.js} +2 -2
  36. package/dist/web/client/app.js +4 -1
  37. package/dist/web/client/app.js.br +0 -0
  38. package/dist/web/client/app.js.gz +0 -0
  39. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-VF7DMSKF.js";
4
+ } from "./chunk-2BYQP6JX.js";
5
5
  import "./chunk-TU3L3PW7.js";
6
6
  import {
7
7
  atomicWriteFileSync
8
8
  } from "./chunk-IW3Q7AE5.js";
9
- import "./chunk-FLDYAXJR.js";
9
+ import "./chunk-X3722RFA.js";
10
10
 
11
11
  // src/cli/batch.ts
12
12
  import Anthropic from "@anthropic-ai/sdk";
@@ -11,7 +11,7 @@ import {
11
11
  CONFIG_FILE_NAME,
12
12
  HISTORY_DIR_NAME,
13
13
  PLUGINS_DIR_NAME
14
- } from "./chunk-FLDYAXJR.js";
14
+ } from "./chunk-X3722RFA.js";
15
15
 
16
16
  // src/config/config-manager.ts
17
17
  import { readFileSync, existsSync, mkdirSync } from "fs";
@@ -20,8 +20,9 @@ import { Parser, Language } from "web-tree-sitter";
20
20
  import { createRequire } from "module";
21
21
  import path from "path";
22
22
  import fs from "fs";
23
+ var packagedProcess = process;
23
24
  var entryCandidates = [
24
- process.pkg?.entrypoint,
25
+ packagedProcess.pkg?.entrypoint,
25
26
  process.argv[1],
26
27
  process.execPath
27
28
  ].filter((p) => !!p);
@@ -48,12 +49,12 @@ function resolveWasmPath(filename) {
48
49
  candidates.push(path.join(dir, "..", "wasm", filename));
49
50
  candidates.push(path.join(dir, "..", "dist", "wasm", filename));
50
51
  }
51
- if (process.pkg) {
52
+ if (packagedProcess.pkg) {
52
53
  candidates.push(path.join(path.dirname(process.execPath), "dist", "wasm", filename));
53
54
  candidates.push(path.join("/snapshot", "ai-cli", "dist", "wasm", filename));
54
55
  candidates.push(path.join("/snapshot", "jinzd-ai-cli", "dist", "wasm", filename));
55
56
  }
56
- const resourcesPath = process.resourcesPath;
57
+ const resourcesPath = packagedProcess.resourcesPath;
57
58
  if (resourcesPath) {
58
59
  candidates.push(path.join(resourcesPath, "app.asar.unpacked", "dist", "wasm", filename));
59
60
  candidates.push(path.join(resourcesPath, "app", "dist", "wasm", filename));
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  evaluateCiGate,
4
4
  normalizeCiThresholds
5
- } from "./chunk-NXAHXWPF.js";
5
+ } from "./chunk-H7QC5VMQ.js";
6
6
  import {
7
7
  buildReviewPrompt,
8
8
  buildSecurityReviewPrompt,
@@ -13,13 +13,13 @@ import {
13
13
  } from "./chunk-6YZEZFX5.js";
14
14
  import {
15
15
  ProviderRegistry
16
- } from "./chunk-4KTK2OYU.js";
16
+ } from "./chunk-UGY5SVNX.js";
17
17
  import {
18
18
  ConfigManager
19
- } from "./chunk-VF7DMSKF.js";
19
+ } from "./chunk-2BYQP6JX.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-FLDYAXJR.js";
22
+ } from "./chunk-X3722RFA.js";
23
23
 
24
24
  // src/cli/ci.ts
25
25
  import { execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-FLDYAXJR.js";
4
+ } from "./chunk-X3722RFA.js";
5
5
 
6
6
  // src/cli/ci-format.ts
7
7
  var DEFAULT_CI_THRESHOLDS = {
@@ -5,11 +5,11 @@ import {
5
5
  import {
6
6
  estimateTokens,
7
7
  truncateForPersist
8
- } from "./chunk-NXNXWM6H.js";
8
+ } from "./chunk-WOXIKQRI.js";
9
9
  import {
10
10
  isMemoryExpired,
11
11
  isMemoryStale
12
- } from "./chunk-KWSGSSWN.js";
12
+ } from "./chunk-RFSKPCYC.js";
13
13
  import {
14
14
  isCleanDocumentBody,
15
15
  stripOuterCodeFence
@@ -27,7 +27,7 @@ import {
27
27
  MCP_PROTOCOL_VERSION,
28
28
  MCP_TOOL_PREFIX,
29
29
  VERSION
30
- } from "./chunk-FLDYAXJR.js";
30
+ } from "./chunk-X3722RFA.js";
31
31
 
32
32
  // src/mcp/client.ts
33
33
  import { spawn } from "child_process";
@@ -1382,6 +1382,178 @@ function applyThinkCommand(sub, state) {
1382
1382
  return stay(`Unknown /think option "${sub}" \u2014 expected on | off | low | high | max | status`);
1383
1383
  }
1384
1384
 
1385
+ // src/core/provider-command.ts
1386
+ function preservedSuffix(messageCount) {
1387
+ return messageCount > 0 ? ` (conversation preserved: ${messageCount} messages)` : "";
1388
+ }
1389
+ function resolveProviderSwitch(input) {
1390
+ const id = input.targetId;
1391
+ if (!input.configured) {
1392
+ return {
1393
+ status: "not-configured",
1394
+ message: `Provider '${id}' is not configured. Run: aicli config`
1395
+ };
1396
+ }
1397
+ if (input.availableModels.length === 0) {
1398
+ return {
1399
+ status: "no-models",
1400
+ 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.")
1401
+ };
1402
+ }
1403
+ if (id === input.currentProvider) {
1404
+ return { status: "unchanged", message: `Already using provider: ${id}` };
1405
+ }
1406
+ const model = input.configuredDefaultModel || input.providerDefaultModel;
1407
+ return {
1408
+ status: "switched",
1409
+ providerId: id,
1410
+ model,
1411
+ message: `Switched to provider: ${id} (${model})` + preservedSuffix(input.messageCount)
1412
+ };
1413
+ }
1414
+ function parseModelCommand(args, currentProvider) {
1415
+ const raw = args[0];
1416
+ if (!raw) return { kind: "select" };
1417
+ const sub = raw.toLowerCase();
1418
+ if (sub === "refresh") return { kind: "refresh", providerId: args[1] ?? currentProvider };
1419
+ if (sub === "cache") {
1420
+ if ((args[1] ?? "").toLowerCase() === "clear") return { kind: "cache-clear", providerId: args[2] };
1421
+ return { kind: "cache-show" };
1422
+ }
1423
+ return { kind: "set", modelId: raw };
1424
+ }
1425
+ function resolveModelSwitch(input) {
1426
+ if (input.targetModel === input.currentModel) {
1427
+ return { status: "unchanged", message: `Already using model: ${input.targetModel}` };
1428
+ }
1429
+ const known = input.knownModels.includes(input.targetModel);
1430
+ return {
1431
+ status: "switched",
1432
+ model: input.targetModel,
1433
+ message: `Switched to model: ${input.targetModel}` + preservedSuffix(input.messageCount),
1434
+ 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}.`
1435
+ };
1436
+ }
1437
+ var PLAN_MODE_TOOL_HINT = [
1438
+ "read_file \xB7 list_dir \xB7 grep_files \xB7 glob_files",
1439
+ "web_fetch \xB7 google_search \xB7 ask_user \xB7 write_todos"
1440
+ ];
1441
+ function applyPlanCommand(sub, active) {
1442
+ const key = (sub ?? "enter").toLowerCase() === "toggle" ? active ? "exit" : "enter" : (sub ?? "enter").toLowerCase();
1443
+ if (key === "enter") {
1444
+ if (active) {
1445
+ return {
1446
+ planMode: true,
1447
+ changed: false,
1448
+ status: "already-active",
1449
+ summary: "Already in Plan Mode. Use /plan execute to start executing, or /plan exit to leave.",
1450
+ details: []
1451
+ };
1452
+ }
1453
+ return {
1454
+ planMode: true,
1455
+ changed: true,
1456
+ status: "entered",
1457
+ summary: "\u{1F4CB} Plan Mode activated",
1458
+ details: [
1459
+ "AI can now ONLY use read-only tools:",
1460
+ ...PLAN_MODE_TOOL_HINT,
1461
+ "Describe your task and the AI will analyze the codebase and create a detailed implementation plan.",
1462
+ "When ready: /plan execute | To cancel: /plan exit"
1463
+ ]
1464
+ };
1465
+ }
1466
+ if (key === "execute") {
1467
+ if (!active) {
1468
+ return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode. Enter first with /plan", details: [] };
1469
+ }
1470
+ return {
1471
+ planMode: false,
1472
+ changed: true,
1473
+ status: "executing",
1474
+ summary: "Plan Mode ended \u2014 switching to execute mode. AI now has full tool access.",
1475
+ details: []
1476
+ };
1477
+ }
1478
+ if (key === "exit" || key === "cancel") {
1479
+ if (!active) {
1480
+ return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode.", details: [] };
1481
+ }
1482
+ return { planMode: false, changed: true, status: "cancelled", summary: "Plan Mode cancelled. Returning to normal mode.", details: [] };
1483
+ }
1484
+ if (key === "status" || key === "show") {
1485
+ return {
1486
+ planMode: active,
1487
+ changed: false,
1488
+ status: "status",
1489
+ summary: `Plan Mode: ${active ? "ACTIVE \u{1F4CB}" : "inactive"}`,
1490
+ details: active ? ["Tools restricted to read-only set.", "Use /plan execute to start executing, or /plan exit to cancel."] : []
1491
+ };
1492
+ }
1493
+ return {
1494
+ planMode: active,
1495
+ changed: false,
1496
+ status: "unknown",
1497
+ summary: `Unknown /plan option "${sub}" \u2014 expected execute | exit | status`,
1498
+ details: []
1499
+ };
1500
+ }
1501
+ function parseRouteCommand(args) {
1502
+ const sub = (args[0] ?? "show").toLowerCase();
1503
+ if (sub === "on" || sub === "enable") return { kind: "enable" };
1504
+ if (sub === "off" || sub === "disable") return { kind: "disable" };
1505
+ if (sub === "test") {
1506
+ const message = args.slice(1).join(" ").trim();
1507
+ return message ? { kind: "test", message } : { kind: "usage", message: "Usage: /route test <message>" };
1508
+ }
1509
+ if (sub === "show" || sub === "status") return { kind: "show" };
1510
+ return { kind: "unknown", message: `Unknown subcommand: ${sub}. Usage: /route [on|off|show|test <message>]` };
1511
+ }
1512
+ function routingEmptyRulesNote(routing) {
1513
+ if (routing && routing.rules.length > 0) return null;
1514
+ return 'No rules configured yet. Add rules under `routing.rules` in ~/.aicli/config.json.\nExample: { match: { tag: "fast" }, model: "claude-haiku-4-5" }';
1515
+ }
1516
+ function describeRoutingRule(rule, index) {
1517
+ const parts = [];
1518
+ if (rule.match.tag) parts.push(`tag=#${rule.match.tag}`);
1519
+ if (rule.match.contains && rule.match.contains.length > 0) {
1520
+ const shown = rule.match.contains.slice(0, 3).join(", ");
1521
+ parts.push(`contains=[${shown}${rule.match.contains.length > 3 ? ", \u2026" : ""}]`);
1522
+ }
1523
+ if (typeof rule.match.maxLength === "number") parts.push(`maxLen=${rule.match.maxLength}`);
1524
+ if (typeof rule.match.minLength === "number") parts.push(`minLen=${rule.match.minLength}`);
1525
+ const empty = parts.length === 0;
1526
+ return {
1527
+ index,
1528
+ name: rule.name,
1529
+ condition: empty ? "(empty \u2014 never matches)" : parts.join(" & "),
1530
+ model: rule.model,
1531
+ empty
1532
+ };
1533
+ }
1534
+ function describeRouting(input) {
1535
+ const rules = input.routing?.rules ?? [];
1536
+ return {
1537
+ enabled: input.routing?.enabled === true,
1538
+ provider: input.provider,
1539
+ currentModel: input.currentModel,
1540
+ fallback: input.routing?.fallback,
1541
+ rules: rules.map((r, i) => describeRoutingRule(r, i)),
1542
+ noRulesNote: rules.length === 0 ? "(no rules configured \u2014 edit ~/.aicli/config.json `routing.rules`)" : null,
1543
+ hint: "Commands: /route on | off | test <msg> | show"
1544
+ };
1545
+ }
1546
+ function routeDecisionRows(message, currentModel, decision) {
1547
+ const rows = [
1548
+ { label: "Input", value: message },
1549
+ { label: "Current", value: currentModel },
1550
+ { label: "Decision", value: `${decision.model} ${decision.overridden ? "\u2192 ROUTED" : "(unchanged)"}` },
1551
+ { label: "Reason", value: decision.reason }
1552
+ ];
1553
+ if (typeof decision.ruleIdx === "number") rows.push({ label: "Rule", value: `#${decision.ruleIdx}` });
1554
+ return rows;
1555
+ }
1556
+
1385
1557
  // src/core/profile-command.ts
1386
1558
  var PROFILE_FIELDS = ["name", "nickname", "role", "bio", "interests", "locale", "extra"];
1387
1559
  function stripSurroundingQuotes(value) {
@@ -1543,6 +1715,14 @@ function costSourceNote(source) {
1543
1715
 
1544
1716
  export {
1545
1717
  applyThinkCommand,
1718
+ resolveProviderSwitch,
1719
+ parseModelCommand,
1720
+ resolveModelSwitch,
1721
+ applyPlanCommand,
1722
+ parseRouteCommand,
1723
+ routingEmptyRulesNote,
1724
+ describeRouting,
1725
+ routeDecisionRows,
1546
1726
  applyProfileCommand,
1547
1727
  readGitDiff,
1548
1728
  scanDirTree,
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  CONFIG_DIR_NAME,
7
7
  PLUGINS_DIR_NAME
8
- } from "./chunk-FLDYAXJR.js";
8
+ } from "./chunk-X3722RFA.js";
9
9
 
10
10
  // src/diagnostics/tool-stats.ts
11
11
  import { existsSync, readFileSync, mkdirSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TEST_TIMEOUT
3
- } from "./chunk-UNL7NR55.js";
3
+ } from "./chunk-SJCOE4C7.js";
4
4
 
5
5
  // src/tools/builtin/run-tests.ts
6
6
  import { execSync, spawnSync } from "child_process";
@@ -8,13 +8,13 @@ import {
8
8
  listHooks,
9
9
  listInstalledPlugins,
10
10
  resetStats
11
- } from "./chunk-AOISQVXS.js";
11
+ } from "./chunk-L3CPM472.js";
12
12
  import {
13
13
  ProviderRegistry
14
- } from "./chunk-4KTK2OYU.js";
14
+ } from "./chunk-UGY5SVNX.js";
15
15
  import {
16
16
  ConfigManager
17
- } from "./chunk-VF7DMSKF.js";
17
+ } from "./chunk-2BYQP6JX.js";
18
18
  import {
19
19
  getGitRoot
20
20
  } from "./chunk-HOSJZMQS.js";
@@ -26,7 +26,7 @@ import {
26
26
  MCP_PROJECT_CONFIG_NAME,
27
27
  MEMORY_FILE_NAME,
28
28
  VERSION
29
- } from "./chunk-FLDYAXJR.js";
29
+ } from "./chunk-X3722RFA.js";
30
30
 
31
31
  // src/diagnostics/doctor-report.ts
32
32
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -6,7 +6,7 @@ import {
6
6
  MEMORY_FILE_NAME,
7
7
  MEMORY_MAX_CHARS,
8
8
  MEMORY_STORE_FILE_NAME
9
- } from "./chunk-UNL7NR55.js";
9
+ } from "./chunk-SJCOE4C7.js";
10
10
  import {
11
11
  atomicWriteFileSync
12
12
  } from "./chunk-6BUTA5VW.js";
@@ -19,8 +19,9 @@ import { Parser, Language } from "web-tree-sitter";
19
19
  import { createRequire } from "module";
20
20
  import path from "path";
21
21
  import fs from "fs";
22
+ var packagedProcess = process;
22
23
  var entryCandidates = [
23
- process.pkg?.entrypoint,
24
+ packagedProcess.pkg?.entrypoint,
24
25
  process.argv[1],
25
26
  process.execPath
26
27
  ].filter((p) => !!p);
@@ -47,12 +48,12 @@ function resolveWasmPath(filename) {
47
48
  candidates.push(path.join(dir, "..", "wasm", filename));
48
49
  candidates.push(path.join(dir, "..", "dist", "wasm", filename));
49
50
  }
50
- if (process.pkg) {
51
+ if (packagedProcess.pkg) {
51
52
  candidates.push(path.join(path.dirname(process.execPath), "dist", "wasm", filename));
52
53
  candidates.push(path.join("/snapshot", "ai-cli", "dist", "wasm", filename));
53
54
  candidates.push(path.join("/snapshot", "jinzd-ai-cli", "dist", "wasm", filename));
54
55
  }
55
- const resourcesPath = process.resourcesPath;
56
+ const resourcesPath = packagedProcess.resourcesPath;
56
57
  if (resourcesPath) {
57
58
  candidates.push(path.join(resourcesPath, "app.asar.unpacked", "dist", "wasm", filename));
58
59
  candidates.push(path.join(resourcesPath, "app", "dist", "wasm", filename));
@@ -13,7 +13,7 @@ import {
13
13
  MEMORY_FILE_NAME,
14
14
  MEMORY_MAX_CHARS,
15
15
  MEMORY_STORE_FILE_NAME
16
- } from "./chunk-FLDYAXJR.js";
16
+ } from "./chunk-X3722RFA.js";
17
17
 
18
18
  // src/memory/persistent-memory.ts
19
19
  import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-FLDYAXJR.js";
4
+ } from "./chunk-X3722RFA.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -1,5 +1,5 @@
1
1
  // src/core/constants.ts
2
- var VERSION = "0.4.276";
2
+ var VERSION = "0.4.278";
3
3
  var APP_NAME = "ai-cli";
4
4
  var CONFIG_DIR_NAME = ".aicli";
5
5
  var CONFIG_FILE_NAME = "config.json";
@@ -31,7 +31,8 @@ var BaseProvider = class {
31
31
  return this.reliableContentOnlyTee;
32
32
  }
33
33
  /**
34
- * 该 provider 是否支持 `reasoning_effort` 档位(low/high/max)。目前只有 DeepSeek V4
34
+ * 该 provider 是否支持 `reasoning_effort` 档位(low/high/max)。目前是 DeepSeek V4
35
+ * 与智谱 GLM-5 系列(v0.4.276 接入 GLM-5.3 时补上——它关不掉思考,档位是唯一的成本闸门)。
35
36
  *
36
37
  * 2026-08-14 审计 P2-03:提示符标签此前无条件渲染成 `[THINK:high]`,于是 Claude
37
38
  * (用 thinkingBudget 计 token 预算)和 GLM(根本没有 effort 概念)也显示一个
@@ -947,6 +948,18 @@ Node.js does not automatically use system proxies. Try one of the following:
947
948
 
948
949
  // src/providers/openai-compatible.ts
949
950
  import OpenAI from "openai";
951
+
952
+ // src/providers/openai-extensions.ts
953
+ function toSdkParams(params) {
954
+ return params;
955
+ }
956
+ function readReasoningContent(message) {
957
+ if (!message || typeof message !== "object") return void 0;
958
+ const value = message.reasoning_content;
959
+ return typeof value === "string" ? value : void 0;
960
+ }
961
+
962
+ // src/providers/openai-compatible.ts
950
963
  function toUsage(u) {
951
964
  if (!u) return void 0;
952
965
  const cached = u.prompt_tokens_details?.cached_tokens ?? 0;
@@ -1064,7 +1077,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1064
1077
  continue;
1065
1078
  }
1066
1079
  if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0) {
1067
- const assistantMsg = {
1080
+ msgs.push({
1068
1081
  role: "assistant",
1069
1082
  content: typeof m.content === "string" && m.content ? m.content : null,
1070
1083
  tool_calls: m.toolCalls.map((tc) => ({
@@ -1073,15 +1086,22 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1073
1086
  function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
1074
1087
  })),
1075
1088
  reasoning_content: m.reasoningContent ?? ""
1076
- };
1077
- msgs.push(assistantMsg);
1089
+ });
1078
1090
  continue;
1079
1091
  }
1080
- const base = { role: m.role, content: m.content };
1081
1092
  if (m.role === "assistant") {
1082
- base.reasoning_content = m.reasoningContent ?? "";
1093
+ msgs.push({
1094
+ role: "assistant",
1095
+ content: typeof m.content === "string" ? m.content : "",
1096
+ reasoning_content: m.reasoningContent ?? ""
1097
+ });
1098
+ continue;
1099
+ }
1100
+ if (m.role === "system") {
1101
+ msgs.push({ role: "system", content: typeof m.content === "string" ? m.content : "" });
1102
+ continue;
1083
1103
  }
1084
- msgs.push(base);
1104
+ msgs.push({ role: "user", content: m.content });
1085
1105
  }
1086
1106
  const systemContent = [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n");
1087
1107
  if (systemContent) {
@@ -1110,12 +1130,13 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1110
1130
  */
1111
1131
  async chat(request) {
1112
1132
  try {
1113
- const response = await this.client.chat.completions.create({
1133
+ const params = {
1114
1134
  model: request.model,
1115
1135
  messages: this.buildMessages(request),
1116
1136
  ...this.buildChatRequestParams(request),
1117
1137
  stream: false
1118
- }, {
1138
+ };
1139
+ const response = await this.client.chat.completions.create(toSdkParams(params), {
1119
1140
  timeout: request.timeout ?? this.defaultTimeout,
1120
1141
  signal: request.signal
1121
1142
  });
@@ -1123,7 +1144,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1123
1144
  if (!firstChoice) {
1124
1145
  return { content: "", model: response.model, usage: void 0 };
1125
1146
  }
1126
- const reasoningContent = firstChoice.message.reasoning_content;
1147
+ const reasoningContent = readReasoningContent(firstChoice.message);
1127
1148
  return {
1128
1149
  content: firstChoice.message.content ?? "",
1129
1150
  model: response.model,
@@ -1136,14 +1157,15 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1136
1157
  }
1137
1158
  async *chatStream(request) {
1138
1159
  try {
1139
- const stream = await this.client.chat.completions.create({
1160
+ const params = {
1140
1161
  model: request.model,
1141
1162
  messages: this.buildMessages(request),
1142
1163
  ...this.buildChatRequestParams(request),
1143
1164
  stream: true,
1144
1165
  // 请求末尾 usage chunk,供 token 统计使用
1145
1166
  stream_options: { include_usage: true }
1146
- }, {
1167
+ };
1168
+ const stream = await this.client.chat.completions.create(toSdkParams(params), {
1147
1169
  timeout: request.timeout ?? this.defaultTimeout,
1148
1170
  signal: request.signal
1149
1171
  });
@@ -1213,14 +1235,15 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1213
1235
  const baseMessages = this.buildMessages(request);
1214
1236
  const extraMessages = request._extraMessages ?? [];
1215
1237
  const allMessages = [...baseMessages, ...extraMessages];
1216
- const response = await this.client.chat.completions.create({
1238
+ const params = {
1217
1239
  model: request.model,
1218
1240
  messages: allMessages,
1219
1241
  tools: openaiTools,
1220
1242
  tool_choice: "auto",
1221
1243
  ...this.buildChatRequestParams(request),
1222
1244
  stream: false
1223
- }, {
1245
+ };
1246
+ const response = await this.client.chat.completions.create(toSdkParams(params), {
1224
1247
  timeout: request.timeout ?? this.defaultTimeout,
1225
1248
  // 非流式路径同样必须收 signal,否则 Ctrl+C 期间整个请求不可中断(见 chat() 上方注释)
1226
1249
  signal: request.signal
@@ -1233,7 +1256,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1233
1256
  const finishReason = firstChoice.finish_reason;
1234
1257
  const usage = toUsage(response.usage);
1235
1258
  const hasToolCalls = !!(message.tool_calls && message.tool_calls.length > 0);
1236
- const reasoningContent = message.reasoning_content;
1259
+ const reasoningContent = readReasoningContent(message);
1237
1260
  if (message.tool_calls && message.tool_calls.length > 0) {
1238
1261
  const toolCalls = message.tool_calls.map((tc) => {
1239
1262
  const parsed = parseToolCallArguments(
@@ -1312,7 +1335,7 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1312
1335
  const extraMessages = request._extraMessages ?? [];
1313
1336
  const allMessages = [...baseMessages, ...extraMessages];
1314
1337
  try {
1315
- const stream = await this.client.chat.completions.create({
1338
+ const params = {
1316
1339
  model: request.model,
1317
1340
  messages: allMessages,
1318
1341
  tools: openaiTools,
@@ -1320,7 +1343,8 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1320
1343
  ...this.buildChatRequestParams(request),
1321
1344
  stream: true,
1322
1345
  stream_options: { include_usage: true }
1323
- }, {
1346
+ };
1347
+ const stream = await this.client.chat.completions.create(toSdkParams(params), {
1324
1348
  timeout: request.timeout ?? this.defaultTimeout,
1325
1349
  signal: request.signal
1326
1350
  });
@@ -1438,9 +1462,11 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1438
1462
  name: tc.name,
1439
1463
  arguments: JSON.stringify(tc.arguments)
1440
1464
  }
1441
- }))
1465
+ })),
1466
+ // DeepSeek V4 thinking 模式严格校验:assistant 消息必须包含 reasoning_content
1467
+ // 字段(即使为空字符串也必须存在,否则 API 返回 400)
1468
+ reasoning_content: reasoningContent ?? ""
1442
1469
  };
1443
- assistantMsg.reasoning_content = reasoningContent ?? "";
1444
1470
  const resultMsgs = results.map((r) => ({
1445
1471
  role: "tool",
1446
1472
  tool_call_id: r.callId,
@@ -1653,7 +1679,7 @@ var ZhipuProvider = class extends OpenAICompatibleProvider {
1653
1679
  buildChatRequestParams(request) {
1654
1680
  const params = super.buildChatRequestParams(request);
1655
1681
  if (request.thinkingEffort && REASONING_EFFORT_MODELS.test(request.model)) {
1656
- params["reasoning_effort"] = request.thinkingEffort;
1682
+ params.reasoning_effort = request.thinkingEffort;
1657
1683
  }
1658
1684
  return params;
1659
1685
  }
@@ -11,10 +11,10 @@ import {
11
11
  searchPersistentMemories,
12
12
  touchMemoryReferences,
13
13
  updateMemoryEntry
14
- } from "./chunk-KWSGSSWN.js";
14
+ } from "./chunk-RFSKPCYC.js";
15
15
  import {
16
16
  indexProject
17
- } from "./chunk-GLSR2NR3.js";
17
+ } from "./chunk-4DEMGCK4.js";
18
18
  import {
19
19
  hasSemanticIndex,
20
20
  semanticSearch
@@ -30,7 +30,7 @@ import {
30
30
  runHook,
31
31
  runLifecycleHooks,
32
32
  runTool
33
- } from "./chunk-AOISQVXS.js";
33
+ } from "./chunk-L3CPM472.js";
34
34
  import {
35
35
  buildTruncatedArgumentsError,
36
36
  getDangerLevel,
@@ -50,14 +50,14 @@ import {
50
50
  } from "./chunk-4BKXL7SM.js";
51
51
  import {
52
52
  runTestsTool
53
- } from "./chunk-OMBKLWLJ.js";
53
+ } from "./chunk-SB4XONS4.js";
54
54
  import {
55
55
  CONFIG_DIR_NAME,
56
56
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
57
57
  SUBAGENT_ALLOWED_TOOLS,
58
58
  SUBAGENT_DEFAULT_MAX_ROUNDS,
59
59
  SUBAGENT_MAX_ROUNDS_LIMIT
60
- } from "./chunk-FLDYAXJR.js";
60
+ } from "./chunk-X3722RFA.js";
61
61
 
62
62
  // src/tools/shell-pool.ts
63
63
  import { spawn as spawn2 } from "child_process";
@@ -339,6 +339,13 @@ function cleanupAllSessions() {
339
339
  }
340
340
  }
341
341
 
342
+ // src/core/tool-capable-provider.ts
343
+ function isToolCapableProvider(provider) {
344
+ if (!provider) return false;
345
+ const candidate = provider;
346
+ return typeof candidate.chatWithTools === "function" && typeof candidate.buildToolResultMessages === "function";
347
+ }
348
+
342
349
  // src/tools/builtin/bash.ts
343
350
  import { spawn as spawn3 } from "child_process";
344
351
  import { existsSync as existsSync2, readdirSync, statSync } from "fs";
@@ -2945,7 +2952,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
2945
2952
  const mode = appendMode ? "appended" : "written";
2946
2953
  void (async () => {
2947
2954
  try {
2948
- const { updateFile } = await import("./indexer-VZETNE4H.js");
2955
+ const { updateFile } = await import("./indexer-TNDWGFRI.js");
2949
2956
  await updateFile(process.cwd(), filePath);
2950
2957
  } catch {
2951
2958
  }
@@ -5576,7 +5583,8 @@ async function runSubAgent(task, maxRounds, agentIndex, ctx, agent) {
5576
5583
  if (!ctx.provider) {
5577
5584
  throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
5578
5585
  }
5579
- const providerFromConfig = agent.provider && ctx.providers?.has(agent.provider) ? ctx.providers.get(agent.provider) : void 0;
5586
+ const configured = agent.provider && ctx.providers?.has(agent.provider) ? ctx.providers.get(agent.provider) : void 0;
5587
+ const providerFromConfig = isToolCapableProvider(configured) ? configured : void 0;
5580
5588
  const provider = providerFromConfig ?? ctx.provider;
5581
5589
  const model = agent.model ?? (providerFromConfig?.info?.defaultModel ?? ctx.model);
5582
5590
  const run = startAgentRun({
@@ -6995,6 +7003,7 @@ export {
6995
7003
  cleanupRejectedTeeFile,
6996
7004
  askUserContext,
6997
7005
  googleSearchContext,
7006
+ isToolCapableProvider,
6998
7007
  listAgentConfigs,
6999
7008
  resolveAgentConfig,
7000
7009
  listAgentRuns,