jinzd-ai-cli 0.4.264 → 0.4.266

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 (41) hide show
  1. package/README.md +59 -37
  2. package/README.zh-CN.md +63 -41
  3. package/dist/{batch-Q22M55UP.js → batch-FQU6HK3Z.js} +2 -2
  4. package/dist/{chunk-VT6NRDD2.js → chunk-23SJVOQD.js} +6 -2
  5. package/dist/{chunk-K7HQLXXQ.js → chunk-3O4VLKD4.js} +1 -1
  6. package/dist/chunk-5C76XDDF.js +78 -0
  7. package/dist/chunk-6YZEZFX5.js +40 -0
  8. package/dist/{chunk-TL5MD5XU.js → chunk-AUEX6RNY.js} +17 -1
  9. package/dist/{chunk-IAIUXBB5.js → chunk-CEJUB4YV.js} +1 -1
  10. package/dist/{chunk-4T4KHPRD.js → chunk-FA3HGXXM.js} +1 -1
  11. package/dist/{chunk-BMSBFK7J.js → chunk-FIDEG2PG.js} +4 -4
  12. package/dist/{chunk-UQP2BNQT.js → chunk-GWOZET2E.js} +1 -1
  13. package/dist/{chunk-JVUYKO4V.js → chunk-LGJ7EX2F.js} +1 -1
  14. package/dist/{chunk-TANTNIGP.js → chunk-LHY7ST6X.js} +1 -1
  15. package/dist/{chunk-QCZFHQHY.js → chunk-N2E6GFLZ.js} +1 -1
  16. package/dist/{chunk-U4XQIP7Z.js → chunk-QW76RX6G.js} +1 -1
  17. package/dist/{chunk-UGJIRJ4H.js → chunk-TJNBSR3I.js} +1 -1
  18. package/dist/{chunk-IEYR2BGG.js → chunk-UCHND2K6.js} +65 -2
  19. package/dist/{chunk-QG4NG7KJ.js → chunk-V4D7YD24.js} +9 -2
  20. package/dist/{chunk-3ARPAVNV.js → chunk-VOIOUJXI.js} +11 -3
  21. package/dist/{chunk-DYONT23T.js → chunk-XEVURCAM.js} +13 -8
  22. package/dist/{chunk-53SOOMYG.js → chunk-Y2X24VYB.js} +24 -95
  23. package/dist/{ci-RMEIHZJL.js → ci-GZH3QMME.js} +6 -5
  24. package/dist/{ci-format-UCFSWVG5.js → ci-format-VKP2MGYJ.js} +2 -2
  25. package/dist/{constants-Z2R2BKUY.js → constants-ICAF6I3J.js} +1 -1
  26. package/dist/{doctor-cli-64L7BBK7.js → doctor-cli-CO6BJWRW.js} +5 -5
  27. package/dist/electron-server.js +127 -26
  28. package/dist/{hub-P7D6VDCK.js → hub-4APIRQ73.js} +8 -4
  29. package/dist/{hub-server-QGUSMN22.js → hub-server-RF6LURXY.js} +3 -1
  30. package/dist/index.js +125 -582
  31. package/dist/{persistent-memory-CELKJM4U.js → persistent-memory-YD3SHSUH.js} +2 -2
  32. package/dist/{persistent-memory-QX6C2JAR.js → persistent-memory-YPO25MOP.js} +2 -2
  33. package/dist/{pr-DJP53EZV.js → pr-CDOPCAPK.js} +15 -10
  34. package/dist/{run-tests-CK44KRY3.js → run-tests-4XRHKHN5.js} +2 -2
  35. package/dist/{run-tests-A2YM4U2U.js → run-tests-QNNP2HCD.js} +2 -2
  36. package/dist/{server-XYVL7A33.js → server-53GM3CRH.js} +34 -29
  37. package/dist/{server-GUV2HZ7N.js → server-B4A7LHZ5.js} +6 -5
  38. package/dist/setup-wizard-S55ICNHH.js +483 -0
  39. package/dist/{task-orchestrator-5RT5EH2J.js → task-orchestrator-WNV6PT6Z.js} +10 -7
  40. package/dist/{usage-LGPJ4UAW.js → usage-E3FEYSBU.js} +3 -3
  41. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- CostTracker
