jinzd-ai-cli 0.4.114 → 0.4.115

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.
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  AuthManager,
4
+ TOKEN_EXPIRY_MS,
4
5
  __resetLoginAttemptsForTests
5
- } from "./chunk-5UPFMM2A.js";
6
+ } from "./chunk-O7NM4WTS.js";
6
7
  import "./chunk-PDX44BCA.js";
7
8
  export {
8
9
  AuthManager,
10
+ TOKEN_EXPIRY_MS,
9
11
  __resetLoginAttemptsForTests
10
12
  };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-CP6PALA4.js";
4
+ } from "./chunk-UZLNS3QG.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-UF62SHR7.js";
6
+ import "./chunk-OHUHYWBR.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-XXKWSBRC.js";
5
+ } from "./chunk-TJGRPTJS.js";
6
6
  import {
7
7
  AuthError,
8
8
  ProviderError,
@@ -18,7 +18,7 @@ import {
18
18
  MCP_PROTOCOL_VERSION,
19
19
  MCP_TOOL_PREFIX,
20
20
  VERSION
21
- } from "./chunk-UF62SHR7.js";
21
+ } from "./chunk-OHUHYWBR.js";
22
22
  import {
23
23
  redactJson
24
24
  } from "./chunk-7ZJN4KLV.js";
@@ -72,10 +72,20 @@ var ClaudeProvider = class extends BaseProvider {
72
72
  ]
73
73
  };
74
74
  async initialize(apiKey, options) {
75
- this.client = new Anthropic({
75
+ const clientOptions = {
76
76
  apiKey,
77
77
  baseURL: options?.baseUrl
78
- });
78
+ };
79
+ const proxyUrl = options?.proxy;
80
+ try {
81
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
82
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
83
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
84
+ const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
85
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
86
+ } catch {
87
+ }
88
+ this.client = new Anthropic(clientOptions);
79
89
  }
