jinzd-ai-cli 0.4.69 → 0.4.70

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.
@@ -8,7 +8,7 @@ import {
8
8
  RateLimitError,
9
9
  schemaToJsonSchema,
10
10
  truncateForPersist
11
- } from "./chunk-ZKHLLGUU.js";
11
+ } from "./chunk-IVTWWDWZ.js";
12
12
  import {
13
13
  APP_NAME,
14
14
  CONFIG_DIR_NAME,
@@ -21,7 +21,7 @@ import {
21
21
  MCP_TOOL_PREFIX,
22
22
  PLUGINS_DIR_NAME,
23
23
  VERSION
24
- } from "./chunk-APJHOYCH.js";
24
+ } from "./chunk-ND3O5NQU.js";
25
25
 
26
26
  // src/config/config-manager.ts
27
27
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -417,21 +417,30 @@ var ClaudeProvider = class extends BaseProvider {
417
417
  }
418
418
  /**
419
419
  * Build a cacheable system prompt payload.
420
- * When the prompt is long enough to be worth caching, return an array with a
421
- * single text block carrying `cache_control: { type: 'ephemeral' }`. This caches
422
- * system + memory + context files across every request in an agentic loop.
423
- * Short prompts pass through as a plain string (no caching overhead).
420
+ *
421
+ * stable — session-invariant content (guidelines, memory, context). Marked with
422
+ * cache_control: ephemeral when long enough, so Claude reuses the KV cache
423
+ * across every round in an agentic loop AND across turns within a session.
424
+ * volatile — per-request content (date/time, working directory). Appended as a second
425
+ * text block WITHOUT cache_control so it never busts the stable cache.
426
+ *
427
+ * Short prompts (below the Anthropic minimum) pass through as a plain string.
424
428
  */
