jinzd-ai-cli 0.4.65 → 0.4.67

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.
@@ -10,7 +10,7 @@ import {
10
10
  SUBAGENT_DEFAULT_MAX_ROUNDS,
11
11
  SUBAGENT_MAX_ROUNDS_LIMIT,
12
12
  runTestsTool
13
- } from "./chunk-AQNAV4H4.js";
13
+ } from "./chunk-F44OKMB2.js";
14
14
 
15
15
  // src/tools/builtin/bash.ts
16
16
  import { execSync } from "child_process";
@@ -793,6 +793,11 @@ import { dirname as dirname2 } from "path";
793
793
  import chalk3 from "chalk";
794
794
  import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
795
795
 
796
+ // src/core/readline-internal.ts
797
+ function rlInternal(rl) {
798
+ return rl;
799
+ }
800
+
796
801
  // src/tools/types.ts
797
802
  function isFileWriteTool(name) {
798
803
  return name === "write_file" || name === "edit_file" || name === "notebook_edit";
@@ -842,6 +847,51 @@ function schemaToJsonSchema(schema) {
842
847
  return result;
843
848
  }
844
849
 
850
+ // src/tools/executor-phases.ts
851
+ function groupCallsByPhase(calls) {
852
+ const safeParallel = [];
853
+ const safeBash = [];
854
+ const fileWriteCalls = [];
855
+ const otherCalls = [];
856
+ for (let i = 0; i < calls.length; i++) {
857
+ const call = calls[i];
858
+ const level = getDangerLevel(call.name, call.arguments);
859
+ if (level === "safe") {
860
+ if (call.name === "bash") {
861
+ safeBash.push({ idx: i, call });
862
+ } else {
863
+ safeParallel.push({ idx: i, call });
864
+ }
865
+ } else if (isFileWriteTool(call.name) && level === "write") {
866
+ fileWriteCalls.push({ idx: i, call });
867
+ } else {
868
+ otherCalls.push({ idx: i, call });
869
+ }
870
+ }
871
+ return { safeParallel, safeBash, fileWriteCalls, otherCalls };
872
+ }
873
+ async function runSafePhases(phase, execute, results, logTag = "executor") {
874
+ const t0 = Date.now();
875
+ const { safeParallel, safeBash } = phase;
876
+ const wrapExec = async (idx, call) => {
877
+ try {
878
+ results[idx] = await execute(call);
879
+ } catch (err) {
880
+ const msg = err instanceof Error ? err.message : String(err);
881
+ console.error(`[${logTag}] Unexpected error in tool "${call.name}":`, err);
882
+ results[idx] = { callId: call.id, content: `Tool execution failed: ${msg}`, isError: true };
883
+ }
884
+ };
885
+ const parallelPhase = safeParallel.length > 0 ? Promise.all(safeParallel.map(({ idx, call }) => wrapExec(idx, call))) : Promise.resolve();
886
+ const bashPhase = (async () => {
887
+ for (const { idx, call } of safeBash) {
888
+ await wrapExec(idx, call);
889
+ }
890
+ })();
891
+ await Promise.all([parallelPhase, bashPhase]);
892
+ return Date.now() - t0;
893
+ }
894
+
845
895
  // src/tools/diff-utils.ts
846
896
  import chalk from "chalk";
847
897
  function renderDiff(oldText, newText, opts = {}) {
@@ -1365,57 +1415,22 @@ var ToolExecutor = class {
1365
1415
  }
1366
1416
  }
1367
1417
  async executeAll(calls) {
1368
- const safeParallel = [];
1369
- const safeBash = [];
1370
- const fileWriteCalls = [];
1371
- const otherCalls = [];
1372
- for (let i = 0; i < calls.length; i++) {
1373
- const call = calls[i];
1374
- const level = getDangerLevel(call.name, call.arguments);
1375
- if (level === "safe") {
1376
- if (call.name === "bash") {
1377
- safeBash.push({ idx: i, call });
1378
- } else {
1379
- safeParallel.push({ idx: i, call });
1380
- }
1381
- } else if (isFileWriteTool(call.name) && level === "write") {
1382
- fileWriteCalls.push({ idx: i, call });
1383
- } else {
1384
- otherCalls.push({ idx: i, call });
1385
- }
1386
- }
1418
+ const phase = groupCallsByPhase(calls);
1387
1419
  const results = new Array(calls.length);
1388
- const t0 = Date.now();
1389
- const wrapExec = async (idx, call) => {
1390
- try {
1391
- results[idx] = await this.execute(call);
1392
- } catch (err) {
1393
- const msg = err instanceof Error ? err.message : String(err);
1394
- console.error(`[executor] Unexpected error in tool "${call.name}":`, err);
1395
- results[idx] = { callId: call.id, content: `Tool execution failed: ${msg}`, isError: true };
1396
- }
1397
- };
1398
- const parallelPhase = safeParallel.length > 0 ? Promise.all(safeParallel.map(({ idx, call }) => wrapExec(idx, call))) : Promise.resolve();
1399
- const bashPhase = (async () => {
1400
- for (const { idx, call } of safeBash) {
1401
- await wrapExec(idx, call);
1402
- }
1403
- })();
1404
- await Promise.all([parallelPhase, bashPhase]);
1405
- if (safeParallel.length >= 2) {
1406
- const elapsed = Date.now() - t0;
1407
- console.log(theme.dim(` \u26A1 ${safeParallel.length} tools executed in parallel (${elapsed}ms)`));
1408
- }
1409
- if (fileWriteCalls.length === 1) {
1410
- const { idx, call } = fileWriteCalls[0];
1420
+ const elapsed = await runSafePhases(phase, (c) => this.execute(c), results, "executor");
1421
+ if (phase.safeParallel.length >= 2) {
1422
+ console.log(theme.dim(` \u26A1 ${phase.safeParallel.length} tools executed in parallel (${elapsed}ms)`));
1423
+ }
1424
+ if (phase.fileWriteCalls.length === 1) {
1425
+ const { idx, call } = phase.fileWriteCalls[0];
1411
1426
  results[idx] = await this.execute(call);
1412
- } else if (fileWriteCalls.length >= 2) {
1413
- const batchResults = await this.executeBatchFileWrites(fileWriteCalls.map((f) => f.call));
1414
- for (let i = 0; i < fileWriteCalls.length; i++) {
1415
- results[fileWriteCalls[i].idx] = batchResults[i];
1427
+ } else if (phase.fileWriteCalls.length >= 2) {
1428
+ const batchResults = await this.executeBatchFileWrites(phase.fileWriteCalls.map((f) => f.call));
1429
+ for (let i = 0; i < phase.fileWriteCalls.length; i++) {
1430
+ results[phase.fileWriteCalls[i].idx] = batchResults[i];
1416
1431
  }
1417
1432
  }
1418
- for (const { idx, call } of otherCalls) {
1433
+ for (const { idx, call } of phase.otherCalls) {
1419
1434
  results[idx] = await this.execute(call);
1420
1435
  }
1421
1436
  return results;
@@ -1450,6 +1465,10 @@ var ToolExecutor = class {
1450
1465
  results[i] = { callId: calls[i].id, content: `[User rejected] The user rejected this ${calls[i].name} operation. Do not retry without asking.`, isError: true };
1451
1466
  }
1452
1467
  }
1468
+ if (approvedIndices.length >= 2) {
1469
+ const labels = approvedIndices.map((i) => `[${i + 1}]`).join(" ");
1470
+ console.log(theme.dim(` \u26A1 Writing ${labels} in parallel (results may interleave)`));
1471
+ }
1453
1472
  const t0 = Date.now();
1454
1473
  await Promise.all(
1455
1474
  approvedIndices.map(async (i) => {
@@ -1488,7 +1507,7 @@ var ToolExecutor = class {
1488
1507
  return Promise.resolve("none");
1489
1508
  }
1490
1509
  const rl = this.rl;
1491
- const rlAny = rl;
1510
+ const rlAny = rlInternal(rl);
1492
1511
  const savedOutput = rlAny.output;
1493
1512
  rlAny.output = process.stdout;
1494
1513
  rl.resume();
@@ -1668,7 +1687,7 @@ var ToolExecutor = class {
1668
1687
  return Promise.resolve(false);
1669
1688
  }
1670
1689
  const rl = this.rl;
1671
- const rlAny = rl;
1690
+ const rlAny = rlInternal(rl);
1672
1691
  const savedOutput = rlAny.output;
1673
1692
  rlAny.output = process.stdout;
1674
1693
  rl.resume();
@@ -2930,7 +2949,7 @@ var askUserTool = {
2930
2949
  }
2931
2950
  };
2932
2951
  function promptUser(rl, question) {
2933
- const rlAny = rl;
2952
+ const rlAny = rlInternal(rl);
2934
2953
  const savedOutput = rlAny.output;
2935
2954
  rlAny.output = process.stdout;
2936
2955
  rl.resume();
@@ -4332,13 +4351,15 @@ export {
4332
4351
  RateLimitError,
4333
4352
  ConfigError,
4334
4353
  ProviderNotFoundError,
4335
- isFileWriteTool,
4336
4354
  getDangerLevel,
4337
4355
  schemaToJsonSchema,
4338
4356
  initTheme,
4339
4357
  theme,
4340
4358
  undoStack,
4341
4359
  renderDiff,
4360
+ rlInternal,
4361
+ groupCallsByPhase,
4362
+ runSafePhases,
4342
4363
  runHook,
4343
4364
  checkPermission,
4344
4365
  setMaxOutputCap,
@@ -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.65";
11
+ var VERSION = "0.4.67";
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.65";
9
+ var VERSION = "0.4.67";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -8,7 +8,7 @@ import {
8
8
  RateLimitError,
9
9
  schemaToJsonSchema,
10
10
  truncateForPersist
11
- } from "./chunk-SKN6HBBT.js";
11
+ } from "./chunk-BVLQ3FRA.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-AQNAV4H4.js";
24
+ } from "./chunk-F44OKMB2.js";
25
25
 
26
26
  // src/config/config-manager.ts
27
27
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -3819,18 +3819,34 @@ function trimOldToolOutput(messages, keepRecentRounds = 10) {
3819
3819
  }
3820
3820
  return trimmed;
3821
3821
  }
3822
+ function cloneMessages(messages) {
3823
+ return messages.map((m) => ({ ...m }));
3824
+ }
3822
3825
  function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
3823
- const json = JSON.stringify(session.toJSON());
3824
- if (json.length <= sizeLimit) return false;
3826
+ const originalSize = JSON.stringify(session.toJSON()).length;
3827
+ if (originalSize <= sizeLimit) return false;
3828
+ const snapshotLen = session.messages.length;
3829
+ let working = cloneMessages(session.messages.slice(0, snapshotLen));
3830
+ let committedSize = originalSize;
3831
+ let anyTrim = false;
3825
3832
  let keepRecent = 10;
3826
3833
  while (keepRecent >= 2) {
3827
- const trimmed = trimOldToolOutput(session.messages, keepRecent);
3828
- if (trimmed === 0) break;
3834
+ const trimmedCount = trimOldToolOutput(working, keepRecent);
3835
+ if (trimmedCount === 0) break;
3836
+ anyTrim = true;
3837
+ const originalMessages = session.messages;
3838
+ session.messages = [...working, ...originalMessages.slice(snapshotLen)];
3829
3839
  const newSize = JSON.stringify(session.toJSON()).length;
3830
- if (newSize <= sizeLimit) return true;
3840
+ session.messages = originalMessages;
3841
+ committedSize = newSize;
3842
+ if (newSize <= sizeLimit) break;
3831
3843
  keepRecent = Math.max(2, Math.floor(keepRecent / 2));
3844
+ if (keepRecent < 2) break;
3832
3845
  }
3833
- return true;
3846
+ if (!anyTrim) return false;
3847
+ const appended = session.messages.slice(snapshotLen);
3848
+ session.messages.splice(0, session.messages.length, ...working, ...appended);
3849
+ return committedSize < originalSize;
3834
3850
  }
3835
3851
 
3836
3852
  // src/repl/dev-state.ts
@@ -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-4DCUYIRI.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-4PVBMQJ7.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-2B2EVU6Z.js";
34
+ } from "./chunk-XSIVGDCN.js";
35
35
  import {
36
36
  ToolExecutor,
37
37
  ToolRegistry,
@@ -41,12 +41,13 @@ import {
41
41
  initTheme,
42
42
  lastResponseStore,
43
43
  renderDiff,
44
+ rlInternal,
44
45
  setContextWindow,
45
46
  setMaxOutputCap,
46
47
  spawnAgentContext,
47
48
  theme,
48
49
  undoStack
49
- } from "./chunk-SKN6HBBT.js";
50
+ } from "./chunk-BVLQ3FRA.js";
50
51
  import {
51
52
  fileCheckpoints
52
53
  } from "./chunk-4BKXL7SM.js";
@@ -71,7 +72,7 @@ import {
71
72
  SKILLS_DIR_NAME,
72
73
  VERSION,
73
74
  buildUserIdentityPrompt
74
- } from "./chunk-AQNAV4H4.js";
75
+ } from "./chunk-F44OKMB2.js";
75
76
 
76
77
  // src/index.ts
77
78
  import { program } from "commander";
@@ -2193,7 +2194,7 @@ ${hint}` : "")
2193
2194
  usage: "/test [command|filter]",
2194
2195
  async execute(args, ctx) {
2195
2196
  try {
2196
- const { executeTests } = await import("./run-tests-C4CEOYZP.js");
2197
+ const { executeTests } = await import("./run-tests-NUF7CNM4.js");
2197
2198
  const argStr = args.join(" ").trim();
2198
2199
  let testArgs = {};
2199
2200
  if (argStr) {
@@ -3273,21 +3274,38 @@ var CostTracker = class {
3273
3274
  if (existsSync4(this.filePath)) {
3274
3275
  const data = JSON.parse(readFileSync3(this.filePath, "utf-8"));
3275
3276
  if (data.version === 1 && Array.isArray(data.records)) {
3276
- this.records = data.records;
3277
+ this.records = [...data.records].sort((a, b) => a.date.localeCompare(b.date));
3277
3278
  }
3278
3279
  }
3279
3280
  } catch {
3280
3281
  this.records = [];
3281
3282
  }
3282
3283
  }
3283
- /** Save to disk (atomic write). */
3284
+ /**
3285
+ * Save to disk (atomic write).
3286
+ *
3287
+ * H2: Snapshot records before writing and only clear the dirty flag after
3288
+ * the write succeeds. If writeFileSync/renameSync throws, dirty remains
3289
+ * true so the next save() retries — prevents silent data loss on transient
3290
+ * disk errors. writeFileSync is sync in Node.js so there's no interleaving
3291
+ * risk within a single save() call; the snapshot mainly protects against
3292
+ * future refactors to async I/O.
3293
+ */
3284
3294
  save() {
3285
3295
  if (!this.dirty) return;
3286
- const data = { version: 1, records: this.records };
3287
- const tmp = this.filePath + ".tmp";
3288
- writeFileSync2(tmp, JSON.stringify(data, null, 2), "utf-8");
3289
- renameSync(tmp, this.filePath);
3290
- this.dirty = false;
3296
+ const snapshot = {
3297
+ version: 1,
3298
+ records: [...this.records]
3299
+ // shallow copy — records are plain data
3300
+ };
3301
+ try {
3302
+ const tmp = this.filePath + ".tmp";
3303
+ writeFileSync2(tmp, JSON.stringify(snapshot, null, 2), "utf-8");
3304
+ renameSync(tmp, this.filePath);
3305
+ this.dirty = false;
3306
+ } catch (err) {
3307
+ console.error("[cost-tracker] Failed to persist cost history:", err);
3308
+ }
3291
3309
  }
3292
3310
  /**
3293
3311
  * Record cost from a completed session/interaction.
@@ -4371,7 +4389,7 @@ Session '${this.resumeSessionId}' not found.
4371
4389
  }
4372
4390
  processing = true;
4373
4391
  this.rl.pause();
4374
- const rlAny = this.rl;
4392
+ const rlAny = rlInternal(this.rl);
4375
4393
  const savedOutput = rlAny.output;
4376
4394
  rlAny.output = null;
4377
4395
  try {
@@ -4386,10 +4404,10 @@ Session '${this.resumeSessionId}' not found.
4386
4404
  processing = false;
4387
4405
  if (this.running) {
4388
4406
  rlAny.output = savedOutput;
4389
- const rlInternal = this.rl;
4390
- rlInternal.line = "";
4391
- rlInternal.cursor = 0;
4392
- rlInternal.paused = false;
4407
+ const rlInternal2 = this.rl;
4408
+ rlInternal2.line = "";
4409
+ rlInternal2.cursor = 0;
4410
+ rlInternal2.paused = false;
4393
4411
  process.stdin.resume();
4394
4412
  this.showPrompt();
4395
4413
  } else {
@@ -5542,7 +5560,7 @@ ${mcpBudgetNote}` : "");
5542
5560
  process.stdout.write(theme.dim(" (Press ") + theme.info("n") + theme.dim(" or ") + theme.info("Esc") + theme.dim(" to stop)\n"));
5543
5561
  this.teardownInterjectionListener();
5544
5562
  const pauseResponse = await new Promise((resolve3) => {
5545
- const rlWithOutput = this.rl;
5563
+ const rlWithOutput = rlInternal(this.rl);
5546
5564
  const savedOutput = rlWithOutput.output;
5547
5565
  rlWithOutput.output = process.stdout;
5548
5566
  this.rl.question(theme.warning(" \u25B8 "), (answer) => {
@@ -5685,9 +5703,9 @@ Tip: You can continue the conversation by asking the AI to proceed.`
5685
5703
  select: (prompt, items, initialIndex) => {
5686
5704
  this.selecting = true;
5687
5705
  return selectFromList(prompt, items, initialIndex).finally(() => {
5688
- const rlInternal = this.rl;
5689
- rlInternal.line = "";
5690
- rlInternal.cursor = 0;
5706
+ const rlInternal2 = this.rl;
5707
+ rlInternal2.line = "";
5708
+ rlInternal2.cursor = 0;
5691
5709
  process.stdin.pause();
5692
5710
  setImmediate(() => {
5693
5711
  this.selecting = false;
@@ -5732,7 +5750,7 @@ Tip: You can continue the conversation by asking the AI to proceed.`
5732
5750
  getGitBranch: () => this.gitBranch,
5733
5751
  getLastResponse: () => lastResponseStore.content,
5734
5752
  runSetupWizard: async () => {
5735
- const rlAny = this.rl;
5753
+ const rlAny = rlInternal(this.rl);
5736
5754
  rlAny.output = process.stdout;
5737
5755
  process.stdin.resume();
5738
5756
  try {
@@ -5905,7 +5923,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5905
5923
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5906
5924
  process.exit(1);
5907
5925
  }
5908
- const { startWebServer } = await import("./server-CDRR3DC7.js");
5926
+ const { startWebServer } = await import("./server-5HLNHRKM.js");
5909
5927
  await startWebServer({ port, host: options.host });
5910
5928
  });
5911
5929
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6138,7 +6156,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6138
6156
  }),
6139
6157
  config.get("customProviders")
6140
6158
  );
6141
- const { startHub } = await import("./hub-TKYPJZEX.js");
6159
+ const { startHub } = await import("./hub-RGK325NQ.js");
6142
6160
  await startHub(
6143
6161
  {
6144
6162
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-UNLZJRCY.js";
4
+ } from "./chunk-X7NVAEFI.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-AQNAV4H4.js";
5
+ } from "./chunk-F44OKMB2.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -21,7 +21,7 @@ import {
21
21
  persistToolRound,
22
22
  rebuildExtraMessages,
23
23
  setupProxy
24
- } from "./chunk-2B2EVU6Z.js";
24
+ } from "./chunk-XSIVGDCN.js";
25
25
  import {
26
26
  AuthManager
27
27
  } from "./chunk-BYNY5JPB.js";