80
90
  /**
81
91
  * 将内部 MessageContentPart[] 格式转换为 Anthropic SDK 期望的 ContentBlockParam[]。
@@ -932,13 +942,20 @@ var OpenAICompatibleProvider = class extends BaseProvider {
932
942
  timeout: this.defaultTimeout
933
943
  };
934
944
  const proxyUrl = options?.proxy;
935
- if (proxyUrl) {
936
- try {
937
- const { ProxyAgent, fetch: undiciFetch } = await import("undici");
938
- const agent = new ProxyAgent({ uri: proxyUrl });
939
- clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
940
- } catch {
941
- }
945
+ try {
946
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
947
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
948
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
949
+ const dispatcher = proxyUrl ? new ProxyAgent({
950
+ uri: proxyUrl,
951
+ bodyTimeout: STREAM_BODY_TIMEOUT,
952
+ headersTimeout: STREAM_HEADERS_TIMEOUT
953
+ }) : new Agent({
954
+ bodyTimeout: STREAM_BODY_TIMEOUT,
955
+ headersTimeout: STREAM_HEADERS_TIMEOUT
956
+ });
957
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
958
+ } catch {
942
959
  }
943
960
  this.client = new OpenAI(clientOptions);
944
961
  }
@@ -1854,6 +1871,40 @@ function peelMetaNarration(content) {
1854
1871
  }
1855
1872
  return out.trim();
1856
1873
  }
1874
+ var META_NARRATION_HARD_MARKERS = [
1875
+ /\[⚠️\s*CONTENT GENERATION MODE\]/,
1876
+ /CONTENT_ONLY_STREAM_REMINDER\b/,
1877
+ /<system-reminder>/i
1878
+ ];
1879
+ var META_NARRATION_HEURISTICS = [
1880
+ /\bthe user (?:is asking me|wants me|is requesting|expects me)\b/i,
1881
+ /\blet me (?:re-?read|re-?consider|reconsider|think about|carefully (?:re-?read|consider))\b/i,
1882
+ /\bI'?m (?:in (?:a )?content-only|in CONTENT-ONLY|currently in)\b/i,
1883
+ /\bI think (?:there might be|I should|I cannot|the (?:user|best)|maybe)\b/i,
1884
+ /\bWait,?\s+let me\b/i,
1885
+ /\bActually,?\s+I\b/i,
1886
+ /\bI need to be honest with the user\b/i,
1887
+ /\bI(?:'m| am) in a special mode\b/i,
1888
+ /\bGiven that I cannot\b/i
1889
+ ];
1890
+ function detectMetaNarration(content) {
1891
+ if (!content) return null;
1892
+ const head = content.slice(0, 2e3);
1893
+ for (const re of META_NARRATION_HARD_MARKERS) {
1894
+ if (re.test(head)) return re.source;
1895
+ }
1896
+ if (/^#{1,3}\s+\S/m.test(head)) return null;
1897
+ let hits = 0;
1898
+ let firstMatch = "";
1899
+ for (const re of META_NARRATION_HEURISTICS) {
1900
+ if (re.test(head)) {
1901
+ hits++;
1902
+ if (!firstMatch) firstMatch = re.source;
1903
+ if (hits >= 2) return `meta-narration:${firstMatch}`;
1904
+ }
1905
+ }
1906
+ return null;
1907
+ }
1857
1908
  function looksLikeDocumentBody(content) {
1858
1909
  if (!content || content.length < 200) return false;
1859
1910
  if (/^#{1,6}\s+\S/m.test(content)) return true;
@@ -4156,6 +4207,7 @@ export {
4156
4207
  buildPhantomCorrectionMessage,
4157
4208
  detectPseudoToolCalls,
4158
4209
  stripPseudoToolCalls,
4210
+ detectMetaNarration,
4159
4211
  looksLikeDocumentBody,
4160
4212
  stripToolCallReminder,
4161
4213
  TEE_FINAL_USER_NUDGE,
@@ -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.114";
9
+ var VERSION = "0.4.115";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/web/auth.ts
4
- import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync } from "fs";
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync, renameSync, unlinkSync } from "fs";
5
5
  import { join } from "path";
6
6
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
7
7
  var USERS_FILE = "users.json";
8
8
  var TOKEN_EXPIRY_HOURS = 24;
9
+ var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
9
10
  var USERS_DIR = "users";
10
11
  var LOGIN_MAX_FAILS = 5;
11
12
  var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
@@ -252,7 +253,17 @@ var AuthManager = class {
252
253
  }
253
254
  saveDB(db) {
254
255
  mkdirSync(this.baseDir, { recursive: true });
255
- writeFileSync(this.usersFile, JSON.stringify(db, null, 2), "utf-8");
256
+ const tmp = `${this.usersFile}.tmp`;
257
+ try {
258
+ writeFileSync(tmp, JSON.stringify(db, null, 2), "utf-8");
259
+ renameSync(tmp, this.usersFile);
260
+ } catch (err) {
261
+ try {
262
+ unlinkSync(tmp);
263
+ } catch {
264
+ }
265
+ throw err;
266
+ }
256
267
  }
257
268
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
258
269
  hashPasswordLegacy(password, salt) {
@@ -278,6 +289,7 @@ var AuthManager = class {
278
289
  };
279
290
 
280
291
  export {
292
+ TOKEN_EXPIRY_MS,
281
293
  __resetLoginAttemptsForTests,
282
294
  AuthManager
283
295
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.114";
4
+ var VERSION = "0.4.115";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-UF62SHR7.js";
4
+ } from "./chunk-OHUHYWBR.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3BICTI5M.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-2WAF7FOX.js";
8
+ } from "./chunk-PEMNYHIS.js";
9
9
  import {
10
10
  EnvLoader,
11
11
  NetworkError,
@@ -18,7 +18,7 @@ import {
18
18
  SUBAGENT_ALLOWED_TOOLS,
19
19
  SUBAGENT_DEFAULT_MAX_ROUNDS,
20
20
  SUBAGENT_MAX_ROUNDS_LIMIT
21
- } from "./chunk-UF62SHR7.js";
21
+ } from "./chunk-OHUHYWBR.js";
22
22
  import {
23
23
  fileCheckpoints
24
24
  } from "./chunk-4BKXL7SM.js";
@@ -372,7 +372,7 @@ Important rules:
372
372
  }
373
373
  updateCwdFromCommand(command, effectiveCwd);
374
374
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
375
- const result = IS_WINDOWS && Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : stdout;
375
+ const result = Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : String(stdout ?? "");
376
376
  return result || "(command completed with no output)";
377
377
  } catch (err) {
378
378
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-UF62SHR7.js";
11
+ } from "./chunk-OHUHYWBR.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-UF62SHR7.js";
39
+ } from "./chunk-OHUHYWBR.js";
40
40
  import "./chunk-PDX44BCA.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-W45U3KQE.js";
39
+ } from "./chunk-KQZU2VS5.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -604,10 +604,20 @@ var ClaudeProvider = class extends BaseProvider {
604
604
  ]
605
605
  };
606
606
  async initialize(apiKey, options) {
607
- this.client = new Anthropic({
607
+ const clientOptions = {
608
608
  apiKey,
609
609
  baseURL: options?.baseUrl
610
- });
610
+ };
611
+ const proxyUrl = options?.proxy;
612
+ try {
613
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
614
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
615
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
616
+ const dispatcher = proxyUrl ? new ProxyAgent({ uri: proxyUrl, bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT }) : new Agent({ bodyTimeout: STREAM_BODY_TIMEOUT, headersTimeout: STREAM_HEADERS_TIMEOUT });
617
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
618
+ } catch {
619
+ }
620
+ this.client = new Anthropic(clientOptions);
611
621
  }
612
622
  /**
613
623
  * 将内部 MessageContentPart[] 格式转换为 Anthropic SDK 期望的 ContentBlockParam[]。
@@ -1464,13 +1474,20 @@ var OpenAICompatibleProvider = class extends BaseProvider {
1464
1474
  timeout: this.defaultTimeout
1465
1475
  };
1466
1476
  const proxyUrl = options?.proxy;
1467
- if (proxyUrl) {
1468
- try {
1469
- const { ProxyAgent, fetch: undiciFetch } = await import("undici");
1470
- const agent = new ProxyAgent({ uri: proxyUrl });
1471
- clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
1472
- } catch {
1473
- }
1477
+ try {
1478
+ const { Agent, ProxyAgent, fetch: undiciFetch } = await import("undici");
1479
+ const STREAM_BODY_TIMEOUT = 30 * 60 * 1e3;
1480
+ const STREAM_HEADERS_TIMEOUT = 5 * 60 * 1e3;
1481
+ const dispatcher = proxyUrl ? new ProxyAgent({
1482
+ uri: proxyUrl,
1483
+ bodyTimeout: STREAM_BODY_TIMEOUT,
1484
+ headersTimeout: STREAM_HEADERS_TIMEOUT
1485
+ }) : new Agent({
1486
+ bodyTimeout: STREAM_BODY_TIMEOUT,
1487
+ headersTimeout: STREAM_HEADERS_TIMEOUT
1488
+ });
1489
+ clientOptions.fetch = ((url, init) => undiciFetch(url, { ...init, dispatcher }));
1490
+ } catch {
1474
1491
  }
1475
1492
  this.client = new OpenAI(clientOptions);
1476
1493
  }
@@ -2268,6 +2285,40 @@ function peelMetaNarration(content) {
2268
2285
  }
2269
2286
  return out.trim();
2270
2287
  }
2288
+ var META_NARRATION_HARD_MARKERS = [
2289
+ /\[⚠️\s*CONTENT GENERATION MODE\]/,
2290
+ /CONTENT_ONLY_STREAM_REMINDER\b/,
2291
+ /<system-reminder>/i
2292
+ ];
2293
+ var META_NARRATION_HEURISTICS = [
2294
+ /\bthe user (?:is asking me|wants me|is requesting|expects me)\b/i,
2295
+ /\blet me (?:re-?read|re-?consider|reconsider|think about|carefully (?:re-?read|consider))\b/i,
2296
+ /\bI'?m (?:in (?:a )?content-only|in CONTENT-ONLY|currently in)\b/i,
2297
+ /\bI think (?:there might be|I should|I cannot|the (?:user|best)|maybe)\b/i,
2298
+ /\bWait,?\s+let me\b/i,
2299
+ /\bActually,?\s+I\b/i,
2300
+ /\bI need to be honest with the user\b/i,
2301
+ /\bI(?:'m| am) in a special mode\b/i,
2302
+ /\bGiven that I cannot\b/i
2303
+ ];
2304
+ function detectMetaNarration(content) {
2305
+ if (!content) return null;
2306
+ const head = content.slice(0, 2e3);
2307
+ for (const re of META_NARRATION_HARD_MARKERS) {
2308
+ if (re.test(head)) return re.source;
2309
+ }
2310
+ if (/^#{1,3}\s+\S/m.test(head)) return null;
2311
+ let hits = 0;
2312
+ let firstMatch = "";
2313
+ for (const re of META_NARRATION_HEURISTICS) {
2314
+ if (re.test(head)) {
2315
+ hits++;
2316
+ if (!firstMatch) firstMatch = re.source;
2317
+ if (hits >= 2) return `meta-narration:${firstMatch}`;
2318
+ }
2319
+ }
2320
+ return null;
2321
+ }
2271
2322
  function looksLikeDocumentBody(content) {
2272
2323
  if (!content || content.length < 200) return false;
2273
2324
  if (/^#{1,6}\s+\S/m.test(content)) return true;
@@ -3885,7 +3936,7 @@ Important rules:
3885
3936
  }
3886
3937
  updateCwdFromCommand(command, effectiveCwd);
3887
3938
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
3888
- const result = IS_WINDOWS && Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : stdout;
3939
+ const result = Buffer.isBuffer(stdout) ? stdout.toString("utf-8") : String(stdout ?? "");
3889
3940
  return result || "(command completed with no output)";
3890
3941
  } catch (err) {
3891
3942
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
@@ -10842,6 +10893,31 @@ ${summaryResult.content}`,
10842
10893
  await new Promise((resolve7, reject) => {
10843
10894
  fileStream.end((err) => err ? reject(err) : resolve7());
10844
10895
  });
10896
+ const metaMatch = detectMetaNarration(fullContent);
10897
+ if (metaMatch) {
10898
+ try {
10899
+ unlinkSync4(saveToFile);
10900
+ } catch {
10901
+ }
10902
+ isError = true;
10903
+ summary = `[save_last_response REJECTED] Your output was internal reasoning / meta-narration (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body (matched: ${metaMatch}). ${saveToFile} was NOT saved.
10904
+
10905
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.`;
10906
+ if (teeUsage) {
10907
+ roundUsage.inputTokens += teeUsage.inputTokens;
10908
+ roundUsage.outputTokens += teeUsage.outputTokens;
10909
+ roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
10910
+ roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
10911
+ }
10912
+ this.send({
10913
+ type: "tool_call_result",
10914
+ callId: call.id,
10915
+ result: summary,
10916
+ isError: true,
10917
+ endTime: Date.now()
10918
+ });
10919
+ return { content: "", summary, isError: true };
10920
+ }
10845
10921
  const pseudoMatch = detectPseudoToolCalls(fullContent);
10846
10922
  if (pseudoMatch) {
10847
10923
  const cleaned = stripPseudoToolCalls(fullContent);
@@ -10879,9 +10955,13 @@ ${summaryResult.content}`,
10879
10955
  } catch {
10880
10956
  }
10881
10957
  }
10958
+ try {
10959
+ unlinkSync4(saveToFile);
10960
+ } catch {
10961
+ }
10882
10962
  isError = true;
10883
10963
  const msg = err instanceof Error ? err.message : String(err);
10884
- summary = `[save_last_response failed] ${msg}`;
10964
+ summary = `[save_last_response failed] streaming was interrupted: ${msg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
10885
10965
  }
10886
10966
  this.send({
10887
10967
  type: "tool_call_result",
@@ -11960,7 +12040,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11960
12040
  case "test": {
11961
12041
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11962
12042
  try {
11963
- const { executeTests } = await import("./run-tests-OB4CWKKX.js");
12043
+ const { executeTests } = await import("./run-tests-7VYL7OVA.js");
11964
12044
  const argStr = args.join(" ").trim();
11965
12045
  let testArgs = {};
11966
12046
  if (argStr) {
@@ -12944,11 +13024,12 @@ async function setupProxy(configProxy) {
12944
13024
  }
12945
13025
 
12946
13026
  // src/web/auth.ts
12947
- import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync } from "fs";
13027
+ import { existsSync as existsSync21, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync10, copyFileSync, renameSync as renameSync2, unlinkSync as unlinkSync5 } from "fs";
12948
13028
  import { join as join14 } from "path";
12949
13029
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
12950
13030
  var USERS_FILE = "users.json";
12951
13031
  var TOKEN_EXPIRY_HOURS = 24;
13032
+ var TOKEN_EXPIRY_MS = TOKEN_EXPIRY_HOURS * 3600 * 1e3;
12952
13033
  var USERS_DIR = "users";
12953
13034
  var LOGIN_MAX_FAILS = 5;
12954
13035
  var LOGIN_LOCKOUT_MS = 15 * 60 * 1e3;
@@ -13192,7 +13273,17 @@ var AuthManager = class {
13192
13273
  }
13193
13274
  saveDB(db) {
13194
13275
  mkdirSync10(this.baseDir, { recursive: true });
13195
- writeFileSync9(this.usersFile, JSON.stringify(db, null, 2), "utf-8");
13276
+ const tmp = `${this.usersFile}.tmp`;
13277
+ try {
13278
+ writeFileSync9(tmp, JSON.stringify(db, null, 2), "utf-8");
13279
+ renameSync2(tmp, this.usersFile);
13280
+ } catch (err) {
13281
+ try {
13282
+ unlinkSync5(tmp);
13283
+ } catch {
13284
+ }
13285
+ throw err;
13286
+ }
13196
13287
  }
13197
13288
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
13198
13289
  hashPasswordLegacy(password, salt) {
@@ -13406,7 +13497,7 @@ async function startWebServer(options = {}) {
13406
13497
  }
13407
13498
  const token = authManager.login(username, password);
13408
13499
  console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
13409
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
13500
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
13410
13501
  res.json({ success: true, username });
13411
13502
  });
13412
13503
  app.post("/api/auth/login", (req, res) => {
@@ -13420,7 +13511,7 @@ async function startWebServer(options = {}) {
13420
13511
  res.status(401).json({ error: "Invalid username or password" });
13421
13512
  return;
13422
13513
  }
13423
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
13514
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
13424
13515
  res.json({ success: true, username });
13425
13516
  });
13426
13517
  app.post("/api/auth/logout", (_req, res) => {
@@ -386,7 +386,7 @@ ${content}`);
386
386
  }
387
387
  }
388
388
  async function runTaskMode(config, providers, configManager, topic) {
389
- const { TaskOrchestrator } = await import("./task-orchestrator-32YQ6HB2.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-24IGVXYP.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  buildWriteRoundReminder,
15
15
  clearDevState,
16
16
  computeCost,
17
+ detectMetaNarration,
17
18
  detectPseudoToolCalls,
18
19
  detectsHallucinatedFileOp,
19
20
  extractWrittenFilePaths,
@@ -31,10 +32,10 @@ import {
31
32
  setupProxy,
32
33
  stripPseudoToolCalls,
33
34
  stripToolCallReminder
34
- } from "./chunk-E24HT62E.js";
35
+ } from "./chunk-FMPWML3F.js";
35
36
  import {
36
37
  ConfigManager
37
- } from "./chunk-CP6PALA4.js";
38
+ } from "./chunk-UZLNS3QG.js";
38
39
  import {
39
40
  ToolExecutor,
40
41
  ToolRegistry,
@@ -53,10 +54,10 @@ import {
53
54
  spawnAgentContext,
54
55
  theme,
55
56
  undoStack
56
- } from "./chunk-XXKWSBRC.js";
57
+ } from "./chunk-TJGRPTJS.js";
57
58
  import "./chunk-3BICTI5M.js";
58
59
  import "./chunk-2DXY7UGF.js";
59
- import "./chunk-2WAF7FOX.js";
60
+ import "./chunk-PEMNYHIS.js";
60
61
  import "./chunk-2ZD3YTVM.js";
61
62
  import {
62
63
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -79,7 +80,7 @@ import {
79
80
  SKILLS_DIR_NAME,
80
81
  VERSION,
81
82
  buildUserIdentityPrompt
82
- } from "./chunk-UF62SHR7.js";
83
+ } from "./chunk-OHUHYWBR.js";
83
84
  import {
84
85
  formatGitContextForPrompt,
85
86
  getGitContext,
@@ -411,6 +412,7 @@ var Renderer = class {
411
412
  buf = "";
412
413
  };
413
414
  let interrupted = false;
415
+ let streamErr = null;
414
416
  try {
415
417
  for await (const chunk of stream) {
416
418
  if (options?.signal?.aborted) {
@@ -435,41 +437,51 @@ var Renderer = class {
435
437
  interrupted = true;
436
438
  if (!inThinking) flushBuf();
437
439
  } else {
438
- throw err;
440
+ streamErr = err;
439
441
  }
440
442
  }
441
443
  if (interrupted) {
442
444
  process.stdout.write(theme.dim(" [interrupted]\n"));
443
445
  }
444
446
  let tokensShown = false;
445
- if (options?.showTokens) {
446
- process.stdout.write("\n");
447
- if (usage) {
448
- const sessionTotal = options.sessionTotal;
449
- const updatedTotal = sessionTotal ? { inputTokens: sessionTotal.inputTokens + usage.inputTokens, outputTokens: sessionTotal.outputTokens + usage.outputTokens } : void 0;
450
- this.renderUsage(usage, updatedTotal);
451
- tokensShown = true;
452
- } else if (fullContent.length > 0) {
453
- const est = Math.ceil(fullContent.length / 2.5);
454
- process.stdout.write(theme.dim(`\u{1F4CA} ~${est.toLocaleString()} output tokens (estimated)
447
+ if (!streamErr) {
448
+ if (options?.showTokens) {
449
+ process.stdout.write("\n");
450
+ if (usage) {
451
+ const sessionTotal = options.sessionTotal;
452
+ const updatedTotal = sessionTotal ? { inputTokens: sessionTotal.inputTokens + usage.inputTokens, outputTokens: sessionTotal.outputTokens + usage.outputTokens } : void 0;
453
+ this.renderUsage(usage, updatedTotal);
454
+ tokensShown = true;
455
+ } else if (fullContent.length > 0) {
456
+ const est = Math.ceil(fullContent.length / 2.5);
457
+ process.stdout.write(theme.dim(`\u{1F4CA} ~${est.toLocaleString()} output tokens (estimated)
455
458
 
456
459
  `));
457
- tokensShown = true;
460
+ tokensShown = true;
461
+ } else {
462
+ process.stdout.write("\n");
463
+ }
458
464
  } else {
459
- process.stdout.write("\n");
465
+ process.stdout.write("\n\n");
460
466
  }
461
467
  } else {
462
- process.stdout.write("\n\n");
468
+ process.stdout.write("\n");
463
469
  }
464
470
  if (fileStream) {
465
- await new Promise((resolve3, reject) => {
466
- fileStream.end((err) => err ? reject(err) : resolve3());
467
- });
468
- const kb = (Buffer.byteLength(fullContent, "utf-8") / 1024).toFixed(1);
469
- process.stdout.write(theme.success(` \u2705 Saved: ${options.saveToFile} (${kb} KB)
471
+ try {
472
+ await new Promise((resolve3) => {
473
+ fileStream.end(() => resolve3());
474
+ });
475
+ } catch {
476
+ }
477
+ if (!streamErr) {
478
+ const kb = (Buffer.byteLength(fullContent, "utf-8") / 1024).toFixed(1);
479
+ process.stdout.write(theme.success(` \u2705 Saved: ${options.saveToFile} (${kb} KB)
470
480
 
471
481
  `));
482
+ }
472
483
  }
484
+ if (streamErr) throw streamErr;
473
485
  return { content: fullContent, usage, tokensShown };
474
486
  }
475
487
  renderResponse(content) {
@@ -1600,7 +1612,7 @@ ${text}
1600
1612
  const { join: join6 } = await import("path");
1601
1613
  const { existsSync: existsSync6 } = await import("fs");
1602
1614
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1603
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-YEBRZDBP.js");
1615
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-Y6LRE5TI.js");
1604
1616
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1605
1617
  const cwd = process.cwd();
1606
1618
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2650,7 +2662,7 @@ ${hint}` : "")
2650
2662
  usage: "/test [command|filter]",
2651
2663
  async execute(args, ctx) {
2652
2664
  try {
2653
- const { executeTests } = await import("./run-tests-7WN5Q7YV.js");
2665
+ const { executeTests } = await import("./run-tests-TWE7TJ4T.js");
2654
2666
  const argStr = args.join(" ").trim();
2655
2667
  let testArgs = {};
2656
2668
  if (argStr) {
@@ -6260,10 +6272,71 @@ ${mcpBudgetNote}` : "");
6260
6272
  _extraMessages: teeExtraMessages
6261
6273
  });
6262
6274
  const teeShowTokens = this.shouldShowTokens();
6263
- const { content: genContent, usage: genUsage, tokensShown: teeTokShown } = await this.renderer.renderStream(
6264
- genStream,
6265
- { saveToFile, showTokens: teeShowTokens, sessionTotal: teeShowTokens ? { ...this.sessionTokenUsage } : void 0, signal: teeAc.signal }
6266
- );
6275
+ let genContent;
6276
+ let genUsage;
6277
+ let teeTokShown = false;
6278
+ try {
6279
+ const teeResult = await this.renderer.renderStream(
6280
+ genStream,
6281
+ { saveToFile, showTokens: teeShowTokens, sessionTotal: teeShowTokens ? { ...this.sessionTokenUsage } : void 0, signal: teeAc.signal }
6282
+ );
6283
+ genContent = teeResult.content;
6284
+ genUsage = teeResult.usage;
6285
+ teeTokShown = teeResult.tokensShown;
6286
+ } catch (teeErr) {
6287
+ try {
6288
+ unlinkSync2(saveToFile);
6289
+ } catch {
6290
+ }
6291
+ const errMsg = teeErr instanceof Error ? teeErr.message : String(teeErr);
6292
+ process.stdout.write(theme.error(
6293
+ `
6294
+ \u2717 tee stream failed: ${errMsg}
6295
+ ${saveToFile} (partial) was deleted. Asking model to retry.
6296
+
6297
+ `
6298
+ ));
6299
+ const errorResults = result.toolCalls.map((tc) => ({
6300
+ callId: tc.id,
6301
+ content: tc.name === "save_last_response" ? `[save_last_response failed] streaming was interrupted: ${errMsg}. ${saveToFile} was NOT saved. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).` : `[skipped: save_last_response failed]`,
6302
+ isError: tc.name === "save_last_response"
6303
+ }));
6304
+ const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
6305
+ const newMsgs3 = provider.buildToolResultMessages(result.toolCalls, errorResults, reasoningContent3);
6306
+ extraMessages.push(...newMsgs3);
6307
+ continue;
6308
+ }
6309
+ const metaMatch = detectMetaNarration(genContent);
6310
+ if (metaMatch) {
6311
+ try {
6312
+ unlinkSync2(saveToFile);
6313
+ } catch {
6314
+ }
6315
+ process.stdout.write(theme.error(
6316
+ `
6317
+ \u2717 Rejected save: response was meta-narration / leaked reasoning, not document body (matched: ${metaMatch})
6318
+ ${saveToFile} was deleted; asking model to retry.
6319
+
6320
+ `
6321
+ ));
6322
+ const errorResults = result.toolCalls.map((tc) => ({
6323
+ callId: tc.id,
6324
+ content: tc.name === "save_last_response" ? `[save_last_response REJECTED] Your output was internal reasoning / meta-narration about the task (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body. ${saveToFile} was NOT saved.
6325
+
6326
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading like "# \u5BA1\u8BA1\u62A5\u544A" / "# Audit Report" and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.` : `[skipped: save_last_response was rejected and other parallel calls are abandoned]`,
6327
+ isError: tc.name === "save_last_response"
6328
+ }));
6329
+ const reasoningContent3 = "reasoningContent" in result ? result.reasoningContent : void 0;
6330
+ const newMsgs3 = provider.buildToolResultMessages(result.toolCalls, errorResults, reasoningContent3);
6331
+ extraMessages.push(...newMsgs3);
6332
+ if (genUsage) {
6333
+ roundUsage.inputTokens += genUsage.inputTokens;
6334
+ roundUsage.outputTokens += genUsage.outputTokens;
6335
+ roundUsage.cacheCreationTokens += genUsage.cacheCreationTokens ?? 0;
6336
+ roundUsage.cacheReadTokens += genUsage.cacheReadTokens ?? 0;
6337
+ }
6338
+ continue;
6339
+ }
6267
6340
  const pseudoMatch = detectPseudoToolCalls(genContent);
6268
6341
  if (pseudoMatch) {
6269
6342
  const cleaned = stripPseudoToolCalls(genContent);
@@ -6882,11 +6955,11 @@ program.command("web").description("Start Web UI server with browser-based chat
6882
6955
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6883
6956
  process.exit(1);
6884
6957
  }
6885
- const { startWebServer } = await import("./server-AKG7HG36.js");
6958
+ const { startWebServer } = await import("./server-RODHACCH.js");
6886
6959
  await startWebServer({ port, host: options.host });
6887
6960
  });
6888
6961
  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) => {
6889
- const { AuthManager } = await import("./auth-VBV7HTLQ.js");
6962
+ const { AuthManager } = await import("./auth-7KK5BOCA.js");
6890
6963
  const config = new ConfigManager();
6891
6964
  const auth = new AuthManager(config.getConfigDir());
6892
6965
  if (!action || action === "list") {
@@ -7018,7 +7091,7 @@ program.command("sessions").description("List recent conversation sessions").act
7018
7091
  });
7019
7092
  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) => {
7020
7093
  try {
7021
- const batch = await import("./batch-Q5NQCXKN.js");
7094
+ const batch = await import("./batch-SHNIUSW2.js");
7022
7095
  switch (action) {
7023
7096
  case "submit":
7024
7097
  if (!arg) {
@@ -7061,7 +7134,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7061
7134
  }
7062
7135
  });
7063
7136
  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) => {
7064
- const { startMcpServer } = await import("./server-NQ5J6FAL.js");
7137
+ const { startMcpServer } = await import("./server-3P5BYK74.js");
7065
7138
  await startMcpServer({
7066
7139
  allowDestructive: !!options.allowDestructive,
7067
7140
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7188,7 +7261,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7188
7261
  }),
7189
7262
  config.get("customProviders")
7190
7263
  );
7191
- const { startHub } = await import("./hub-DGJC2RRF.js");
7264
+ const { startHub } = await import("./hub-E3WMJGYK.js");
7192
7265
  await startHub(
7193
7266
  {
7194
7267
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-W45U3KQE.js";
4
+ } from "./chunk-KQZU2VS5.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-2WAF7FOX.js";
6
- import "./chunk-UF62SHR7.js";
5
+ } from "./chunk-PEMNYHIS.js";
6
+ import "./chunk-OHUHYWBR.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -3,14 +3,14 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-XXKWSBRC.js";
6
+ } from "./chunk-TJGRPTJS.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-2WAF7FOX.js";
9
+ import "./chunk-PEMNYHIS.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-UF62SHR7.js";
13
+ } from "./chunk-OHUHYWBR.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
15
  import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- AuthManager
4
- } from "./chunk-5UPFMM2A.js";
3
+ AuthManager,
4
+ TOKEN_EXPIRY_MS
5
+ } from "./chunk-O7NM4WTS.js";
5
6
  import {
6
7
  CONTENT_ONLY_STREAM_REMINDER,
7
8
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -13,6 +14,7 @@ import {
13
14
  TOOL_CALL_REMINDER,
14
15
  autoTrimSessionIfNeeded,
15
16
  computeCost,
17
+ detectMetaNarration,
16
18
  detectPseudoToolCalls,
17
19
  detectsHallucinatedFileOp,
18
20
  formatCost,
@@ -24,10 +26,10 @@ import {
24
26
  setupProxy,
25
27
  stripPseudoToolCalls,
26
28
  stripToolCallReminder
27
- } from "./chunk-E24HT62E.js";
29
+ } from "./chunk-FMPWML3F.js";
28
30
  import {
29
31
  ConfigManager
30
- } from "./chunk-CP6PALA4.js";
32
+ } from "./chunk-UZLNS3QG.js";
31
33
  import {
32
34
  ToolExecutor,
33
35
  ToolRegistry,
@@ -45,10 +47,10 @@ import {
45
47
  spawnAgentContext,
46
48
  truncateOutput,
47
49
  undoStack
48
- } from "./chunk-XXKWSBRC.js";
50
+ } from "./chunk-TJGRPTJS.js";
49
51
  import "./chunk-3BICTI5M.js";
50
52
  import "./chunk-2DXY7UGF.js";
51
- import "./chunk-2WAF7FOX.js";
53
+ import "./chunk-PEMNYHIS.js";
52
54
  import "./chunk-2ZD3YTVM.js";
53
55
  import {
54
56
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -68,7 +70,7 @@ import {
68
70
  SKILLS_DIR_NAME,
69
71
  VERSION,
70
72
  buildUserIdentityPrompt
71
- } from "./chunk-UF62SHR7.js";
73
+ } from "./chunk-OHUHYWBR.js";
72
74
  import {
73
75
  formatGitContextForPrompt,
74
76
  getGitContext,
@@ -1294,6 +1296,31 @@ ${summaryResult.content}`,
1294
1296
  await new Promise((resolve3, reject) => {
1295
1297
  fileStream.end((err) => err ? reject(err) : resolve3());
1296
1298
  });
1299
+ const metaMatch = detectMetaNarration(fullContent);
1300
+ if (metaMatch) {
1301
+ try {
1302
+ unlinkSync(saveToFile);
1303
+ } catch {
1304
+ }
1305
+ isError = true;
1306
+ summary = `[save_last_response REJECTED] Your output was internal reasoning / meta-narration (e.g. "Let me re-read\u2026", "the user is asking me to\u2026") instead of the requested document body (matched: ${metaMatch}). ${saveToFile} was NOT saved.
1307
+
1308
+ This fresh stream has NO tools. Produce ONLY the document body: start with a markdown heading and write the full content. Do NOT narrate that you will produce the document \u2014 produce it.`;
1309
+ if (teeUsage) {
1310
+ roundUsage.inputTokens += teeUsage.inputTokens;
1311
+ roundUsage.outputTokens += teeUsage.outputTokens;
1312
+ roundUsage.cacheCreationTokens += teeUsage.cacheCreationTokens ?? 0;
1313
+ roundUsage.cacheReadTokens += teeUsage.cacheReadTokens ?? 0;
1314
+ }
1315
+ this.send({
1316
+ type: "tool_call_result",
1317
+ callId: call.id,
1318
+ result: summary,
1319
+ isError: true,
1320
+ endTime: Date.now()
1321
+ });
1322
+ return { content: "", summary, isError: true };
1323
+ }
1297
1324
  const pseudoMatch = detectPseudoToolCalls(fullContent);
1298
1325
  if (pseudoMatch) {
1299
1326
  const cleaned = stripPseudoToolCalls(fullContent);
@@ -1331,9 +1358,13 @@ ${summaryResult.content}`,
1331
1358
  } catch {
1332
1359
  }
1333
1360
  }
1361
+ try {
1362
+ unlinkSync(saveToFile);
1363
+ } catch {
1364
+ }
1334
1365
  isError = true;
1335
1366
  const msg = err instanceof Error ? err.message : String(err);
1336
- summary = `[save_last_response failed] ${msg}`;
1367
+ summary = `[save_last_response failed] streaming was interrupted: ${msg}. ${saveToFile} (partial) was deleted. Retry \u2014 and consider producing a more compact output (split very large reports across multiple save_last_response calls if the previous attempt timed out).`;
1337
1368
  }
1338
1369
  this.send({
1339
1370
  type: "tool_call_result",
@@ -2412,7 +2443,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2412
2443
  case "test": {
2413
2444
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2414
2445
  try {
2415
- const { executeTests } = await import("./run-tests-7WN5Q7YV.js");
2446
+ const { executeTests } = await import("./run-tests-TWE7TJ4T.js");
2416
2447
  const argStr = args.join(" ").trim();
2417
2448
  let testArgs = {};
2418
2449
  if (argStr) {
@@ -3573,7 +3604,7 @@ async function startWebServer(options = {}) {
3573
3604
  }
3574
3605
  const token = authManager.login(username, password);
3575
3606
  console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
3576
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
3607
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
3577
3608
  res.json({ success: true, username });
3578
3609
  });
3579
3610
  app.post("/api/auth/login", (req, res) => {
@@ -3587,7 +3618,7 @@ async function startWebServer(options = {}) {
3587
3618
  res.status(401).json({ error: "Invalid username or password" });
3588
3619
  return;
3589
3620
  }
3590
- res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: 7 * 24 * 3600 * 1e3 });
3621
+ res.cookie("aicli_token", token, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
3591
3622
  res.json({ success: true, username });
3592
3623
  });
3593
3624
  app.post("/api/auth/logout", (_req, res) => {
@@ -4,14 +4,14 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-XXKWSBRC.js";
7
+ } from "./chunk-TJGRPTJS.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-2WAF7FOX.js";
10
+ import "./chunk-PEMNYHIS.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-UF62SHR7.js";
14
+ } from "./chunk-OHUHYWBR.js";
15
15
  import "./chunk-4BKXL7SM.js";
16
16
  import "./chunk-7ZJN4KLV.js";
17
17
  import "./chunk-KHYD3WXE.js";
@@ -845,132 +845,132 @@ button, a, .session-item, .file-tree-row, .template-item, .tool-item, .mcp-serve
845
845
  @media (display-mode: standalone) {
846
846
  .navbar { padding-top: env(safe-area-inset-top, 0px); }
847
847
  }
848
-
849
- /* ── Session Replay (B1) ───────────────────────────── */
850
- .replay-step {
851
- border-left: 3px solid hsl(var(--b3));
852
- padding: 0.5rem 0.6rem;
853
- background: hsl(var(--b1));
854
- border-radius: 0 0.35rem 0.35rem 0;
855
- font-size: 0.85rem;
856
- }
857
- .replay-step.role-user { border-left-color: #3b82f6; }
858
- .replay-step.role-assistant { border-left-color: #10b981; }
859
- .replay-step.role-tool { border-left-color: #f59e0b; }
860
- .replay-step.role-tool.error { border-left-color: #ef4444; }
861
- .replay-step-header {
862
- display: flex;
863
- gap: 0.5rem;
864
- align-items: center;
865
- font-size: 0.72rem;
866
- opacity: 0.75;
867
- margin-bottom: 0.25rem;
868
- }
869
- .replay-step-header .role-tag {
870
- font-weight: 600;
871
- padding: 0 0.35rem;
872
- border-radius: 0.25rem;
873
- background: hsl(var(--b3));
874
- }
875
- .replay-step-body {
876
- white-space: pre-wrap;
877
- word-break: break-word;
878
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
879
- font-size: 0.78rem;
880
- max-height: 18rem;
881
- overflow-y: auto;
882
- }
883
- .replay-step-body.text-body {
884
- font-family: inherit;
885
- font-size: 0.85rem;
886
- }
887
- .replay-tool-block {
888
- margin-top: 0.3rem;
889
- padding: 0.4rem;
890
- background: hsl(var(--b2));
891
- border-radius: 0.3rem;
892
- font-size: 0.78rem;
893
- }
894
- .replay-tool-block .tool-name {
895
- font-weight: 600;
896
- color: #f59e0b;
897
- }
898
- .replay-tool-block pre {
899
- margin: 0.2rem 0 0;
900
- white-space: pre-wrap;
901
- word-break: break-word;
902
- font-size: 0.72rem;
903
- max-height: 12rem;
904
- overflow-y: auto;
905
- }
906
-
907
- /* ── B2 Branch picker (sidebar) ─────────────────────────── */
908
- .branch-item {
909
- display: flex;
910
- align-items: center;
911
- gap: 0.35rem;
912
- padding: 0.35rem 0.5rem;
913
- border-radius: 0.35rem;
914
- cursor: pointer;
915
- border: 1px solid transparent;
916
- transition: background 0.1s, border-color 0.1s;
917
- font-size: 0.78rem;
918
- line-height: 1.25;
919
- position: relative;
920
- }
921
- .branch-item:hover {
922
- background: rgba(128, 128, 128, 0.12);
923
- }
924
- .branch-item.active {
925
- background: rgba(34, 197, 94, 0.12);
926
- border-color: rgba(34, 197, 94, 0.45);
927
- }
928
- .branch-item .branch-marker {
929
- flex-shrink: 0;
930
- width: 0.8rem;
931
- color: rgb(34, 197, 94);
932
- }
933
- .branch-item .branch-title {
934
- flex: 1;
935
- min-width: 0;
936
- overflow: hidden;
937
- text-overflow: ellipsis;
938
- white-space: nowrap;
939
- }
940
- .branch-item .branch-id {
941
- flex-shrink: 0;
942
- opacity: 0.5;
943
- font-family: ui-monospace, SFMono-Regular, monospace;
944
- font-size: 0.7rem;
945
- }
946
- .branch-item .branch-count {
947
- flex-shrink: 0;
948
- opacity: 0.55;
949
- font-size: 0.7rem;
950
- }
951
- .branch-item .branch-actions {
952
- display: none;
953
- gap: 0.15rem;
954
- flex-shrink: 0;
955
- }
956
- .branch-item:hover .branch-actions {
957
- display: flex;
958
- }
959
- .branch-item .branch-actions button {
960
- background: transparent;
961
- border: none;
962
- padding: 0 0.2rem;
963
- font-size: 0.72rem;
964
- cursor: pointer;
965
- opacity: 0.7;
966
- }
967
- .branch-item .branch-actions button:hover {
968
- opacity: 1;
969
- }
970
- .branch-item .branch-indent {
971
- flex-shrink: 0;
972
- color: rgba(128, 128, 128, 0.5);
973
- font-family: ui-monospace, SFMono-Regular, monospace;
974
- font-size: 0.72rem;
975
- white-space: pre;
976
- }
848
+
849
+ /* ── Session Replay (B1) ───────────────────────────── */
850
+ .replay-step {
851
+ border-left: 3px solid hsl(var(--b3));
852
+ padding: 0.5rem 0.6rem;
853
+ background: hsl(var(--b1));
854
+ border-radius: 0 0.35rem 0.35rem 0;
855
+ font-size: 0.85rem;
856
+ }
857
+ .replay-step.role-user { border-left-color: #3b82f6; }
858
+ .replay-step.role-assistant { border-left-color: #10b981; }
859
+ .replay-step.role-tool { border-left-color: #f59e0b; }
860
+ .replay-step.role-tool.error { border-left-color: #ef4444; }
861
+ .replay-step-header {
862
+ display: flex;
863
+ gap: 0.5rem;
864
+ align-items: center;
865
+ font-size: 0.72rem;
866
+ opacity: 0.75;
867
+ margin-bottom: 0.25rem;
868
+ }
869
+ .replay-step-header .role-tag {
870
+ font-weight: 600;
871
+ padding: 0 0.35rem;
872
+ border-radius: 0.25rem;
873
+ background: hsl(var(--b3));
874
+ }
875
+ .replay-step-body {
876
+ white-space: pre-wrap;
877
+ word-break: break-word;
878
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
879
+ font-size: 0.78rem;
880
+ max-height: 18rem;
881
+ overflow-y: auto;
882
+ }
883
+ .replay-step-body.text-body {
884
+ font-family: inherit;
885
+ font-size: 0.85rem;
886
+ }
887
+ .replay-tool-block {
888
+ margin-top: 0.3rem;
889
+ padding: 0.4rem;
890
+ background: hsl(var(--b2));
891
+ border-radius: 0.3rem;
892
+ font-size: 0.78rem;
893
+ }
894
+ .replay-tool-block .tool-name {
895
+ font-weight: 600;
896
+ color: #f59e0b;
897
+ }
898
+ .replay-tool-block pre {
899
+ margin: 0.2rem 0 0;
900
+ white-space: pre-wrap;
901
+ word-break: break-word;
902
+ font-size: 0.72rem;
903
+ max-height: 12rem;
904
+ overflow-y: auto;
905
+ }
906
+
907
+ /* ── B2 Branch picker (sidebar) ─────────────────────────── */
908
+ .branch-item {
909
+ display: flex;
910
+ align-items: center;
911
+ gap: 0.35rem;
912
+ padding: 0.35rem 0.5rem;
913
+ border-radius: 0.35rem;
914
+ cursor: pointer;
915
+ border: 1px solid transparent;
916
+ transition: background 0.1s, border-color 0.1s;
917
+ font-size: 0.78rem;
918
+ line-height: 1.25;
919
+ position: relative;
920
+ }
921
+ .branch-item:hover {
922
+ background: rgba(128, 128, 128, 0.12);
923
+ }
924
+ .branch-item.active {
925
+ background: rgba(34, 197, 94, 0.12);
926
+ border-color: rgba(34, 197, 94, 0.45);
927
+ }
928
+ .branch-item .branch-marker {
929
+ flex-shrink: 0;
930
+ width: 0.8rem;
931
+ color: rgb(34, 197, 94);
932
+ }
933
+ .branch-item .branch-title {
934
+ flex: 1;
935
+ min-width: 0;
936
+ overflow: hidden;
937
+ text-overflow: ellipsis;
938
+ white-space: nowrap;
939
+ }
940
+ .branch-item .branch-id {
941
+ flex-shrink: 0;
942
+ opacity: 0.5;
943
+ font-family: ui-monospace, SFMono-Regular, monospace;
944
+ font-size: 0.7rem;
945
+ }
946
+ .branch-item .branch-count {
947
+ flex-shrink: 0;
948
+ opacity: 0.55;
949
+ font-size: 0.7rem;
950
+ }
951
+ .branch-item .branch-actions {
952
+ display: none;
953
+ gap: 0.15rem;
954
+ flex-shrink: 0;
955
+ }
956
+ .branch-item:hover .branch-actions {
957
+ display: flex;
958
+ }
959
+ .branch-item .branch-actions button {
960
+ background: transparent;
961
+ border: none;
962
+ padding: 0 0.2rem;
963
+ font-size: 0.72rem;
964
+ cursor: pointer;
965
+ opacity: 0.7;
966
+ }
967
+ .branch-item .branch-actions button:hover {
968
+ opacity: 1;
969
+ }
970
+ .branch-item .branch-indent {
971
+ flex-shrink: 0;
972
+ color: rgba(128, 128, 128, 0.5);
973
+ font-family: ui-monospace, SFMono-Regular, monospace;
974
+ font-size: 0.72rem;
975
+ white-space: pre;
976
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.114",
3
+ "version": "0.4.115",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",