425
- buildSystemParam(systemPrompt) {
426
- if (!systemPrompt) return void 0;
427
- if (systemPrompt.length < CACHE_MIN_SYSTEM_CHARS) return systemPrompt;
428
- return [
429
- {
430
- type: "text",
431
- text: systemPrompt,
432
- cache_control: { type: "ephemeral" }
433
- }
429
+ buildSystemParam(stable, volatile) {
430
+ const stableText = stable ?? "";
431
+ const volatileText = volatile ?? "";
432
+ if (!stableText && !volatileText) return void 0;
433
+ if (stableText.length < CACHE_MIN_SYSTEM_CHARS) {
434
+ const combined = [stableText, volatileText].filter(Boolean).join("\n\n---\n\n");
435
+ return combined || void 0;
436
+ }
437
+ const blocks = [
438
+ { type: "text", text: stableText, cache_control: { type: "ephemeral" } }
434
439
  ];
440
+ if (volatileText) {
441
+ blocks.push({ type: "text", text: volatileText });
442
+ }
443
+ return blocks;
435
444
  }
436
445
  /**
437
446
  * Mark the last tool definition with `cache_control: ephemeral` so the entire
@@ -511,7 +520,7 @@ var ClaudeProvider = class extends BaseProvider {
511
520
  const response = await this.client.messages.create({
512
521
  model: request.model,
513
522
  messages,
514
- system: this.buildSystemParam(request.systemPrompt),
523
+ system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
515
524
  max_tokens: request.maxTokens ?? 8192,
516
525
  temperature,
517
526
  thinking
@@ -536,7 +545,7 @@ var ClaudeProvider = class extends BaseProvider {
536
545
  const stream = this.client.messages.stream({
537
546
  model: request.model,
538
547
  messages,
539
- system: this.buildSystemParam(request.systemPrompt),
548
+ system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
540
549
  max_tokens: request.maxTokens ?? 8192,
541
550
  temperature,
542
551
  thinking
@@ -593,7 +602,7 @@ var ClaudeProvider = class extends BaseProvider {
593
602
  model: request.model,
594
603
  messages: allMessages,
595
604
  tools: anthropicTools,
596
- system: this.buildSystemParam(request.systemPrompt),
605
+ system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
597
606
  max_tokens: request.maxTokens ?? 8192,
598
607
  temperature,
599
608
  thinking
@@ -649,7 +658,7 @@ var ClaudeProvider = class extends BaseProvider {
649
658
  model: request.model,
650
659
  messages: allMessages,
651
660
  tools: anthropicTools,
652
- system: this.buildSystemParam(request.systemPrompt),
661
+ system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
653
662
  max_tokens: request.maxTokens ?? 8192,
654
663
  temperature,
655
664
  thinking
@@ -909,7 +918,7 @@ var GeminiProvider = class extends BaseProvider {
909
918
  }
910
919
  const genModel = this.client.getGenerativeModel({
911
920
  model: request.model,
912
- systemInstruction: request.systemPrompt
921
+ systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
913
922
  }, this.reqOpts);
914
923
  const history = this.toGeminiHistory(request.messages.slice(0, -1));
915
924
  const lastMsgParts = this.contentToGeminiParts(
@@ -935,7 +944,7 @@ var GeminiProvider = class extends BaseProvider {
935
944
  }
936
945
  const genModel = this.client.getGenerativeModel({
937
946
  model: request.model,
938
- systemInstruction: request.systemPrompt
947
+ systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
939
948
  }, this.reqOpts);
940
949
  const history = this.toGeminiHistory(request.messages.slice(0, -1));
941
950
  const lastMsgParts = this.contentToGeminiParts(
@@ -1003,7 +1012,7 @@ var GeminiProvider = class extends BaseProvider {
1003
1012
  }
1004
1013
  const genModel = this.client.getGenerativeModel({
1005
1014
  model: request.model,
1006
- systemInstruction: request.systemPrompt
1015
+ systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
1007
1016
  }, this.reqOpts);
1008
1017
  const chat = genModel.startChat({ history: fullHistory, tools: geminiTools });
1009
1018
  const result = await chat.sendMessage(msgToSend);
@@ -1127,8 +1136,9 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1127
1136
  /** 将 systemPrompt + messages 合并为 OpenAI messages 数组(system 消息放首位)。 */
1128
1137
  buildMessages(request) {
1129
1138
  const msgs = this.normalizeMessages(request.messages);
1130
- if (request.systemPrompt) {
1131
- return [{ role: "system", content: request.systemPrompt }, ...msgs];
1139
+ const systemContent = [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n");
1140
+ if (systemContent) {
1141
+ return [{ role: "system", content: systemContent }, ...msgs];
1132
1142
  }
1133
1143
  return msgs;
1134
1144
  }
@@ -10,7 +10,7 @@ import {
10
10
  SUBAGENT_DEFAULT_MAX_ROUNDS,
11
11
  SUBAGENT_MAX_ROUNDS_LIMIT,
12
12
  runTestsTool
13
- } from "./chunk-APJHOYCH.js";
13
+ } from "./chunk-ND3O5NQU.js";
14
14
 
15
15
  // src/tools/builtin/bash.ts
16
16
  import { execSync } from "child_process";
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.4.69";
11
+ var VERSION = "0.4.70";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.69";
9
+ var VERSION = "0.4.70";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -385,7 +385,7 @@ ${content}`);
385
385
  }
386
386
  }
387
387
  async function runTaskMode(config, providers, configManager, topic) {
388
- const { TaskOrchestrator } = await import("./task-orchestrator-AZPMRFET.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-EB2XPY5S.js");
389
389
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
390
390
  let interrupted = false;
391
391
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  saveDevState,
32
32
  sessionHasMeaningfulContent,
33
33
  setupProxy
34
- } from "./chunk-3IZATQV5.js";
34
+ } from "./chunk-3YT2DUUT.js";
35
35
  import {
36
36
  ToolExecutor,
37
37
  ToolRegistry,
@@ -47,7 +47,7 @@ import {
47
47
  spawnAgentContext,
48
48
  theme,
49
49
  undoStack
50
- } from "./chunk-ZKHLLGUU.js";
50
+ } from "./chunk-IVTWWDWZ.js";
51
51
  import {
52
52
  fileCheckpoints
53
53
  } from "./chunk-4BKXL7SM.js";
@@ -72,7 +72,7 @@ import {
72
72
  SKILLS_DIR_NAME,
73
73
  VERSION,
74
74
  buildUserIdentityPrompt
75
- } from "./chunk-APJHOYCH.js";
75
+ } from "./chunk-ND3O5NQU.js";
76
76
 
77
77
  // src/index.ts
78
78
  import { program } from "commander";
@@ -2267,7 +2267,7 @@ ${hint}` : "")
2267
2267
  usage: "/test [command|filter]",
2268
2268
  async execute(args, ctx) {
2269
2269
  try {
2270
- const { executeTests } = await import("./run-tests-5HDSNAAC.js");
2270
+ const { executeTests } = await import("./run-tests-NPRCZYN3.js");
2271
2271
  const argStr = args.join(" ").trim();
2272
2272
  let testArgs = {};
2273
2273
  if (argStr) {
@@ -4120,17 +4120,28 @@ ${projectContext}`);
4120
4120
  if (gitContextStr) parts.push(gitContextStr);
4121
4121
  return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
4122
4122
  }
4123
- /** 每次请求时动态生成含当前日期时间的 system prompt。 */
4123
+ /**
4124
+ * Build the system prompt split into stable (cacheable) and volatile (non-cacheable) parts.
4125
+ *
4126
+ * stable — identity, guidelines, memory, dev state, project context, skills, plan mode.
4127
+ * Content is session-invariant → Claude caches it across every agentic round
4128
+ * AND across turns, so only the first request of a session pays full input cost.
4129
+ * volatile — current date/time + working directory. Appended as a separate uncached block
4130
+ * so it never invalidates the stable cache.
4131
+ *
4132
+ * ⚠️ 注意:不使用 toLocaleDateString/toLocaleTimeString,
4133
+ * pkg 打包的精简 ICU 不支持 locale 参数,会回退到错误格式。
4134
+ */
4124
4135
  buildCurrentSystemPrompt() {
4125
4136
  const now = /* @__PURE__ */ new Date();
4126
4137
  const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4127
4138
  const pad = (n) => String(n).padStart(2, "0");
4128
4139
  const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${WEEKDAYS[now.getDay()]}`;
4129
4140
  const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
4130
- const dateTimeInfo = `Current date and time: ${dateStr} ${timeStr}`;
4131
4141
  const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
4132
4142
  const shellInfo = process.platform === "win32" ? "The bash tool uses PowerShell (not cmd). Use PowerShell command syntax. Do not use Unix commands (e.g. grep, head, find, wc, date +format, etc.), use PowerShell equivalents instead (e.g. Select-String, Select-Object -First, Get-ChildItem, Measure-Object, Get-Date -Format, etc.)." : `The bash tool uses ${process.env.SHELL || "/bin/bash"}.`;
4133
- const envInfo = `OS: ${osName}
4143
+ const volatile = `Current date and time: ${dateStr} ${timeStr}
4144
+ OS: ${osName}
4134
4145
  ${shellInfo}
4135
4146
  Working directory: ${process.cwd()}`;
4136
4147
  const effectiveMaxRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
@@ -4142,24 +4153,20 @@ You have a maximum of ${effectiveMaxRounds} tool call rounds per conversation tu
4142
4153
  - On Windows, use PowerShell cmdlets (Invoke-RestMethod, Get-ChildItem) instead of Unix commands (curl, grep, find).
4143
4154
  - If starting a long-running server process via bash, use background execution \u2014 do not block the tool round.
4144
4155
  - Prioritize the most critical tasks first in case you run out of rounds.`;
4145
- const parts = [dateTimeInfo + "\n" + envInfo];
4156
+ const stableParts = [];
4146
4157
  const userProfile = this.config.get("userProfile");
4147
4158
  if (userProfile) {
4148
4159
  const identityPrompt = buildUserIdentityPrompt(userProfile);
4149
- if (identityPrompt) {
4150
- parts.push(identityPrompt);
4151
- }
4160
+ if (identityPrompt) stableParts.push(identityPrompt);
4152
4161
  }
4153
- parts.push(AGENTIC_BEHAVIOR_GUIDELINE + budgetInfo);
4162
+ stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE + budgetInfo);
4154
4163
  const memory = this.loadMemoryContent();
4155
- if (memory) {
4156
- parts.push(`# Persistent Memory
4164
+ if (memory) stableParts.push(`# Persistent Memory
4157
4165
 
4158
4166
  ${memory.content}`);
4159
- }
4160
4167
  const devState = loadDevState();
4161
4168
  if (devState) {
4162
- parts.push(
4169
+ stableParts.push(
4163
4170
  `# Development State Handoff
4164
4171
 
4165
4172
  The following is a development state snapshot generated by the previous AI model. Please seamlessly continue the user's work based on this.
@@ -4168,25 +4175,21 @@ Important: If the snapshot mentions key reference files (e.g. templates, format
4168
4175
  ` + devState
4169
4176
  );
4170
4177
  }
4171
- if (this.activeSystemPrompt) {
4172
- parts.push(this.activeSystemPrompt);
4173
- }
4178
+ if (this.activeSystemPrompt) stableParts.push(this.activeSystemPrompt);
4174
4179
  for (const { dir, content } of this.extraContextDirs) {
4175
- parts.push(`# Added Directory Context: ${dir}
4180
+ stableParts.push(`# Added Directory Context: ${dir}
4176
4181
 
4177
4182
  ${content}`);
4178
4183
  }
4179
4184
  const skillContent = this.skillManager?.getActivePromptContent();
4180
4185
  if (skillContent) {
4181
4186
  const skillName = this.skillManager.getActive().meta.name;
4182
- parts.push(`# Active Skill: ${skillName}
4187
+ stableParts.push(`# Active Skill: ${skillName}
4183
4188
 
4184
4189
  ${skillContent}`);
4185
4190
  }
4186
- if (this.planMode) {
4187
- parts.push(PLAN_MODE_SYSTEM_ADDON);
4188
- }
4189
- return parts.join("\n\n---\n\n");
4191
+ if (this.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
4192
+ return { stable: stableParts.join("\n\n---\n\n"), volatile };
4190
4193
  }
4191
4194
  /**
4192
4195
  * 上下文压缩:调用当前 provider 生成对话摘要,替换旧消息,保留最近 COMPACT_KEEP_LAST 条。
@@ -4237,10 +4240,12 @@ ${skillContent}`);
4237
4240
  ...session.messages,
4238
4241
  { role: "user", content: summaryPromptText, timestamp: /* @__PURE__ */ new Date() }
4239
4242
  ];
4243
+ const { stable: syStable, volatile: syVolatile } = this.buildCurrentSystemPrompt();
4240
4244
  const response = await provider.chat({
4241
4245
  messages: summaryMessages,
4242
4246
  model: this.currentModel,
4243
- systemPrompt: this.buildCurrentSystemPrompt(),
4247
+ systemPrompt: syStable,
4248
+ systemPromptVolatile: syVolatile,
4244
4249
  stream: false,
4245
4250
  temperature: 0.3,
4246
4251
  maxTokens: Math.min(modelParams.maxTokens ?? DEFAULT_MAX_TOKENS, 4096),
@@ -4302,10 +4307,12 @@ ${response.content.trim()}
4302
4307
  timestamp: /* @__PURE__ */ new Date()
4303
4308
  }
4304
4309
  ];
4310
+ const { stable: snStable, volatile: snVolatile } = this.buildCurrentSystemPrompt();
4305
4311
  const response = await provider.chat({
4306
4312
  messages: snapshotMessages,
4307
4313
  model: this.currentModel,
4308
- systemPrompt: this.buildCurrentSystemPrompt(),
4314
+ systemPrompt: snStable,
4315
+ systemPromptVolatile: snVolatile,
4309
4316
  stream: false,
4310
4317
  temperature: 0.3,
4311
4318
  maxTokens: Math.min(modelParams.maxTokens ?? 4096, 4096),
@@ -4779,7 +4786,8 @@ Session '${this.resumeSessionId}' not found.
4779
4786
  */
4780
4787
  estimateConversationTokens() {
4781
4788
  let total = 0;
4782
- const sysPrompt = this.buildCurrentSystemPrompt();
4789
+ const { stable: estStable, volatile: estVolatile } = this.buildCurrentSystemPrompt();
4790
+ const sysPrompt = [estStable, estVolatile].filter(Boolean).join("\n\n---\n\n");
4783
4791
  if (sysPrompt) total += this.estimateTokens(sysPrompt);
4784
4792
  const session = this.sessions.current;
4785
4793
  if (session) {
@@ -5068,13 +5076,15 @@ Session '${this.resumeSessionId}' not found.
5068
5076
  const useStreaming = this.config.get("ui").streaming;
5069
5077
  const effectiveModel = modelOverride ?? this.currentModel;
5070
5078
  const modelParams = this.getModelParams(effectiveModel);
5079
+ const { stable: chatStable, volatile: chatVolatile } = this.buildCurrentSystemPrompt();
5071
5080
  if (useStreaming) {
5072
5081
  const ac = this.setupStreamInterrupt();
5073
5082
  try {
5074
5083
  const stream = provider.chatStream({
5075
5084
  messages,
5076
5085
  model: effectiveModel,
5077
- systemPrompt: this.buildCurrentSystemPrompt(),
5086
+ systemPrompt: chatStable,
5087
+ systemPromptVolatile: chatVolatile,
5078
5088
  stream: true,
5079
5089
  temperature: modelParams.temperature,
5080
5090
  maxTokens: modelParams.maxTokens,
@@ -5108,7 +5118,8 @@ Session '${this.resumeSessionId}' not found.
5108
5118
  const response = await provider.chat({
5109
5119
  messages,
5110
5120
  model: effectiveModel,
5111
- systemPrompt: this.buildCurrentSystemPrompt(),
5121
+ systemPrompt: chatStable,
5122
+ systemPromptVolatile: chatVolatile,
5112
5123
  stream: false,
5113
5124
  temperature: modelParams.temperature,
5114
5125
  maxTokens: modelParams.maxTokens,
@@ -5275,7 +5286,7 @@ Session '${this.resumeSessionId}' not found.
5275
5286
  const maxToolRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
5276
5287
  const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
5277
5288
  const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
5278
- const baseSystemPrompt = (this.buildCurrentSystemPrompt() ?? "") + TOOL_CALL_REMINDER;
5289
+ const { stable: toolStable, volatile: toolVolatile } = this.buildCurrentSystemPrompt();
5279
5290
  const pauseHint = autoPauseInterval > 0 ? `
5280
5291
  - Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
5281
5292
  const roundBudgetHint = this.planMode ? `
@@ -5295,9 +5306,10 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
5295
5306
  - Do NOT read the same file more than once \u2014 use the content from previous reads.
5296
5307
  - Prioritize the most critical tasks first in case rounds run out.
5297
5308
  - When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
5298
- const systemPrompt = baseSystemPrompt + roundBudgetHint + (mcpBudgetNote ? `
5309
+ const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
5299
5310
 
5300
5311
  ${mcpBudgetNote}` : "");
5312
+ const systemPromptVolatile = toolVolatile;
5301
5313
  const modelParams = this.getModelParams(effectiveModel);
5302
5314
  const useStreaming = this.config.get("ui").streaming;
5303
5315
  const spinner = this.renderer.showSpinner("Thinking...");
@@ -5418,6 +5430,7 @@ ${mcpBudgetNote}` : "");
5418
5430
  messages: apiMessages,
5419
5431
  model: effectiveModel,
5420
5432
  systemPrompt,
5433
+ systemPromptVolatile,
5421
5434
  stream: false,
5422
5435
  temperature: modelParams.temperature,
5423
5436
  maxTokens: modelParams.maxTokens,
@@ -5573,6 +5586,7 @@ ${mcpBudgetNote}` : "");
5573
5586
  messages: apiMessages,
5574
5587
  model: effectiveModel,
5575
5588
  systemPrompt,
5589
+ systemPromptVolatile,
5576
5590
  stream: true,
5577
5591
  temperature: modelParams.temperature,
5578
5592
  maxTokens: modelParams.maxTokens,
@@ -5623,14 +5637,22 @@ ${mcpBudgetNote}` : "");
5623
5637
  googleSearchContext.configManager = this.config;
5624
5638
  streamToFileContext.provider = provider;
5625
5639
  streamToFileContext.model = effectiveModel;
5626
- streamToFileContext.systemPrompt = systemPrompt;
5640
+ streamToFileContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
5641
+
5642
+ ---
5643
+
5644
+ ${systemPromptVolatile}` : systemPrompt;
5627
5645
  streamToFileContext.messages = apiMessages;
5628
5646
  streamToFileContext.extraMessages = extraMessages;
5629
5647
  streamToFileContext.temperature = modelParams.temperature;
5630
5648
  streamToFileContext.timeout = modelParams.timeout;
5631
5649
  spawnAgentContext.provider = provider;
5632
5650
  spawnAgentContext.model = effectiveModel;
5633
- spawnAgentContext.systemPrompt = systemPrompt;
5651
+ spawnAgentContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
5652
+
5653
+ ---
5654
+
5655
+ ${systemPromptVolatile}` : systemPrompt;
5634
5656
  spawnAgentContext.modelParams = modelParams;
5635
5657
  spawnAgentContext.configManager = this.config;
5636
5658
  ToolExecutor.currentMessageIndex = session.messages.length;
@@ -5791,6 +5813,7 @@ ${mcpBudgetNote}` : "");
5791
5813
  messages: apiMessages,
5792
5814
  model: effectiveModel,
5793
5815
  systemPrompt,
5816
+ systemPromptVolatile,
5794
5817
  stream: false,
5795
5818
  temperature: modelParams.temperature,
5796
5819
  maxTokens: modelParams.maxTokens,
@@ -5852,10 +5875,12 @@ Tip: You can continue the conversation by asking the AI to proceed.`
5852
5875
  try {
5853
5876
  const provider = this.providers.get(this.currentProvider);
5854
5877
  const modelParams = this.getModelParams();
5878
+ const { stable: ccStable, volatile: ccVolatile } = this.buildCurrentSystemPrompt();
5855
5879
  const response = await provider.chat({
5856
5880
  messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
5857
5881
  model: this.currentModel,
5858
- systemPrompt: this.buildCurrentSystemPrompt(),
5882
+ systemPrompt: ccStable,
5883
+ systemPromptVolatile: ccVolatile,
5859
5884
  stream: false,
5860
5885
  temperature: 0.3,
5861
5886
  maxTokens: modelParams.maxTokens ?? 4096,
@@ -5910,7 +5935,11 @@ Tip: You can continue the conversation by asking the AI to proceed.`
5910
5935
  getCurrentProvider: () => this.currentProvider,
5911
5936
  getCurrentModel: () => this.currentModel,
5912
5937
  getContextLayers: () => [...this.contextLayers],
5913
- getActiveSystemPrompt: () => this.buildCurrentSystemPrompt(),
5938
+ getActiveSystemPrompt: () => {
5939
+ const { stable, volatile } = this.buildCurrentSystemPrompt();
5940
+ const combined = [stable, volatile].filter(Boolean).join("\n\n---\n\n");
5941
+ return combined || void 0;
5942
+ },
5914
5943
  reloadContext: () => {
5915
5944
  const { layers, mergedContent } = this.loadHierarchicalContext();
5916
5945
  this.contextLayers = layers;
@@ -5963,10 +5992,12 @@ Tip: You can continue the conversation by asking the AI to proceed.`
5963
5992
  chatOnce: async (prompt, options) => {
5964
5993
  const provider = this.providers.get(this.currentProvider);
5965
5994
  const modelParams = this.getModelParams();
5995
+ const { stable: coStable, volatile: coVolatile } = this.buildCurrentSystemPrompt();
5966
5996
  const response = await provider.chat({
5967
5997
  messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
5968
5998
  model: this.currentModel,
5969
- systemPrompt: this.buildCurrentSystemPrompt(),
5999
+ systemPrompt: coStable,
6000
+ systemPromptVolatile: coVolatile,
5970
6001
  stream: false,
5971
6002
  temperature: options?.temperature ?? 0.3,
5972
6003
  maxTokens: options?.maxTokens ?? modelParams.maxTokens ?? 4096,
@@ -6108,7 +6139,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6108
6139
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6109
6140
  process.exit(1);
6110
6141
  }
6111
- const { startWebServer } = await import("./server-L5P63T2I.js");
6142
+ const { startWebServer } = await import("./server-WYL3OD5N.js");
6112
6143
  await startWebServer({ port, host: options.host });
6113
6144
  });
6114
6145
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6341,7 +6372,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6341
6372
  }),
6342
6373
  config.get("customProviders")
6343
6374
  );
6344
- const { startHub } = await import("./hub-H3OSDWZW.js");
6375
+ const { startHub } = await import("./hub-BGO4X72R.js");
6345
6376
  await startHub(
6346
6377
  {
6347
6378
  topic: topic ?? "",
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-APJHOYCH.js";
5
+ } from "./chunk-ND3O5NQU.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-IJIU46TF.js";
4
+ } from "./chunk-YJKPARSH.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -21,7 +21,7 @@ import {
21
21
  persistToolRound,
22
22
  rebuildExtraMessages,
23
23
  setupProxy
24
- } from "./chunk-3IZATQV5.js";
24
+ } from "./chunk-3YT2DUUT.js";
25
25
  import {
26
26
  AuthManager
27
27
  } from "./chunk-BYNY5JPB.js";
@@ -42,7 +42,7 @@ import {
42
42
  spawnAgentContext,
43
43
  truncateOutput,
44
44
  undoStack
45
- } from "./chunk-ZKHLLGUU.js";
45
+ } from "./chunk-IVTWWDWZ.js";
46
46
  import "./chunk-4BKXL7SM.js";
47
47
  import {
48
48
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -62,7 +62,7 @@ import {
62
62
  SKILLS_DIR_NAME,
63
63
  VERSION,
64
64
  buildUserIdentityPrompt
65
- } from "./chunk-APJHOYCH.js";
65
+ } from "./chunk-ND3O5NQU.js";
66
66
 
67
67
  // src/web/server.ts
68
68
  import express from "express";
@@ -393,7 +393,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
393
393
  // src/core/system-prompt-builder.ts
394
394
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
395
395
  import { join } from "path";
396
- function buildSystemPrompt(ctx) {
396
+ function buildSystemPromptParts(ctx) {
397
397
  const now = /* @__PURE__ */ new Date();
398
398
  const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
399
399
  const pad = (n) => String(n).padStart(2, "0");
@@ -402,28 +402,25 @@ function buildSystemPrompt(ctx) {
402
402
  const dateTimeInfo = `Current date and time: ${dateStr} ${timeStr}`;
403
403
  const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
404
404
  const shellInfo = process.platform === "win32" ? "The bash tool uses PowerShell (not cmd). Use PowerShell command syntax. Do not use Unix commands (e.g. grep, head, find, wc, date +format, etc.), use PowerShell equivalents instead (e.g. Select-String, Select-Object -First, Get-ChildItem, Measure-Object, Get-Date -Format, etc.)." : `The bash tool uses ${process.env.SHELL || "/bin/bash"}.`;
405
- const envInfo = `OS: ${osName}
405
+ const volatile = `${dateTimeInfo}
406
+ OS: ${osName}
406
407
  ${shellInfo}
407
408
  Working directory: ${process.cwd()}`;
408
- const parts = [dateTimeInfo + "\n" + envInfo];
409
+ const stableParts = [];
409
410
  if (ctx.userProfile) {
410
411
  const identityPrompt = buildUserIdentityPrompt(ctx.userProfile);
411
- if (identityPrompt) {
412
- parts.push(identityPrompt);
413
- }
412
+ if (identityPrompt) stableParts.push(identityPrompt);
414
413
  }
415
- parts.push(AGENTIC_BEHAVIOR_GUIDELINE);
414
+ stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE);
416
415
  if (ctx.configDir) {
417
416
  const memory = loadMemoryContent(ctx.configDir);
418
- if (memory) {
419
- parts.push(`# Persistent Memory
417
+ if (memory) stableParts.push(`# Persistent Memory
420
418
 
421
419
  ${memory.content}`);
422
- }
423
420
  }
424
421
  const devState = loadDevState();
425
422
  if (devState) {
426
- parts.push(
423
+ stableParts.push(
427
424
  `# Development State Handoff
428
425
 
429
426
  The following is a development state snapshot generated by the previous AI model. Please seamlessly continue the user's work based on this.
@@ -432,25 +429,21 @@ Important: If the snapshot mentions key reference files (e.g. templates, format
432
429
  ` + devState
433
430
  );
434
431
  }
435
- if (ctx.activeSystemPrompt) {
436
- parts.push(ctx.activeSystemPrompt);
437
- }
432
+ if (ctx.activeSystemPrompt) stableParts.push(ctx.activeSystemPrompt);
438
433
  if (ctx.extraContextDirs) {
439
434
  for (const { dir, content } of ctx.extraContextDirs) {
440
- parts.push(`# Added Directory Context: ${dir}
435
+ stableParts.push(`# Added Directory Context: ${dir}
441
436
 
442
437
  ${content}`);
443
438
  }
444
439
  }
445
440
  if (ctx.activeSkill) {
446
- parts.push(`# Active Skill: ${ctx.activeSkill.name}
441
+ stableParts.push(`# Active Skill: ${ctx.activeSkill.name}
447
442
 
448
443
  ${ctx.activeSkill.content}`);
449
444
  }
450
- if (ctx.planMode) {
451
- parts.push(PLAN_MODE_SYSTEM_ADDON);
452
- }
453
- return parts.join("\n\n---\n\n");
445
+ if (ctx.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
446
+ return { stable: stableParts.join("\n\n---\n\n"), volatile };
454
447
  }
455
448
  function loadMemoryContent(configDir) {
456
449
  const memoryPath = join(configDir, MEMORY_FILE_NAME);
@@ -785,10 +778,12 @@ var SessionHandler = class _SessionHandler {
785
778
  const ac = new AbortController();
786
779
  this.abortController = ac;
787
780
  try {
781
+ const { stable: simplStable, volatile: simplVolatile } = this.buildSystemPrompt();
788
782
  const stream = provider.chatStream({
789
783
  messages,
790
784
  model: this.currentModel,
791
- systemPrompt: this.buildSystemPrompt(),
785
+ systemPrompt: simplStable,
786
+ systemPromptVolatile: simplVolatile,
792
787
  stream: true,
793
788
  temperature: modelParams.temperature,
794
789
  maxTokens: modelParams.maxTokens,
@@ -830,7 +825,7 @@ var SessionHandler = class _SessionHandler {
830
825
  const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
831
826
  const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
832
827
  const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
833
- const baseSystemPrompt = (this.buildSystemPrompt() ?? "") + TOOL_CALL_REMINDER;
828
+ const { stable: toolStable, volatile: toolVolatile } = this.buildSystemPrompt();
834
829
  const pauseHint = autoPauseInterval > 0 ? `
835
830
  - Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
836
831
  const roundBudgetHint = `
@@ -840,9 +835,10 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
840
835
  - Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
841
836
  - Prioritize the most critical tasks first in case rounds run out.
842
837
  - When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
843
- const systemPrompt = baseSystemPrompt + roundBudgetHint + (mcpBudgetNote ? `
838
+ const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
844
839
 
845
840
  ${mcpBudgetNote}` : "");
841
+ const systemPromptVolatile = toolVolatile;
846
842
  const modelParams = this.getModelParams();
847
843
  const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
848
844
  const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
@@ -929,6 +925,7 @@ Too much tool output accumulated this turn. Your work so far is preserved.
929
925
  messages: apiMessages,
930
926
  model: this.currentModel,
931
927
  systemPrompt,
928
+ systemPromptVolatile,
932
929
  stream: false,
933
930
  temperature: modelParams.temperature,
934
931
  maxTokens: modelParams.maxTokens,
@@ -1028,7 +1025,11 @@ Details: ${errMsg.split("\n")[0]}
1028
1025
  googleSearchContext.configManager = this.config;
1029
1026
  spawnAgentContext.provider = provider;
1030
1027
  spawnAgentContext.model = this.currentModel;
1031
- spawnAgentContext.systemPrompt = systemPrompt;
1028
+ spawnAgentContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
1029
+
1030
+ ---
1031
+
1032
+ ${systemPromptVolatile}` : systemPrompt;
1032
1033
  spawnAgentContext.modelParams = modelParams;
1033
1034
  spawnAgentContext.configManager = this.config;
1034
1035
  ToolExecutor.currentMessageIndex = this.sessions.current?.messages.length ?? 0;
@@ -1106,6 +1107,7 @@ Details: ${errMsg.split("\n")[0]}
1106
1107
  messages: apiMessages,
1107
1108
  model: this.currentModel,
1108
1109
  systemPrompt,
1110
+ systemPromptVolatile,
1109
1111
  stream: false,
1110
1112
  temperature: modelParams.temperature,
1111
1113
  maxTokens: modelParams.maxTokens,
@@ -1946,7 +1948,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1946
1948
  case "test": {
1947
1949
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1948
1950
  try {
1949
- const { executeTests } = await import("./run-tests-5HDSNAAC.js");
1951
+ const { executeTests } = await import("./run-tests-NPRCZYN3.js");
1950
1952
  const argStr = args.join(" ").trim();
1951
1953
  let testArgs = {};
1952
1954
  if (argStr) {
@@ -2500,13 +2502,14 @@ Add .md files to create commands.` });
2500
2502
  buildSystemPrompt() {
2501
2503
  const skillContent = this.skillManager?.getActivePromptContent();
2502
2504
  const activeSkill = skillContent && this.skillManager?.getActive() ? { name: this.skillManager.getActive().meta.name, content: skillContent } : void 0;
2503
- let prompt = buildSystemPrompt({
2505
+ const { stable: baseStable, volatile } = buildSystemPromptParts({
2504
2506
  activeSystemPrompt: this.activeSystemPrompt,
2505
2507
  activeSkill,
2506
2508
  planMode: this.planMode,
2507
2509
  configDir: this.config.getConfigDir(),
2508
2510
  userProfile: this.config.get("userProfile")
2509
2511
  });
2512
+ let stable = baseStable;
2510
2513
  if (this.addedDirs.size > 0) {
2511
2514
  const MAX_DIR_CONTEXT = 4e4;
2512
2515
  let dirContext = "\n\n--- Added Directory Context ---\n";
@@ -2519,9 +2522,9 @@ Add .md files to create commands.` });
2519
2522
  dirContext += this.scanDirTree(dir, 2, 40) + "\n";
2520
2523
  totalLen = dirContext.length;
2521
2524
  }
2522
- prompt += dirContext;
2525
+ stable += dirContext;
2523
2526
  }
2524
- return prompt;
2527
+ return { stable, volatile };
2525
2528
  }
2526
2529
  getModelParams() {
2527
2530
  const allParams = this.config.get("modelParams");
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-ZKHLLGUU.js";
7
+ } from "./chunk-IVTWWDWZ.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-APJHOYCH.js";
11
+ } from "./chunk-ND3O5NQU.js";
12
12
 
13
13
  // src/hub/task-orchestrator.ts
14
14
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.69",
3
+ "version": "0.4.70",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",