4
- } from "./chunk-QG4NG7KJ.js";
5
2
  import {
6
3
  McpManager,
7
4
  SNAPSHOT_PROMPT,
8
5
  SkillManager,
6
+ applyThinkCommand,
9
7
  autoTrimSessionIfNeeded,
10
8
  buildInitPrompt,
11
9
  clearDevState,
@@ -17,7 +15,10 @@ import {
17
15
  scanProject,
18
16
  sessionHasMeaningfulContent,
19
17
  setupProxy
20
- } from "./chunk-IEYR2BGG.js";
18
+ } from "./chunk-UCHND2K6.js";
19
+ import {
20
+ CostTracker
21
+ } from "./chunk-V4D7YD24.js";
21
22
  import {
22
23
  computeCost,
23
24
  formatCost,
@@ -27,6 +28,12 @@ import {
27
28
  buildReviewPrompt,
28
29
  buildSecurityReviewPrompt
29
30
  } from "./chunk-HLWUDRBO.js";
31
+ import {
32
+ NO_THINKING,
33
+ applyThinkingFlag,
34
+ chatParamsFrom,
35
+ thinkingParamsFor
36
+ } from "./chunk-6YZEZFX5.js";
30
37
  import {
31
38
  ToolExecutor,
32
39
  ToolRegistry,
@@ -38,7 +45,6 @@ import {
38
45
  formatPermissionProfileWarning,
39
46
  getRecentlyDeniedAutoActions,
40
47
  googleSearchContext,
41
- initTheme,
42
48
  isInterrupted,
43
49
  lastResponseStore,
44
50
  renderDiff,
@@ -48,9 +54,8 @@ import {
48
54
  setContextWindow,
49
55
  setMaxOutputCap,
50
56
  spawnAgentContext,
51
- theme,
52
57
  undoStack
53
- } from "./chunk-53SOOMYG.js";
58
+ } from "./chunk-Y2X24VYB.js";
54
59
  import {
55
60
  addMemoryEntry,
56
61
  backupMemoryStore,
@@ -76,11 +81,15 @@ import {
76
81
  touchMemoryReferences,
77
82
  updateMemoryApproval,
78
83
  updateMemoryEntry
79
- } from "./chunk-K7HQLXXQ.js";
84
+ } from "./chunk-3O4VLKD4.js";
80
85
  import "./chunk-GLSR2NR3.js";
81
86
  import "./chunk-UUSRWSSX.js";
82
87
  import "./chunk-CKH4KQ4E.js";
83
88
  import "./chunk-BXP6YZ2P.js";
89
+ import {
90
+ initTheme,
91
+ theme
92
+ } from "./chunk-5C76XDDF.js";
84
93
  import {
85
94
  SessionManager,
86
95
  getContentText
@@ -90,14 +99,14 @@ import {
90
99
  formatDoctorReport,
91
100
  loadContextFiles,
92
101
  writeCrashLog
93
- } from "./chunk-BMSBFK7J.js";
102
+ } from "./chunk-FIDEG2PG.js";
94
103
  import {
95
104
  ProviderRegistry,
96
105
  listBuiltInProviderMetadata
97
- } from "./chunk-TL5MD5XU.js";
106
+ } from "./chunk-AUEX6RNY.js";
98
107
  import {
99
108
  ConfigManager
100
- } from "./chunk-IAIUXBB5.js";
109
+ } from "./chunk-CEJUB4YV.js";
101
110
  import {
102
111
  describePlugin,
103
112
  getActivePluginAssets,
@@ -114,7 +123,7 @@ import {
114
123
  trustHook,
115
124
  trustPlugin,
116
125
  untrustHook
117
- } from "./chunk-U4XQIP7Z.js";
126
+ } from "./chunk-QW76RX6G.js";
118
127
  import {
119
128
  CONTENT_ONLY_STREAM_REMINDER,
120
129
  TEE_FINAL_USER_NUDGE,
@@ -148,7 +157,7 @@ import {
148
157
  import {
149
158
  fileCheckpoints
150
159
  } from "./chunk-4BKXL7SM.js";
151
- import "./chunk-QCZFHQHY.js";
160
+ import "./chunk-N2E6GFLZ.js";
152
161
  import {
153
162
  AGENTIC_BEHAVIOR_GUIDELINE,
154
163
  AUTHOR,
@@ -167,7 +176,7 @@ import {
167
176
  SKILLS_DIR_NAME,
168
177
  VERSION,
169
178
  buildUserIdentityPrompt
170
- } from "./chunk-JVUYKO4V.js";
179
+ } from "./chunk-LGJ7EX2F.js";
171
180
  import {
172
181
  buildChatIndex,
173
182
  clearChatIndex,
@@ -237,7 +246,10 @@ Now produce the title (\u2264 12 Chinese chars or \u2264 6 English words):`,
237
246
  systemPrompt: SYSTEM_PROMPT,
238
247
  stream: false,
239
248
  maxTokens: 40,
240
- temperature: 0.3
249
+ temperature: 0.3,
250
+ // 生成标题不值得付推理成本;显式关而不是靠「未配置即关」的默认兜底——
251
+ // 默认值是产品决策,哪天再变,靠默认的调用点会跟着一起变贵(审计 P0-01)。
252
+ ...NO_THINKING
241
253
  });
242
254
  const title = sanitizeTitle(resp.content);
243
255
  if (!title) return;
@@ -1141,46 +1153,24 @@ function createProviderCommands() {
1141
1153
  description: "Toggle Extended Thinking mode (deep reasoning for supported models)",
1142
1154
  usage: "/think [on|off|low|high|max|status]",
1143
1155
  execute(args, ctx) {
1144
- const sub = args[0]?.toLowerCase() ?? "toggle";
1145
- if (sub === "on") {
1146
- ctx.setThinkingMode(true);
1147
- ctx.renderer.printSuccess("Extended Thinking enabled \u{1F4AD}");
1148
- console.log(theme.dim(" AI will now use deep reasoning before answering."));
1149
- console.log(theme.dim(" Supported: DeepSeek V4, Claude Sonnet/Opus, GLM-5"));
1150
- console.log(theme.dim(" Use /think off to disable."));
1151
- console.log();
1152
- return;
1153
- }
1154
- if (sub === "off") {
1155
- ctx.setThinkingMode(false);
1156
- ctx.renderer.printSuccess("Extended Thinking disabled.");
1157
- console.log();
1158
- return;
1159
- }
1160
- if (sub === "low" || sub === "high" || sub === "max") {
1161
- ctx.setThinkingMode(true);
1162
- ctx.setThinkingEffort(sub);
1163
- ctx.renderer.printSuccess(`Extended Thinking enabled (effort: ${sub}).`);
1164
- console.log();
1165
- return;
1166
- }
1167
- if (sub === "status") {
1168
- const mode = ctx.getThinkingMode();
1169
- const label = mode === true ? theme.accent("ON (runtime override)") : mode === false ? theme.dim("OFF (runtime override)") : theme.dim("using config value");
1170
- console.log(` Extended Thinking: ${label}`);
1171
- console.log(` Reasoning effort: ${ctx.getThinkingEffort() ?? "using config/default (high for DeepSeek V4)"}`);
1172
- console.log();
1173
- return;
1174
- }
1175
- const current = ctx.getThinkingMode();
1176
- if (current === true) {
1177
- ctx.setThinkingMode(false);
1178
- ctx.renderer.printSuccess("Extended Thinking disabled.");
1156
+ const outcome = applyThinkCommand(args[0], {
1157
+ runtimeThinking: ctx.getThinkingMode(),
1158
+ runtimeEffort: ctx.getThinkingEffort(),
1159
+ configuredThinking: ctx.getConfiguredThinking(),
1160
+ configuredEffort: ctx.getConfiguredThinkingEffort(),
1161
+ supportsEffort: ctx.getProviderSupportsReasoningEffort(),
1162
+ provider: ctx.getCurrentProvider()
1163
+ });
1164
+ if (outcome.changed) {
1165
+ ctx.setThinkingMode(outcome.runtimeThinking);
1166
+ ctx.setThinkingEffort(outcome.runtimeEffort);
1167
+ ctx.renderer.printSuccess(outcome.effective ? `${outcome.summary} \u{1F4AD}` : outcome.summary);
1168
+ } else if (outcome.summary.startsWith("Unknown")) {
1169
+ console.log(theme.warning(` ${outcome.summary}`));
1179
1170
  } else {
1180
- ctx.setThinkingMode(true);
1181
- ctx.renderer.printSuccess("Extended Thinking enabled \u{1F4AD}");
1182
- console.log(theme.dim(" Use /think off to disable."));
1171
+ console.log(` ${outcome.effective ? theme.accent(outcome.summary) : theme.dim(outcome.summary)}`);
1183
1172
  }
1173
+ for (const line of outcome.details) console.log(theme.dim(` ${line}`));
1184
1174
  console.log();
1185
1175
  }
1186
1176
  },
@@ -1340,12 +1330,12 @@ No tools match "${filter}".
1340
1330
  return;
1341
1331
  }
1342
1332
  if (sub === "install") {
1343
- const input2 = args[1];
1344
- if (!input2) {
1333
+ const input = args[1];
1334
+ if (!input) {
1345
1335
  ctx.renderer.printInfo("Usage: /plugin install <path-to-plugin-dir>");
1346
1336
  return;
1347
1337
  }
1348
- const plugin = installPlugin(configDir, input2);
1338
+ const plugin = installPlugin(configDir, input);
1349
1339
  ctx.renderer.printSuccess(`Installed plugin ${plugin.name}@${plugin.manifest.version}. Run /plugin trust ${plugin.name}, then /plugin enable ${plugin.name}.`);
1350
1340
  return;
1351
1341
  }
@@ -1407,7 +1397,7 @@ No tools match "${filter}".
1407
1397
  const { join: join6 } = await import("path");
1408
1398
  const { existsSync: existsSync6 } = await import("fs");
1409
1399
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1410
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-Z2R2BKUY.js");
1400
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-ICAF6I3J.js");
1411
1401
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1412
1402
  const cwd = process.cwd();
1413
1403
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2395,7 +2385,7 @@ function createReviewCommands() {
2395
2385
  usage: "/test [command|filter]",
2396
2386
  async execute(args, ctx) {
2397
2387
  try {
2398
- const { executeTests } = await import("./run-tests-A2YM4U2U.js");
2388
+ const { executeTests } = await import("./run-tests-QNNP2HCD.js");
2399
2389
  const argStr = args.join(" ").trim();
2400
2390
  let testArgs = {};
2401
2391
  if (argStr) {
@@ -3782,17 +3772,20 @@ ${text}
3782
3772
  }
3783
3773
  const provider = ctx.getCurrentProvider();
3784
3774
  const model = ctx.getCurrentModel();
3785
- const cost = computeCost(provider, model, usage);
3786
3775
  const pricing = getPricing(provider, model);
3776
+ const accrued = ctx.getSessionAccruedCost();
3777
+ const usedAccrual = accrued.usd > 0;
3778
+ const cost = usedAccrual ? accrued.usd : computeCost(provider, model, usage);
3787
3779
  let savings = null;
3788
- if (cost != null && pricing && cacheRead > 0) {
3780
+ const liveCost = computeCost(provider, model, usage);
3781
+ if (liveCost != null && pricing && cacheRead > 0) {
3789
3782
  const costWithoutCache = computeCost(provider, model, {
3790
3783
  inputTokens: usage.inputTokens + cacheRead,
3791
3784
  outputTokens: usage.outputTokens,
3792
3785
  cacheCreationTokens: cacheCreate,
3793
3786
  cacheReadTokens: 0
3794
3787
  });
3795
- if (costWithoutCache != null) savings = costWithoutCache - cost;
3788
+ if (costWithoutCache != null) savings = costWithoutCache - liveCost;
3796
3789
  }
3797
3790
  console.log();
3798
3791
  console.log(theme.heading(" \u{1F4B0} Session Cost & Token Usage"));
@@ -3812,6 +3805,11 @@ ${text}
3812
3805
  console.log(theme.dim(" " + "\u2500".repeat(48)));
3813
3806
  if (cost != null) {
3814
3807
  console.log(theme.dim(" Cost : ") + chalk3.bold.cyan(formatCost(cost).padStart(12)));
3808
+ if (usedAccrual && accrued.incomplete) {
3809
+ console.log(theme.dim(" (partial \u2014 some turns had unknown pricing)"));
3810
+ } else if (!usedAccrual) {
3811
+ console.log(theme.dim(" (estimated at current rates)"));
3812
+ }
3815
3813
  if (savings != null && savings > 0) {
3816
3814
  console.log(
3817
3815
  theme.dim(" Cache savings : ") + chalk3.green(`-${formatCost(savings)}`.padStart(12)) + theme.dim(` (vs no cache)`)
@@ -4484,482 +4482,6 @@ var streamToFileContext = {
4484
4482
  extraMessages: []
4485
4483
  };
4486
4484
 
4487
- // src/repl/setup-wizard.ts
4488
- import { password, select, input, confirm } from "@inquirer/prompts";
4489
- var PROVIDERS = [
4490
- { value: "claude", name: "Claude (Anthropic)" },
4491
- { value: "gemini", name: "Gemini (Google)" },
4492
- { value: "deepseek", name: "DeepSeek" },
4493
- { value: "zhipu", name: "Zhipu (GLM)" },
4494
- { value: "kimi", name: "Kimi (Moonshot AI)" },
4495
- { value: "minimax", name: "MiniMax (\u6D77\u87BA)" },
4496
- { value: "qwen", name: "Qwen (Alibaba Cloud)" },
4497
- { value: "openai", name: "OpenAI" },
4498
- { value: "openrouter", name: "OpenRouter" },
4499
- { value: "ollama", name: "Ollama (Local)" }
4500
- ];
4501
- var NO_KEY_PROVIDERS = /* @__PURE__ */ new Set(["ollama"]);
4502
- var OLLAMA_DEFAULT_HOST = "http://localhost:11434";
4503
- function maskKey(key) {
4504
- if (key.length <= 10) return "****";
4505
- return key.slice(0, 6) + "****" + key.slice(-4);
4506
- }
4507
- function errorMessage(err) {
4508
- if (err instanceof Error && err.message) return err.message;
4509
- return String(err ?? "unknown error");
4510
- }
4511
- var SetupWizard = class {
4512
- constructor(config) {
4513
- this.config = config;
4514
- }
4515
- config;
4516
- async runFirstRun() {
4517
- console.log(theme.heading("\nWelcome to ai-cli!\n"));
4518
- console.log("Let's get to know you first, then set up your AI provider.\n");
4519
- try {
4520
- await this.setupProfile();
4521
- console.log();
4522
- const providerId = await select({
4523
- message: "Which AI provider do you want to set up first?",
4524
- choices: PROVIDERS
4525
- });
4526
- await this.setupProvider(providerId);
4527
- this.config.set("defaultProvider", providerId);
4528
- this.config.save();
4529
- const nickname = this.config.get("userProfile")?.nickname;
4530
- const greeting = nickname ? `Setup complete, ${nickname}!` : "Setup complete!";
4531
- console.log(theme.success(`
4532
- ${greeting} Starting ai-cli...
4533
- `));
4534
- return true;
4535
- } catch (err) {
4536
- const message = errorMessage(err);
4537
- const canceled = /user force closed|interrupted|cancel|aborted/i.test(message);
4538
- if (canceled) {
4539
- console.log(theme.warning("\nSetup canceled. Run aicli config to try again.\n"));
4540
- } else {
4541
- console.log(theme.error(`
4542
- First-run setup failed: ${message}`));
4543
- console.log(theme.dim(`Config directory: ${this.config.getConfigDir()}`));
4544
- console.log(theme.dim("Run aicli config to retry, or aicli doctor to inspect provider/config health.\n"));
4545
- }
4546
- return false;
4547
- }
4548
- }
4549
- async runFull() {
4550
- console.log(theme.heading("\nai-cli Configuration\n"));
4551
- let running = true;
4552
- while (running) {
4553
- const currentProxy = this.config.get("proxy") ?? "";
4554
- const proxyStatus = currentProxy ? theme.success(`[${currentProxy}]`) : theme.dim("[not configured]");
4555
- const googleKeyStatus = this.config.getApiKey("google-search") ? theme.success("[configured]") : theme.dim("[not configured]");
4556
- const profileName = this.config.get("userProfile")?.nickname || this.config.get("userProfile")?.name;
4557
- const profileStatus = profileName ? theme.success(`[${profileName}]`) : theme.dim("[not configured]");
4558
- const yoloOn = this.config.get("alwaysYolo") === true;
4559
- const mcpOff = this.config.get("mcpEnabled") === false;
4560
- const behaviorFlags = [];
4561
- if (yoloOn) behaviorFlags.push("YOLO on");
4562
- if (mcpOff) behaviorFlags.push("MCP off");
4563
- const behaviorStatus = behaviorFlags.length ? theme.warning(`[${behaviorFlags.join(", ")}]`) : theme.dim("[default]");
4564
- const action = await select({
4565
- message: "What would you like to configure?",
4566
- choices: [
4567
- { value: "profile", name: `Edit your profile (identity) ${profileStatus}` },
4568
- { value: "apikey", name: "Manage API key for a provider" },
4569
- { value: "default", name: "Change default provider" },
4570
- { value: "proxy", name: `Configure proxy (HTTP/HTTPS) ${proxyStatus}` },
4571
- { value: "google", name: `Configure Google Search (API Key + CX) ${googleKeyStatus}` },
4572
- { value: "behavior", name: `Behavior toggles (YOLO, MCP, ...) ${behaviorStatus}` },
4573
- { value: "preset", name: "Apply task preset (safe review, workspace coding, local-only, team web)" },
4574
- { value: "done", name: "Done" }
4575
- ]
4576
- });
4577
- if (action === "profile") {
4578
- await this.setupProfile();
4579
- } else if (action === "proxy") {
4580
- await this.setupProxy();
4581
- } else if (action === "google") {
4582
- await this.setupGoogleSearch();
4583
- } else if (action === "behavior") {
4584
- await this.setupBehavior();
4585
- } else if (action === "preset") {
4586
- await this.setupPreset();
4587
- } else if (action === "apikey") {
4588
- const choicesWithStatus = PROVIDERS.map((p) => {
4589
- if (NO_KEY_PROVIDERS.has(p.value)) {
4590
- return { value: p.value, name: `${p.name} ${theme.dim("[no API key needed]")}` };
4591
- }
4592
- const existingKey = this.config.getApiKey(p.value);
4593
- const status = existingKey ? theme.success(`[${maskKey(existingKey)}]`) : theme.dim("[not configured]");
4594
- return { value: p.value, name: `${p.name} ${status}` };
4595
- });
4596
- const providerId = await select({
4597
- message: "Select provider:",
4598
- choices: choicesWithStatus
4599
- });
4600
- await this.setupProvider(providerId);
4601
- } else if (action === "default") {
4602
- const providerId = await select({
4603
- message: "Select default provider:",
4604
- choices: PROVIDERS
4605
- });
4606
- this.config.set("defaultProvider", providerId);
4607
- this.config.save();
4608
- console.log(theme.success(`Default provider set to: ${providerId}
4609
- `));
4610
- } else {
4611
- running = false;
4612
- }
4613
- }
4614
- }
4615
- async setupPreset() {
4616
- const preset = await select({
4617
- message: "Choose a task-oriented preset:",
4618
- choices: [
4619
- { value: "safe-review", name: "Safe review only - read-only tools, network confirm" },
4620
- { value: "workspace-coding", name: "Workspace coding - write in workspace with confirmations" },
4621
- { value: "local-ollama", name: "Local-only Ollama - local provider, external network denied" },
4622
- { value: "team-web", name: "Team Web UI - workspace coding plus auth reminder" }
4623
- ]
4624
- });
4625
- const currentNetwork = this.config.get("networkPolicy");
4626
- if (preset === "safe-review") {
4627
- this.config.set("defaultPermissionProfile", "read-only");
4628
- this.config.set("alwaysYolo", false);
4629
- this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
4630
- console.log(theme.success("Preset applied: Safe review only. Writes require denial/confirmation according to the read-only profile."));
4631
- } else if (preset === "workspace-coding") {
4632
- this.config.set("defaultPermissionProfile", "workspace-write");
4633
- this.config.set("alwaysYolo", false);
4634
- this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
4635
- console.log(theme.success("Preset applied: Workspace coding. File writes stay inside the workspace and sensitive actions still ask."));
4636
- } else if (preset === "local-ollama") {
4637
- this.config.set("defaultProvider", "ollama");
4638
- this.config.set("defaultPermissionProfile", "workspace-write");
4639
- this.config.set("alwaysYolo", false);
4640
- this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "deny", allowPrivateNetwork: true, tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "deny", web_search: "deny", google_search: "deny", mcp: "confirm" } });
4641
- console.log(theme.success("Preset applied: Local-only Ollama. External web/search is denied; private network is allowed for local services."));
4642
- } else {
4643
- this.config.set("defaultPermissionProfile", "workspace-write");
4644
- this.config.set("alwaysYolo", false);
4645
- this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
4646
- console.log(theme.warning("Preset applied: Team Web UI. Start Web with auth enabled and put it behind your normal TLS/reverse proxy boundary."));
4647
- }
4648
- this.config.save();
4649
- console.log(theme.dim("Review changes with aicli doctor or aicli config.\n"));
4650
- }
4651
- async setupProvider(providerId) {
4652
- if (NO_KEY_PROVIDERS.has(providerId)) {
4653
- await this.setupOllama();
4654
- return;
4655
- }
4656
- const provider = PROVIDERS.find((p) => p.value === providerId);
4657
- const displayName = provider?.name ?? providerId;
4658
- const existingKey = this.config.getApiKey(providerId);
4659
- console.log(`
4660
- Managing ${displayName} API Key`);
4661
- if (existingKey) {
4662
- console.log(theme.dim(` Current: ${maskKey(existingKey)}`));
4663
- } else {
4664
- console.log(theme.dim(" Current: (not configured)"));
4665
- }
4666
- const choices = existingKey ? [
4667
- { value: "keep", name: "Keep current key" },
4668
- { value: "fingerprint", name: `Copy masked fingerprint (${maskKey(existingKey)})` },
4669
- { value: "change", name: "Update key" }
4670
- ] : [{ value: "change", name: "Enter key" }, { value: "skip", name: "Skip" }];
4671
- const action = await select({
4672
- message: "Action:",
4673
- choices
4674
- });
4675
- if (action === "fingerprint" && existingKey) {
4676
- console.log(theme.dim(`
4677
- Fingerprint: ${maskKey(existingKey)}`));
4678
- console.log(theme.dim(" Full secrets are not printed. Use Update key to replace it.\n"));
4679
- return;
4680
- } else if (action === "keep" || action === "skip") {
4681
- return;
4682
- }
4683
- const newKey = await password({
4684
- message: `Enter ${displayName} API key:`,
4685
- validate: (val) => val.length > 0 || "API key cannot be empty"
4686
- });
4687
- this.config.setApiKey(providerId, newKey);
4688
- console.log(theme.success(`API key saved for ${displayName}: ${maskKey(newKey)}
4689
- `));
4690
- }
4691
- /** Ollama 专用配置:检测连通性、发现本地模型、可选自定义地址 */
4692
- async setupOllama() {
4693
- console.log(theme.heading("\n\u{1F999} Ollama (Local) Configuration"));
4694
- console.log(theme.dim(" Ollama runs AI models locally \u2014 no API key needed."));
4695
- console.log(theme.dim(" Install: https://ollama.com\n"));
4696
- const customBaseUrls = this.config.get("customBaseUrls") ?? {};
4697
- const currentHost = customBaseUrls["ollama"] ? customBaseUrls["ollama"].replace(/\/v1\/?$/, "") : OLLAMA_DEFAULT_HOST;
4698
- const ollamaHost = await input({
4699
- message: "Ollama server address:",
4700
- default: currentHost,
4701
- validate: (val) => {
4702
- if (!val) return "Address cannot be empty";
4703
- if (!val.startsWith("http://") && !val.startsWith("https://")) {
4704
- return "Must start with http:// or https://";
4705
- }
4706
- return true;
4707
- }
4708
- });
4709
- const host = ollamaHost.replace(/\/+$/, "");
4710
- console.log(theme.dim(`
4711
- Connecting to ${host}...`));
4712
- let models = [];
4713
- try {
4714
- const res = await fetch(`${host}/api/tags`, { signal: AbortSignal.timeout(5e3) });
4715
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
4716
- const data = await res.json();
4717
- models = data.models ?? [];
4718
- } catch {
4719
- console.log(theme.error(` \u2717 Cannot connect to Ollama at ${host}`));
4720
- console.log(theme.dim(" Make sure Ollama is running: ollama serve\n"));
4721
- return;
4722
- }
4723
- if (models.length === 0) {
4724
- console.log(theme.warning(" \u26A0 Ollama is running but no models installed."));
4725
- console.log(theme.dim(" Install a model: ollama pull llama3.1\n"));
4726
- return;
4727
- }
4728
- console.log(theme.success(` \u2713 Connected! Found ${models.length} model(s):
4729
- `));
4730
- for (const m of models) {
4731
- const size = m.details?.parameter_size ?? `${(m.size / 1e9).toFixed(1)}GB`;
4732
- console.log(` \u2022 ${m.name} ${theme.dim(`(${size})`)}`);
4733
- }
4734
- console.log();
4735
- if (host !== OLLAMA_DEFAULT_HOST) {
4736
- const urls = { ...customBaseUrls, ollama: `${host}/v1` };
4737
- this.config.set("customBaseUrls", urls);
4738
- }
4739
- this.config.save();
4740
- console.log(theme.success(" Ollama configured! Use /provider ollama to switch.\n"));
4741
- }
4742
- async setupProxy() {
4743
- const current = this.config.get("proxy") ?? "";
4744
- console.log("\nHTTP/HTTPS Proxy Configuration");
4745
- console.log(theme.dim(" Used for providers that require a proxy (e.g. Gemini in China)"));
4746
- if (current) {
4747
- console.log(theme.dim(` Current: ${current}`));
4748
- }
4749
- const action = await select({
4750
- message: "Action:",
4751
- choices: current ? [
4752
- { value: "keep", name: "Keep current" },
4753
- { value: "change", name: "Update" },
4754
- { value: "clear", name: "Remove proxy" }
4755
- ] : [
4756
- { value: "change", name: "Set proxy" },
4757
- { value: "skip", name: "Skip" }
4758
- ]
4759
- });
4760
- if (action === "keep" || action === "skip") return;
4761
- if (action === "clear") {
4762
- this.config.set("proxy", void 0);
4763
- this.config.save();
4764
- console.log(theme.success("Proxy cleared.\n"));
4765
- return;
4766
- }
4767
- const proxyUrl = await input({
4768
- message: "Enter proxy URL (e.g. http://127.0.0.1:10809):",
4769
- default: current || void 0,
4770
- validate: (val) => {
4771
- if (!val) return "Proxy URL cannot be empty";
4772
- if (!val.startsWith("http://") && !val.startsWith("https://")) {
4773
- return "Must start with http:// or https://";
4774
- }
4775
- return true;
4776
- }
4777
- });
4778
- this.config.set("proxy", proxyUrl);
4779
- this.config.save();
4780
- console.log(theme.success(`Proxy saved: ${proxyUrl}
4781
- `));
4782
- }
4783
- /** Behavior toggles — alwaysYolo, mcpEnabled, and friends (v0.4.126+). */
4784
- async setupBehavior() {
4785
- let running = true;
4786
- while (running) {
4787
- const yoloOn = this.config.get("alwaysYolo") === true;
4788
- const mcpEnabled = this.config.get("mcpEnabled") !== false;
4789
- const yoloStatus = yoloOn ? theme.warning("[ON]") : theme.dim("[off]");
4790
- const mcpStatus = mcpEnabled ? theme.success("[enabled]") : theme.warning("[disabled]");
4791
- console.log(theme.heading("\n\u2699 Behavior Toggles\n"));
4792
- const action = await select({
4793
- message: "Which toggle?",
4794
- choices: [
4795
- { value: "yolo", name: `alwaysYolo ${yoloStatus}` },
4796
- { value: "mcp", name: `mcpEnabled ${mcpStatus}` },
4797
- { value: "done", name: "Done" }
4798
- ]
4799
- });
4800
- if (action === "yolo") {
4801
- await this.toggleAlwaysYolo();
4802
- } else if (action === "mcp") {
4803
- await this.toggleMcpEnabled();
4804
- } else {
4805
- running = false;
4806
- }
4807
- }
4808
- }
4809
- async toggleAlwaysYolo() {
4810
- const current = this.config.get("alwaysYolo") === true;
4811
- console.log(" alwaysYolo: " + (current ? theme.warning("ON") : theme.dim("off")));
4812
- console.log(
4813
- theme.dim(
4814
- " When ON, REPL starts with sessionAutoApprove enabled \u2014 write\n tools (write_file/edit_file, etc.) run without confirmation prompts. Destructive tools still require confirmation. Only enable this\n if you fully trust the working directory you run aicli in.\n"
4815
- )
4816
- );
4817
- const next = await confirm({
4818
- message: current ? "Keep alwaysYolo enabled?" : "Enable alwaysYolo (skip write confirmations; destructive still confirms)?",
4819
- default: current
4820
- });
4821
- if (next === current) {
4822
- console.log(theme.dim(" No change.\n"));
4823
- return;
4824
- }
4825
- this.config.set("alwaysYolo", next);
4826
- this.config.save();
4827
- if (next) {
4828
- console.log(theme.warning(" \u26A1 alwaysYolo enabled \u2014 confirmations will be skipped on next REPL start.\n"));
4829
- } else {
4830
- console.log(theme.success(" alwaysYolo disabled \u2014 confirmations restored on next REPL start.\n"));
4831
- }
4832
- }
4833
- async toggleMcpEnabled() {
4834
- const current = this.config.get("mcpEnabled") !== false;
4835
- console.log(" mcpEnabled: " + (current ? theme.success("enabled") : theme.warning("disabled")));
4836
- console.log(
4837
- theme.dim(
4838
- " Global switch for the MCP (Model Context Protocol) subsystem. When disabled,\n REPL startup skips loadProjectMcpConfig() and mcpManager.connectAll() \u2014\n no MCP servers are spawned, no MCP tools appear in the tool list, faster boot.\n Per-server entries in config.mcpServers are preserved but ignored. Disable this\n if you don't use MCP and want a leaner startup / smaller tool schema.\n"
4839
- )
4840
- );
4841
- const next = await confirm({
4842
- message: current ? "Keep MCP enabled?" : "Re-enable MCP?",
4843
- default: current
4844
- });
4845
- if (next === current) {
4846
- console.log(theme.dim(" No change.\n"));
4847
- return;
4848
- }
4849
- this.config.set("mcpEnabled", next);
4850
- this.config.save();
4851
- if (next) {
4852
- console.log(theme.success(" MCP enabled \u2014 servers will connect on next REPL start.\n"));
4853
- } else {
4854
- console.log(theme.warning(" \u{1F50C} MCP disabled \u2014 no MCP servers will load on next REPL start.\n"));
4855
- }
4856
- }
4857
- async setupGoogleSearch() {
4858
- console.log("\nGoogle Custom Search Configuration");
4859
- console.log(theme.dim(" Enables AI to search the web via google_search tool"));
4860
- console.log(theme.dim(" Requires: API Key + Search Engine ID (cx)"));
4861
- console.log(theme.dim(" Get API Key: https://console.cloud.google.com/apis/credentials"));
4862
- console.log(theme.dim(" Get CX: https://programmablesearchengine.google.com/"));
4863
- const existingKey = this.config.getApiKey("google-search");
4864
- if (existingKey) {
4865
- console.log(theme.dim(`
4866
- API Key: ${maskKey(existingKey)}`));
4867
- } else {
4868
- console.log(theme.dim("\n API Key: (not configured)"));
4869
- }
4870
- const keyAction = await select({
4871
- message: "API Key:",
4872
- choices: existingKey ? [
4873
- { value: "keep", name: "Keep current" },
4874
- { value: "change", name: "Update API Key" }
4875
- ] : [
4876
- { value: "change", name: "Enter API Key" },
4877
- { value: "skip", name: "Skip" }
4878
- ]
4879
- });
4880
- if (keyAction === "change") {
4881
- const newKey = await password({
4882
- message: "Enter Google API Key:",
4883
- validate: (val) => val.length > 0 || "API key cannot be empty"
4884
- });
4885
- this.config.setApiKey("google-search", newKey);
4886
- console.log(theme.success(` API Key saved: ${maskKey(newKey)}`));
4887
- }
4888
- const existingCx = this.config.get("googleSearchEngineId") ?? "";
4889
- if (existingCx) {
4890
- console.log(theme.dim(`
4891
- Search Engine ID: ${existingCx}`));
4892
- } else {
4893
- console.log(theme.dim("\n Search Engine ID: (not configured)"));
4894
- }
4895
- const cxAction = await select({
4896
- message: "Search Engine ID (cx):",
4897
- choices: existingCx ? [
4898
- { value: "keep", name: "Keep current" },
4899
- { value: "change", name: "Update CX" }
4900
- ] : [
4901
- { value: "change", name: "Enter CX" },
4902
- { value: "skip", name: "Skip" }
4903
- ]
4904
- });
4905
- if (cxAction === "change") {
4906
- const newCx = await input({
4907
- message: "Enter Search Engine ID (cx):",
4908
- default: existingCx || void 0,
4909
- validate: (val) => val.length > 0 || "Search Engine ID cannot be empty"
4910
- });
4911
- this.config.set("googleSearchEngineId", newCx);
4912
- this.config.save();
4913
- console.log(theme.success(` Search Engine ID saved: ${newCx}`));
4914
- }
4915
- console.log();
4916
- }
4917
- /** 收集用户身份档案 — aicli 的 "灵魂" 功能 */
4918
- async setupProfile() {
4919
- const existing = this.config.get("userProfile") ?? {};
4920
- console.log(theme.heading("\n\u{1F464} Your Profile"));
4921
- console.log(theme.dim(" This helps AI know who you are \u2014 across all providers and sessions.\n"));
4922
- const name = await input({
4923
- message: "Your name:",
4924
- default: existing.name || void 0
4925
- });
4926
- const nickname = await input({
4927
- message: "Preferred nickname (AI will call you this):",
4928
- default: existing.nickname || void 0
4929
- });
4930
- const role = await input({
4931
- message: 'Your role (e.g. "Full-stack developer", "Student"):',
4932
- default: existing.role || void 0
4933
- });
4934
- const bio = await input({
4935
- message: "Brief intro (1-2 sentences about your background):",
4936
- default: existing.bio || void 0
4937
- });
4938
- const interestsStr = await input({
4939
- message: "Interests / tech stack (comma-separated):",
4940
- default: existing.interests?.join(", ") || void 0
4941
- });
4942
- const locale = await input({
4943
- message: 'Preferred language (e.g. "zh-CN", "en", "ja"):',
4944
- default: existing.locale || void 0
4945
- });
4946
- const profile = {};
4947
- if (name) profile.name = name;
4948
- if (nickname) profile.nickname = nickname;
4949
- if (role) profile.role = role;
4950
- if (bio) profile.bio = bio;
4951
- if (interestsStr) profile.interests = interestsStr.split(",").map((s) => s.trim()).filter(Boolean);
4952
- if (locale) profile.locale = locale;
4953
- if (existing.extra) profile.extra = existing.extra;
4954
- this.config.set("userProfile", profile);
4955
- this.config.save();
4956
- const displayName = nickname || name || "User";
4957
- console.log(theme.success(`
4958
- Profile saved! AI will know you as "${displayName}".
4959
- `));
4960
- }
4961
- };
4962
-
4963
4485
  // src/repl/custom-commands.ts
4964
4486
  import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, mkdirSync as mkdirSync3 } from "fs";
4965
4487
  import { join as join3, extname } from "path";
@@ -5225,12 +4747,12 @@ var IMAGE_MIME = {
5225
4747
  };
5226
4748
  var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
5227
4749
  var MAX_PATH_SPACE_TOKENS = 8;
5228
- function expandUnquotedPath(input2, pathStart, rawPath, cwd) {
4750
+ function expandUnquotedPath(input, pathStart, rawPath, cwd) {
5229
4751
  const plain = { raw: rawPath, consumed: rawPath.length };
5230
4752
  if (existsSync4(resolve2(cwd, rawPath))) return plain;
5231
4753
  let acc = rawPath;
5232
4754
  let consumed = rawPath.length;
5233
- let rest = input2.slice(pathStart + rawPath.length);
4755
+ let rest = input.slice(pathStart + rawPath.length);
5234
4756
  for (let i = 0; i < MAX_PATH_SPACE_TOKENS; i++) {
5235
4757
  const next = /^([ \t]+)(\S+)/.exec(rest);
5236
4758
  if (!next) break;
@@ -5241,18 +4763,18 @@ function expandUnquotedPath(input2, pathStart, rawPath, cwd) {
5241
4763
  }
5242
4764
  return plain;
5243
4765
  }
5244
- function parseAtReferences(input2, cwd) {
4766
+ function parseAtReferences(input, cwd) {
5245
4767
  const atPattern = /@(?:"([^"]+)"|'([^']+)'|(\S+))/g;
5246
4768
  const refs = [];
5247
4769
  const imageParts = [];
5248
- let textBody = input2;
4770
+ let textBody = input;
5249
4771
  let match;
5250
- while ((match = atPattern.exec(input2)) !== null) {
4772
+ while ((match = atPattern.exec(input)) !== null) {
5251
4773
  let rawPath = match[1] ?? match[2] ?? match[3] ?? "";
5252
4774
  let matchedText = match[0];
5253
4775
  if (match[3] !== void 0) {
5254
4776
  const pathStart = match.index + 1;
5255
- const expanded = expandUnquotedPath(input2, pathStart, rawPath, cwd);
4777
+ const expanded = expandUnquotedPath(input, pathStart, rawPath, cwd);
5256
4778
  if (expanded.consumed > rawPath.length) {
5257
4779
  rawPath = expanded.raw;
5258
4780
  matchedText = "@" + expanded.raw;
@@ -5321,7 +4843,7 @@ function removePreviousGrapheme(line, cursor) {
5321
4843
  cursor: start
5322
4844
  };
5323
4845
  }
5324
- function installUnicodeBackspaceRepair(input2, rl, shouldHandle = () => true) {
4846
+ function installUnicodeBackspaceRepair(input, rl, shouldHandle = () => true) {
5325
4847
  const editable = rl;
5326
4848
  let previous = { line: editable.line, cursor: editable.cursor };
5327
4849
  const onKeypress = (_character, key) => {
@@ -5336,20 +4858,8 @@ function installUnicodeBackspaceRepair(input2, rl, shouldHandle = () => true) {
5336
4858
  }
5337
4859
  previous = current;
5338
4860
  };
5339
- input2.on("keypress", onKeypress);
5340
- return () => input2.off("keypress", onKeypress);
5341
- }
5342
-
5343
- // src/repl/chat-params.ts
5344
- function chatParamsFrom(params) {
5345
- return {
5346
- temperature: params.temperature,
5347
- maxTokens: params.maxTokens,
5348
- timeout: params.timeout,
5349
- thinking: params.thinking,
5350
- thinkingEffort: params.thinkingEffort,
5351
- thinkingBudget: params.thinkingBudget
5352
- };
4861
+ input.on("keypress", onKeypress);
4862
+ return () => input.off("keypress", onKeypress);
5353
4863
  }
5354
4864
 
5355
4865
  // src/core/model-router.ts
@@ -5721,6 +5231,13 @@ var Repl = class {
5721
5231
  /** 本次会话累计 token 用量 */
5722
5232
  sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
5723
5233
  unknownPricingWarnings = /* @__PURE__ */ new Set();
5234
+ /**
5235
+ * 本会话按**调用时刻**费率累计的成本(美元)。峰谷计价下,事后拿聚合用量现算
5236
+ * 会给出错的数字——闲时跑的会话在峰时查看会被按峰价显示(审计 P3-01)。
5237
+ */
5238
+ sessionAccruedCostUsd = 0;
5239
+ /** 本会话是否出现过定价未知的调用(此时累计值不完整,展示层需据实说明) */
5240
+ sessionCostIncomplete = false;
5724
5241
  /** Fold a single-request TokenUsage (with optional cache fields) into sessionTokenUsage + cost tracker.
5725
5242
  * modelOverride lets the smart router attribute cost to the actually-used model
5726
5243
  * when it differs from the UI-selected currentModel. */
@@ -5738,7 +5255,9 @@ var Repl = class {
5738
5255
  this.renderer.printInfo(`Cost notice: pricing is unknown for ${key}. Tokens are tracked, but cost is excluded from totals. Run aicli usage to review unknown-priced models.`);
5739
5256
  }
5740
5257
  }
5741
- this.costTracker.addCost(this.currentProvider, effectiveModel, u);
5258
+ const priced = this.costTracker.addCost(this.currentProvider, effectiveModel, u);
5259
+ if (priced === null) this.sessionCostIncomplete = true;
5260
+ else this.sessionAccruedCostUsd += priced;
5742
5261
  }
5743
5262
  /** 启动时检测到的 Git 分支(无 git 仓库时为 null) */
5744
5263
  gitBranch = null;
@@ -6160,7 +5679,8 @@ ${ctxLine}` : "")
6160
5679
  const skillTag = activeSkill ? theme.toolCall(`[${activeSkill.meta.name}]`) : "";
6161
5680
  const planTag = this.planMode ? theme.warning("[PLAN]") : "";
6162
5681
  const modelParams = this.getModelParams();
6163
- const thinkTag = modelParams.thinking ? theme.accent(`[THINK:${modelParams.thinkingEffort ?? "high"}]`) : "";
5682
+ const showsEffort = this.providers.get(this.currentProvider)?.supportsReasoningEffort ?? false;
5683
+ const thinkTag = modelParams.thinking ? theme.accent(showsEffort ? `[THINK:${modelParams.thinkingEffort ?? "high"}]` : "[THINK]") : "";
6164
5684
  const promptStr = theme.success(`[${this.currentProvider}]`) + skillTag + planTag + thinkTag + chalk5.white(" > ");
6165
5685
  this.rl.setPrompt(promptStr);
6166
5686
  }
@@ -6488,8 +6008,8 @@ Session '${this.resumeSessionId}' not found.
6488
6008
  let processing = false;
6489
6009
  let pasteTimer = null;
6490
6010
  const PASTE_DEBOUNCE_MS = 50;
6491
- const processInput = async (input2) => {
6492
- if (!input2) {
6011
+ const processInput = async (input) => {
6012
+ if (!input) {
6493
6013
  this.showPrompt();
6494
6014
  return;
6495
6015
  }
@@ -6499,10 +6019,10 @@ Session '${this.resumeSessionId}' not found.
6499
6019
  const savedOutput = rlAny.output;
6500
6020
  rlAny.output = null;
6501
6021
  try {
6502
- if (input2.startsWith("/")) {
6503
- await this.handleCommand(input2);
6022
+ if (input.startsWith("/")) {
6023
+ await this.handleCommand(input);
6504
6024
  } else {
6505
- await this.handleChat(input2);
6025
+ await this.handleChat(input);
6506
6026
  }
6507
6027
  } catch (err) {
6508
6028
  this.renderer.renderError(err);
@@ -7169,7 +6689,13 @@ Session '${this.resumeSessionId}' not found.
7169
6689
  spinner.stop();
7170
6690
  this.renderer.renderResponse(response.content);
7171
6691
  lastResponseStore.content = response.content;
7172
- session.addMessage({ role: "assistant", content: response.content, timestamp: /* @__PURE__ */ new Date() });
6692
+ session.addMessage({
6693
+ role: "assistant",
6694
+ content: response.content,
6695
+ timestamp: /* @__PURE__ */ new Date(),
6696
+ // 非流式路径同样要落 reasoningContent,否则 V4 thinking 的多轮推理断链(审计 P1-02)
6697
+ ...response.reasoningContent ? { reasoningContent: response.reasoningContent } : {}
6698
+ });
7173
6699
  this.events.emit("message.after", { content: response.content });
7174
6700
  if (response.usage) {
7175
6701
  this.addSessionUsage(response.usage, effectiveModel);
@@ -7852,8 +7378,8 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7852
7378
  await this.checkContextPressure();
7853
7379
  }
7854
7380
  }
7855
- async handleCommand(input2) {
7856
- const parts = input2.slice(1).split(" ");
7381
+ async handleCommand(input) {
7382
+ const parts = input.slice(1).split(" ");
7857
7383
  const cmdName = parts[0].toLowerCase();
7858
7384
  const args = parts.slice(1);
7859
7385
  const cmd = this.commands.get(cmdName);
@@ -7949,7 +7475,10 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7949
7475
  cacheCreationTokens: 0,
7950
7476
  cacheReadTokens: 0
7951
7477
  };
7478
+ this.sessionAccruedCostUsd = 0;
7479
+ this.sessionCostIncomplete = false;
7952
7480
  },
7481
+ getSessionAccruedCost: () => ({ usd: this.sessionAccruedCostUsd, incomplete: this.sessionCostIncomplete }),
7953
7482
  getGitBranch: () => this.gitBranch,
7954
7483
  getLastResponse: () => lastResponseStore.content,
7955
7484
  runSetupWizard: async () => {
@@ -7957,6 +7486,7 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7957
7486
  rlAny.output = process.stdout;
7958
7487
  process.stdin.resume();
7959
7488
  try {
7489
+ const { SetupWizard } = await import("./setup-wizard-S55ICNHH.js");
7960
7490
  const wizard = new SetupWizard(this.config);
7961
7491
  await wizard.runFull();
7962
7492
  } finally {
@@ -7977,6 +7507,9 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7977
7507
  this.runtimeThinking = value;
7978
7508
  this.refreshPrompt();
7979
7509
  },
7510
+ getProviderSupportsReasoningEffort: () => this.providers.get(this.currentProvider)?.supportsReasoningEffort ?? false,
7511
+ getConfiguredThinking: () => this.config.get("modelParams")[this.currentModel]?.thinking,
7512
+ getConfiguredThinkingEffort: () => this.config.get("modelParams")[this.currentModel]?.thinkingEffort,
7980
7513
  getThinkingEffort: () => this.runtimeThinkingEffort,
7981
7514
  setThinkingEffort: (value) => {
7982
7515
  this.runtimeThinkingEffort = value;
@@ -8115,7 +7648,7 @@ process.on("unhandledRejection", (reason) => {
8115
7648
  });
8116
7649
  var builtInProviderIds = listBuiltInProviderMetadata().map(({ id }) => id).join("/");
8117
7650
  program.name("aicli").description("Cross-platform REPL-style AI CLI with multi-provider support").version(VERSION);
8118
- program.option("--provider <name>", `AI provider to use (${builtInProviderIds}, or any custom provider)`).option("-m, --model <name>", "Model to use").option("-p, --prompt <text>", "Headless mode: send a single prompt and exit (reads stdin as context if piped)").option("--system <prompt>", "System prompt override (headless mode)").option("--json", "Output result as JSON object (headless mode only)").option("--output-format <format>", "Output format: text (default) | streaming-json (NDJSON per-chunk, headless only)").option("--no-stream", "Disable streaming output").option("--allowed-tools <tools>", "Comma-separated whitelist of tools (e.g. bash,read_file)").option("--blocked-tools <tools>", "Comma-separated blacklist of tools (e.g. bash,write_file)").option("--resume <id>", "Resume a previous session by ID (prefix match supported)").option("--max-tool-rounds <n>", "Max agentic tool-call rounds per conversation (default: 200)").action(async (options) => {
7651
+ program.option("--provider <name>", `AI provider to use (${builtInProviderIds}, or any custom provider)`).option("-m, --model <name>", "Model to use").option("-p, --prompt <text>", "Headless mode: send a single prompt and exit (reads stdin as context if piped)").option("--system <prompt>", "System prompt override (headless mode)").option("--json", "Output result as JSON object (headless mode only)").option("--output-format <format>", "Output format: text (default) | streaming-json (NDJSON per-chunk, headless only)").option("--no-stream", "Disable streaming output").option("--allowed-tools <tools>", "Comma-separated whitelist of tools (e.g. bash,read_file)").option("--blocked-tools <tools>", "Comma-separated blacklist of tools (e.g. bash,write_file)").option("--resume <id>", "Resume a previous session by ID (prefix match supported)").option("--max-tool-rounds <n>", "Max agentic tool-call rounds per conversation (default: 200)").option("--think <mode>", "Thinking for this run: off | on | low | high | max (default: follow config)").action(async (options) => {
8119
7652
  if (options.prompt !== void 0) {
8120
7653
  await runHeadless(options);
8121
7654
  } else {
@@ -8124,6 +7657,7 @@ program.option("--provider <name>", `AI provider to use (${builtInProviderIds},
8124
7657
  });
8125
7658
  program.command("config").description("Configure API keys and preferences").action(async () => {
8126
7659
  const config = new ConfigManager();
7660
+ const { SetupWizard } = await import("./setup-wizard-S55ICNHH.js");
8127
7661
  const wizard = new SetupWizard(config);
8128
7662
  await wizard.runFull();
8129
7663
  console.log("Configuration saved.");
@@ -8162,7 +7696,7 @@ program.command("web").description("Start Web UI server with browser-based chat
8162
7696
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
8163
7697
  process.exit(1);
8164
7698
  }
8165
- const { startWebServer } = await import("./server-XYVL7A33.js");
7699
+ const { startWebServer } = await import("./server-53GM3CRH.js");
8166
7700
  await startWebServer({ port, host: options.host });
8167
7701
  });
8168
7702
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -8329,11 +7863,11 @@ program.command("sessions").description("List recent conversation sessions").opt
8329
7863
  console.log(footer + "\n");
8330
7864
  });
8331
7865
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
8332
- const { runUsageCli } = await import("./usage-LGPJ4UAW.js");
7866
+ const { runUsageCli } = await import("./usage-E3FEYSBU.js");
8333
7867
  await runUsageCli(options);
8334
7868
  });
8335
7869
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
8336
- const { runDoctorCli } = await import("./doctor-cli-64L7BBK7.js");
7870
+ const { runDoctorCli } = await import("./doctor-cli-CO6BJWRW.js");
8337
7871
  const argv = process.argv.slice(2);
8338
7872
  await runDoctorCli({
8339
7873
  json: !!options.json || argv.includes("--json"),
@@ -8342,7 +7876,7 @@ program.command("doctor").description("Health check: API keys, config, MCP, rece
8342
7876
  });
8343
7877
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
8344
7878
  try {
8345
- const batch = await import("./batch-Q22M55UP.js");
7879
+ const batch = await import("./batch-FQU6HK3Z.js");
8346
7880
  switch (action) {
8347
7881
  case "submit":
8348
7882
  if (!arg) {
@@ -8385,7 +7919,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
8385
7919
  }
8386
7920
  });
8387
7921
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
8388
- const { startMcpServer } = await import("./server-GUV2HZ7N.js");
7922
+ const { startMcpServer } = await import("./server-B4A7LHZ5.js");
8389
7923
  await startMcpServer({
8390
7924
  allowDestructive: !!options.allowDestructive,
8391
7925
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -8398,7 +7932,7 @@ program.command("pr <action> [target]").description("PR review helpers: review |
8398
7932
  console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
8399
7933
  process.exit(1);
8400
7934
  }
8401
- const { runPrCli } = await import("./pr-DJP53EZV.js");
7935
+ const { runPrCli } = await import("./pr-CDOPCAPK.js");
8402
7936
  const result = await runPrCli({
8403
7937
  action,
8404
7938
  target,
@@ -8418,8 +7952,8 @@ program.command("pr <action> [target]").description("PR review helpers: review |
8418
7952
  process.exit(result.exitCode);
8419
7953
  });
8420
7954
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number or URL; diff fetched via gh pr diff <value>", (v) => /^#?\d+$/.test(v) ? parseInt(v.replace(/^#/, ""), 10) : v).option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").option("--json", "Output a structured JSON report").option("--markdown", "Output Markdown report (default)").option("--sarif", "Output SARIF 2.1.0 for code scanning artifacts").option("--fail-on-security-high", "Fail when the security review contains HIGH/CRITICAL findings (default)").option("--no-fail-on-security-high", "Do not fail on security HIGH/CRITICAL findings").option("--fail-on-test-failure", "Fail when the report mentions test failures (default)").option("--no-fail-on-test-failure", "Do not fail on test failure findings").option("--fail-on-lint-failure", "Fail when the report mentions lint/typecheck failures (default)").option("--no-fail-on-lint-failure", "Do not fail on lint/typecheck failure findings").action(async (options) => {
8421
- const { runCi } = await import("./ci-RMEIHZJL.js");
8422
- const { formatCiResult } = await import("./ci-format-UCFSWVG5.js");
7955
+ const { runCi } = await import("./ci-GZH3QMME.js");
7956
+ const { formatCiResult } = await import("./ci-format-VKP2MGYJ.js");
8423
7957
  const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
8424
7958
  if (formats > 1) {
8425
7959
  console.error("Choose only one output format: --json, --markdown, or --sarif.");
@@ -8577,7 +8111,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
8577
8111
  config.get("customProviders"),
8578
8112
  config.getConfigDir()
8579
8113
  );
8580
- const { startHub } = await import("./hub-P7D6VDCK.js");
8114
+ const { startHub } = await import("./hub-4APIRQ73.js");
8581
8115
  await startHub(
8582
8116
  {
8583
8117
  topic: topic ?? "",
@@ -8679,6 +8213,10 @@ ${options.prompt}` : options.prompt;
8679
8213
  const maxTokens = modelParams.maxTokens ?? DEFAULT_MAX_TOKENS;
8680
8214
  const timeout = modelParams.timeout ?? 6e4;
8681
8215
  const temperature = modelParams.temperature;
8216
+ const thinkingParams = applyThinkingFlag(
8217
+ thinkingParamsFor(allModelParams, model),
8218
+ options.think
8219
+ );
8682
8220
  let systemPrompt = options.system;
8683
8221
  if (!systemPrompt) {
8684
8222
  const contextConfig = config.get("context");
@@ -8711,7 +8249,8 @@ ${loaded.mergedContent}` : void 0
8711
8249
  stream: true,
8712
8250
  temperature,
8713
8251
  maxTokens,
8714
- timeout
8252
+ timeout,
8253
+ ...thinkingParams
8715
8254
  });
8716
8255
  let fullContent = "";
8717
8256
  let finalUsage;
@@ -8741,7 +8280,8 @@ ${loaded.mergedContent}` : void 0
8741
8280
  stream: false,
8742
8281
  temperature,
8743
8282
  maxTokens,
8744
- timeout
8283
+ timeout,
8284
+ ...thinkingParams
8745
8285
  });
8746
8286
  const out = {
8747
8287
  content: response.content,
@@ -8758,7 +8298,8 @@ ${loaded.mergedContent}` : void 0
8758
8298
  stream: true,
8759
8299
  temperature,
8760
8300
  maxTokens,
8761
- timeout
8301
+ timeout,
8302
+ ...thinkingParams
8762
8303
  });
8763
8304
  for await (const chunk of stream) {
8764
8305
  if (chunk.done) break;
@@ -8773,7 +8314,8 @@ ${loaded.mergedContent}` : void 0
8773
8314
  stream: false,
8774
8315
  temperature,
8775
8316
  maxTokens,
8776
- timeout
8317
+ timeout,
8318
+ ...thinkingParams
8777
8319
  });
8778
8320
  process.stdout.write(response.content + "\n");
8779
8321
  }
@@ -8789,6 +8331,7 @@ async function startRepl(options) {
8789
8331
  const config = new ConfigManager();
8790
8332
  await setupProxy(config.get("proxy"));
8791
8333
  if (config.isFirstRun()) {
8334
+ const { SetupWizard } = await import("./setup-wizard-S55ICNHH.js");
8792
8335
  const wizard = new SetupWizard(config);
8793
8336
  const configured = await wizard.runFirstRun();
8794
8337
  if (!configured) {