@@ -33,15 +33,16 @@ import {
33
33
  estimateTokens,
34
34
  getDangerLevel,
35
35
  googleSearchContext,
36
- isFileWriteTool,
36
+ groupCallsByPhase,
37
37
  renderDiff,
38
38
  runHook,
39
+ runSafePhases,
39
40
  setContextWindow,
40
41
  setMaxOutputCap,
41
42
  spawnAgentContext,
42
43
  truncateOutput,
43
44
  undoStack
44
- } from "./chunk-SKN6HBBT.js";
45
+ } from "./chunk-BVLQ3FRA.js";
45
46
  import "./chunk-4BKXL7SM.js";
46
47
  import {
47
48
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -61,7 +62,7 @@ import {
61
62
  SKILLS_DIR_NAME,
62
63
  VERSION,
63
64
  buildUserIdentityPrompt
64
- } from "./chunk-AQNAV4H4.js";
65
+ } from "./chunk-F44OKMB2.js";
65
66
 
66
67
  // src/web/server.ts
67
68
  import express from "express";
@@ -325,52 +326,19 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
325
326
  }
326
327
  }
327
328
  async executeAll(calls) {
328
- const safeParallel = [];
329
- const safeBash = [];
330
- const fileWriteCalls = [];
331
- const otherCalls = [];
332
- for (let i = 0; i < calls.length; i++) {
333
- const call = calls[i];
334
- const level = getDangerLevel(call.name, call.arguments);
335
- if (level === "safe") {
336
- if (call.name === "bash") {
337
- safeBash.push({ idx: i, call });
338
- } else {
339
- safeParallel.push({ idx: i, call });
340
- }
341
- } else if (isFileWriteTool(call.name) && level === "write") {
342
- fileWriteCalls.push({ idx: i, call });
343
- } else {
344
- otherCalls.push({ idx: i, call });
345
- }
346
- }
329
+ const phase = groupCallsByPhase(calls);
347
330
  const results = new Array(calls.length);
348
- const wrapExec = async (idx, call) => {
349
- try {
350
- results[idx] = await this.execute(call);
351
- } catch (err) {
352
- const msg = err instanceof Error ? err.message : String(err);
353
- console.error(`[tool-executor-web] Unexpected error in tool "${call.name}":`, err);
354
- results[idx] = { callId: call.id, content: `Tool execution failed: ${msg}`, isError: true };
355
- }
356
- };
357
- const parallelPhase = safeParallel.length > 0 ? Promise.all(safeParallel.map(({ idx, call }) => wrapExec(idx, call))) : Promise.resolve();
358
- const bashPhase = (async () => {
359
- for (const { idx, call } of safeBash) {
360
- await wrapExec(idx, call);
361
- }
362
- })();
363
- await Promise.all([parallelPhase, bashPhase]);
364
- if (fileWriteCalls.length === 1) {
365
- const { idx, call } = fileWriteCalls[0];
331
+ await runSafePhases(phase, (c) => this.execute(c), results, "tool-executor-web");
332
+ if (phase.fileWriteCalls.length === 1) {
333
+ const { idx, call } = phase.fileWriteCalls[0];
366
334
  results[idx] = await this.execute(call);
367
- } else if (fileWriteCalls.length >= 2) {
368
- const batchResult = await this.executeBatchFileWrites(fileWriteCalls);
369
- for (let i = 0; i < fileWriteCalls.length; i++) {
370
- results[fileWriteCalls[i].idx] = batchResult[i];
335
+ } else if (phase.fileWriteCalls.length >= 2) {
336
+ const batchResult = await this.executeBatchFileWrites(phase.fileWriteCalls);
337
+ for (let i = 0; i < phase.fileWriteCalls.length; i++) {
338
+ results[phase.fileWriteCalls[i].idx] = batchResult[i];
371
339
  }
372
340
  }
373
- for (const { idx, call } of otherCalls) {
341
+ for (const { idx, call } of phase.otherCalls) {
374
342
  results[idx] = await this.execute(call);
375
343
  }
376
344
  return results;
@@ -391,6 +359,13 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
391
359
  };
392
360
  }
393
361
  }
362
+ if (approvedIndices.length >= 2) {
363
+ const labels = approvedIndices.map((i) => `[${i + 1}]`).join(" ");
364
+ this.send({
365
+ type: "info",
366
+ message: `\u26A1 Writing ${labels} in parallel (results may interleave)`
367
+ });
368
+ }
394
369
  await Promise.all(
395
370
  approvedIndices.map(async (i) => {
396
371
  const call = calls[i];
@@ -1971,7 +1946,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1971
1946
  case "test": {
1972
1947
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1973
1948
  try {
1974
- const { executeTests } = await import("./run-tests-C4CEOYZP.js");
1949
+ const { executeTests } = await import("./run-tests-NUF7CNM4.js");
1975
1950
  const argStr = args.join(" ").trim();
1976
1951
  let testArgs = {};
1977
1952
  if (argStr) {
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-SKN6HBBT.js";
7
+ } from "./chunk-BVLQ3FRA.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-AQNAV4H4.js";
11
+ } from "./chunk-F44OKMB2.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.65",
3
+ "version": "0.4.67",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",