jinzd-ai-cli 0.4.54 → 0.4.56

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.
@@ -7,7 +7,9 @@ import {
7
7
  SessionManager,
8
8
  SkillManager,
9
9
  TOOL_CALL_REMINDER,
10
+ computeCost,
10
11
  detectsHallucinatedFileOp,
12
+ formatCost,
11
13
  formatGitContextForPrompt,
12
14
  getContentText,
13
15
  getGitContext,
@@ -15,7 +17,7 @@ import {
15
17
  hadPreviousWriteToolCalls,
16
18
  loadDevState,
17
19
  setupProxy
18
- } from "./chunk-6FYFVPVE.js";
20
+ } from "./chunk-2DC5ABAM.js";
19
21
  import {
20
22
  AuthManager
21
23
  } from "./chunk-BYNY5JPB.js";
@@ -34,7 +36,7 @@ import {
34
36
  spawnAgentContext,
35
37
  truncateOutput,
36
38
  undoStack
37
- } from "./chunk-TAR67QTH.js";
39
+ } from "./chunk-FJSEFQ54.js";
38
40
  import "./chunk-4BKXL7SM.js";
39
41
  import {
40
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -54,7 +56,7 @@ import {
54
56
  SKILLS_DIR_NAME,
55
57
  VERSION,
56
58
  buildUserIdentityPrompt
57
- } from "./chunk-NP5KZVP6.js";
59
+ } from "./chunk-7FOGK5TM.js";
58
60
 
59
61
  // src/web/server.ts
60
62
  import express from "express";
@@ -483,12 +485,24 @@ var SessionHandler = class _SessionHandler {
483
485
  currentModel;
484
486
  planMode = false;
485
487
  runtimeThinking = null;
486
- sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
488
+ sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
489
+ /** Accumulate a TokenUsage (with optional cache fields) into sessionTokenUsage. */
490
+ addWebSessionUsage(u) {
491
+ this.sessionTokenUsage.inputTokens += u.inputTokens;
492
+ this.sessionTokenUsage.outputTokens += u.outputTokens;
493
+ this.sessionTokenUsage.cacheCreationTokens += u.cacheCreationTokens ?? 0;
494
+ this.sessionTokenUsage.cacheReadTokens += u.cacheReadTokens ?? 0;
495
+ }
496
+ resetWebSessionUsage() {
497
+ this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
498
+ }
487
499
  abortController = null;
488
500
  userInterjection = null;
489
501
  processing = false;
490
502
  /** Pending ask_user promises */
491
503
  pendingAskUser = /* @__PURE__ */ new Map();
504
+ /** Pending auto-pause promises */
505
+ pendingAutoPause = /* @__PURE__ */ new Map();
492
506
  /** Active system prompt from context files */
493
507
  activeSystemPrompt;
494
508
  /** Directories added via /add-dir */
@@ -547,6 +561,7 @@ var SessionHandler = class _SessionHandler {
547
561
  displayName: p.info.displayName,
548
562
  models: p.info.models.map((m) => ({ id: m.id, name: m.displayName ?? m.id }))
549
563
  }));
564
+ const costUsd = computeCost(this.currentProvider, this.currentModel, this.sessionTokenUsage);
550
565
  this.send({
551
566
  type: "status",
552
567
  provider: this.currentProvider,
@@ -557,6 +572,7 @@ var SessionHandler = class _SessionHandler {
557
572
  planMode: this.planMode,
558
573
  thinkingMode: this.runtimeThinking ?? false,
559
574
  tokenUsage: { ...this.sessionTokenUsage },
575
+ costUsd,
560
576
  providers: providerList
561
577
  });
562
578
  }
@@ -588,6 +604,14 @@ var SessionHandler = class _SessionHandler {
588
604
  }
589
605
  return;
590
606
  }
607
+ case "auto_pause_response": {
608
+ const resolve3 = this.pendingAutoPause.get(msg.requestId);
609
+ if (resolve3) {
610
+ this.pendingAutoPause.delete(msg.requestId);
611
+ resolve3({ action: msg.action, message: msg.message });
612
+ }
613
+ return;
614
+ }
591
615
  case "abort":
592
616
  if (this.abortController) {
593
617
  this.abortController.abort();
@@ -603,6 +627,8 @@ var SessionHandler = class _SessionHandler {
603
627
  if (this.abortController) this.abortController.abort();
604
628
  for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
605
629
  this.pendingAskUser.clear();
630
+ for (const resolve3 of this.pendingAutoPause.values()) resolve3({ action: "stop" });
631
+ this.pendingAutoPause.clear();
606
632
  this.saveIfNeeded();
607
633
  }
608
634
  /** 根据当前模型 context window 更新工具输出截断上限 */
@@ -724,8 +750,7 @@ var SessionHandler = class _SessionHandler {
724
750
  if (chunk.done) {
725
751
  this.send({ type: "response_done", content: fullContent, usage: chunk.usage });
726
752
  if (chunk.usage) {
727
- this.sessionTokenUsage.inputTokens += chunk.usage.inputTokens;
728
- this.sessionTokenUsage.outputTokens += chunk.usage.outputTokens;
753
+ this.addWebSessionUsage(chunk.usage);
729
754
  session.addTokenUsage(chunk.usage);
730
755
  }
731
756
  break;
@@ -749,19 +774,24 @@ var SessionHandler = class _SessionHandler {
749
774
  const apiMessages = [...messages];
750
775
  const extraMessages = [];
751
776
  const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
777
+ const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
778
+ const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
752
779
  const baseSystemPrompt = (this.buildSystemPrompt() ?? "") + TOOL_CALL_REMINDER;
780
+ const pauseHint = autoPauseInterval > 0 ? `
781
+ - Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
753
782
  const roundBudgetHint = `
754
783
 
755
784
  [Tool Round Budget]
756
785
  You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan efficiently:
757
786
  - Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
758
787
  - Prioritize the most critical tasks first in case rounds run out.
759
- - When remaining rounds are low, focus on completing the current task and summarizing.`;
788
+ - When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
760
789
  const systemPrompt = baseSystemPrompt + roundBudgetHint;
761
790
  const modelParams = this.getModelParams();
762
- const roundUsage = { inputTokens: 0, outputTokens: 0 };
791
+ const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
763
792
  const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
764
793
  let consecutiveFreeRounds = 0;
794
+ const roundToolHistory = [];
765
795
  const warnNoteAt = Math.max(10, Math.floor(maxToolRounds * 0.2));
766
796
  const warnLowAt = Math.max(5, Math.floor(maxToolRounds * 0.1));
767
797
  const warnCriticalAt = Math.max(3, Math.floor(maxToolRounds * 0.05));
@@ -829,6 +859,8 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
829
859
  if (result.usage) {
830
860
  roundUsage.inputTokens += result.usage.inputTokens;
831
861
  roundUsage.outputTokens += result.usage.outputTokens;
862
+ roundUsage.cacheCreationTokens += result.usage.cacheCreationTokens ?? 0;
863
+ roundUsage.cacheReadTokens += result.usage.cacheReadTokens ?? 0;
832
864
  }
833
865
  if (result.content && !result.toolCalls) {
834
866
  const hasWriteTools = toolDefs.some((t) => t.name === "write_file" || t.name === "edit_file");
@@ -843,12 +875,15 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
843
875
  }
844
876
  this.send({ type: "response_done", content: result.content, usage: roundUsage });
845
877
  session.addMessage({ role: "assistant", content: result.content, timestamp: /* @__PURE__ */ new Date() });
846
- this.sessionTokenUsage.inputTokens += roundUsage.inputTokens;
847
- this.sessionTokenUsage.outputTokens += roundUsage.outputTokens;
878
+ this.addWebSessionUsage(roundUsage);
848
879
  session.addTokenUsage(roundUsage);
849
880
  return;
850
881
  }
851
882
  if (result.toolCalls && result.toolCalls.length > 0) {
883
+ roundToolHistory.push({
884
+ round: round + 1,
885
+ tools: result.toolCalls.map((tc) => tc.name)
886
+ });
852
887
  googleSearchContext.configManager = this.config;
853
888
  spawnAgentContext.provider = provider;
854
889
  spawnAgentContext.model = this.currentModel;
@@ -876,6 +911,39 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
876
911
  extraMessages.push({ role: "user", content: msg });
877
912
  }
878
913
  }
914
+ const effectiveRound = round + 1;
915
+ const remaining = maxToolRounds - effectiveRound;
916
+ if (autoPauseInterval > 0 && effectiveRound > 0 && effectiveRound % autoPauseInterval === 0 && remaining > 0 && !ac.signal.aborted) {
917
+ const recentHistory = roundToolHistory.slice(-autoPauseInterval);
918
+ const toolCounts = /* @__PURE__ */ new Map();
919
+ for (const rh of recentHistory) {
920
+ for (const t of rh.tools) toolCounts.set(t, (toolCounts.get(t) || 0) + 1);
921
+ }
922
+ const toolSummary = [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).map(([name, count]) => count > 1 ? `${name}\xD7${count}` : name).join(", ");
923
+ const requestId = `pause_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
924
+ const pauseResp = await new Promise((resolve3) => {
925
+ this.pendingAutoPause.set(requestId, resolve3);
926
+ this.send({
927
+ type: "auto_pause_request",
928
+ requestId,
929
+ currentRound: effectiveRound,
930
+ totalRounds: maxToolRounds,
931
+ toolSummary
932
+ });
933
+ });
934
+ if (ac.signal.aborted) break;
935
+ if (pauseResp.action === "stop") {
936
+ this.send({ type: "info", message: `\u23F8 Stopped by user at ${effectiveRound}/${maxToolRounds}` });
937
+ extraMessages.push({
938
+ role: "user",
939
+ content: `The user has stopped the task at round ${effectiveRound}/${maxToolRounds}. Do not call any more tools. Summarize what has been completed and what remains.`
940
+ });
941
+ break;
942
+ } else if (pauseResp.action === "redirect" && pauseResp.message) {
943
+ this.send({ type: "info", message: `\u26A1 Redirect: "${pauseResp.message}"` });
944
+ extraMessages.push({ role: "user", content: pauseResp.message });
945
+ }
946
+ }
879
947
  }
880
948
  try {
881
949
  const summaryExtra = [
@@ -917,8 +985,7 @@ ${summaryResult.content}`,
917
985
  message: `Reached maximum tool call rounds (${maxToolRounds}). You can continue by asking the AI to proceed.`
918
986
  });
919
987
  }
920
- this.sessionTokenUsage.inputTokens += roundUsage.inputTokens;
921
- this.sessionTokenUsage.outputTokens += roundUsage.outputTokens;
988
+ this.addWebSessionUsage(roundUsage);
922
989
  session.addTokenUsage(roundUsage);
923
990
  } catch (err) {
924
991
  if (err.name === "AbortError") {
@@ -1034,7 +1101,7 @@ ${summaryResult.content}`,
1034
1101
  case "clear":
1035
1102
  this.saveIfNeeded();
1036
1103
  this.sessions.createSession(this.currentProvider, this.currentModel);
1037
- this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
1104
+ this.resetWebSessionUsage();
1038
1105
  this.send({ type: "info", message: "Conversation cleared." });
1039
1106
  this.sendStatus();
1040
1107
  this.sendSessionList();
@@ -1068,12 +1135,19 @@ ${summaryResult.content}`,
1068
1135
  }
1069
1136
  case "status": {
1070
1137
  const session = this.sessions.current;
1138
+ const cacheRead = this.sessionTokenUsage.cacheReadTokens;
1139
+ const cacheCreate = this.sessionTokenUsage.cacheCreationTokens;
1140
+ const cost = computeCost(this.currentProvider, this.currentModel, this.sessionTokenUsage);
1141
+ const cacheLine = cacheRead > 0 || cacheCreate > 0 ? `
1142
+ Cache: write=${cacheCreate} read=${cacheRead}` : "";
1143
+ const costLine = cost != null ? `
1144
+ Cost: ${formatCost(cost)}` : "";
1071
1145
  this.send({
1072
1146
  type: "info",
1073
1147
  message: `Provider: ${this.currentProvider}
1074
1148
  Model: ${this.currentModel}
1075
1149
  Session: ${session?.id ?? "none"} (${session?.messages.length ?? 0} messages)
1076
- Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.outputTokens}`
1150
+ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.outputTokens}${cacheLine}${costLine}`
1077
1151
  });
1078
1152
  break;
1079
1153
  }
@@ -1083,7 +1157,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1083
1157
  this.saveIfNeeded();
1084
1158
  const created = this.sessions.createSession(this.currentProvider, this.currentModel);
1085
1159
  this.unsavedSessions.set(created.id, created);
1086
- this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
1160
+ this.resetWebSessionUsage();
1087
1161
  this.send({ type: "info", message: "New session created." });
1088
1162
  this.sendStatus();
1089
1163
  this.sendSessionList();
@@ -1094,7 +1168,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1094
1168
  const cached = cachedExact ?? [...this.unsavedSessions.values()].find((s) => s.id.startsWith(targetId));
1095
1169
  if (cached) {
1096
1170
  this.sessions.setCurrent(cached);
1097
- this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
1171
+ this.resetWebSessionUsage();
1098
1172
  this.send({
1099
1173
  type: "info",
1100
1174
  message: `Loaded session: ${cached.id.slice(0, 8)} "${cached.title ?? ""}" (${cached.messages.length} messages)`
@@ -1108,7 +1182,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1108
1182
  const found = list.find((s) => s.id.startsWith(targetId));
1109
1183
  if (found) {
1110
1184
  this.sessions.loadSession(found.id);
1111
- this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
1185
+ this.resetWebSessionUsage();
1112
1186
  this.send({ type: "info", message: `Loaded session: ${found.id.slice(0, 8)} "${found.title ?? ""}" (${found.messageCount} messages)` });
1113
1187
  this.sendSessionMessages();
1114
1188
  this.sendStatus();
@@ -1116,7 +1190,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1116
1190
  } else {
1117
1191
  const recreated = this.sessions.createSession(this.currentProvider, this.currentModel);
1118
1192
  this.unsavedSessions.set(recreated.id, recreated);
1119
- this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
1193
+ this.resetWebSessionUsage();
1120
1194
  this.send({
1121
1195
  type: "info",
1122
1196
  message: `Previous session (${targetId.slice(0, 8)}) is no longer available \u2014 started a new one.`
@@ -1256,16 +1330,41 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1256
1330
  });
1257
1331
  break;
1258
1332
  case "cost": {
1259
- const total = this.sessionTokenUsage.inputTokens + this.sessionTokenUsage.outputTokens;
1260
- this.send({
1261
- type: "info",
1262
- message: `\u{1F4CA} Token Usage
1263
- Provider: ${this.currentProvider}
1264
- Model: ${this.currentModel}
1265
- Input: ${this.sessionTokenUsage.inputTokens.toLocaleString()}
1266
- Output: ${this.sessionTokenUsage.outputTokens.toLocaleString()}
1267
- Total: ${total.toLocaleString()}`
1268
- });
1333
+ const u = this.sessionTokenUsage;
1334
+ const total = u.inputTokens + u.outputTokens + u.cacheCreationTokens + u.cacheReadTokens;
1335
+ const cost = computeCost(this.currentProvider, this.currentModel, u);
1336
+ let savings = null;
1337
+ if (cost != null && u.cacheReadTokens > 0) {
1338
+ const withoutCache = computeCost(this.currentProvider, this.currentModel, {
1339
+ inputTokens: u.inputTokens + u.cacheReadTokens,
1340
+ outputTokens: u.outputTokens,
1341
+ cacheCreationTokens: u.cacheCreationTokens,
1342
+ cacheReadTokens: 0
1343
+ });
1344
+ if (withoutCache != null) savings = withoutCache - cost;
1345
+ }
1346
+ const lines = [
1347
+ "\u{1F4B0} Session Cost & Token Usage",
1348
+ ` Provider : ${this.currentProvider}`,
1349
+ ` Model : ${this.currentModel}`,
1350
+ ` Input (uncached): ${u.inputTokens.toLocaleString()}`,
1351
+ ` Output : ${u.outputTokens.toLocaleString()}`
1352
+ ];
1353
+ if (u.cacheCreationTokens > 0) lines.push(` Cache write : ${u.cacheCreationTokens.toLocaleString()}`);
1354
+ if (u.cacheReadTokens > 0) {
1355
+ const pct = Math.round(u.cacheReadTokens / (u.cacheReadTokens + u.inputTokens) * 100);
1356
+ lines.push(` Cache read : ${u.cacheReadTokens.toLocaleString()} (${pct}% hit rate)`);
1357
+ }
1358
+ lines.push(` Total tokens : ${total.toLocaleString()}`);
1359
+ if (cost != null) {
1360
+ lines.push(` Cost : ${formatCost(cost)}`);
1361
+ if (savings != null && savings > 0) {
1362
+ lines.push(` Cache savings : -${formatCost(savings)} (vs no cache)`);
1363
+ }
1364
+ } else {
1365
+ lines.push(` Cost : \u2014 (pricing unknown for this model)`);
1366
+ }
1367
+ this.send({ type: "info", message: lines.join("\n") });
1269
1368
  break;
1270
1369
  }
1271
1370
  case "tools":
@@ -1691,7 +1790,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1691
1790
  case "test": {
1692
1791
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1693
1792
  try {
1694
- const { executeTests } = await import("./run-tests-P53FNUJY.js");
1793
+ const { executeTests } = await import("./run-tests-GISOOQZC.js");
1695
1794
  const argStr = args.join(" ").trim();
1696
1795
  let testArgs = {};
1697
1796
  if (argStr) {
@@ -4,11 +4,11 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-TAR67QTH.js";
7
+ } from "./chunk-FJSEFQ54.js";
8
8
  import "./chunk-4BKXL7SM.js";
9
9
  import {
10
10
  SUBAGENT_ALLOWED_TOOLS
11
- } from "./chunk-NP5KZVP6.js";
11
+ } from "./chunk-7FOGK5TM.js";
12
12
 
13
13
  // src/hub/task-orchestrator.ts
14
14
  import { createInterface } from "readline";
@@ -176,6 +176,7 @@ function handleServerMessage(msg) {
176
176
  case 'confirm_request': handleConfirmRequest(msg); break;
177
177
  case 'batch_confirm_request': handleBatchConfirmRequest(msg); break;
178
178
  case 'ask_user_request':handleAskUserRequest(msg); break;
179
+ case 'auto_pause_request': handleAutoPauseRequest(msg); break;
179
180
  case 'thinking_start': handleThinkingStart(); break;
180
181
  case 'thinking_delta': handleThinkingDelta(msg.delta); break;
181
182
  case 'thinking_end': handleThinkingEnd(); break;
@@ -377,6 +378,34 @@ function handleAskUserRequest(msg) {
377
378
  setTimeout(() => document.getElementById(`ask-input-${msg.requestId}`)?.focus(), 100);
378
379
  }
379
380
 
381
+ function handleAutoPauseRequest(msg) {
382
+ const el = document.createElement('div');
383
+ el.className = 'confirm-card tool-border-write my-1';
384
+ el.id = `pause-card-${msg.requestId}`;
385
+ const summary = msg.toolSummary ? escapeHtml(msg.toolSummary) : '(none)';
386
+ el.innerHTML = `
387
+ <div class="flex items-center gap-2 mb-2">
388
+ <span class="badge badge-warning badge-sm">⏸ Auto-pause</span>
389
+ <span class="text-xs opacity-70">Round ${msg.currentRound}/${msg.totalRounds} · ${msg.totalRounds - msg.currentRound} remaining</span>
390
+ </div>
391
+ <div class="text-xs opacity-70 mb-2">Recent tools: ${summary}</div>
392
+ <div class="flex gap-2 w-full mb-2">
393
+ <input type="text" id="pause-input-${msg.requestId}"
394
+ class="input input-sm input-bordered flex-1"
395
+ placeholder="Optional: redirect AI with a new instruction..."
396
+ onkeydown="if(event.key==='Enter')respondAutoPause('${msg.requestId}','redirect')">
397
+ </div>
398
+ <div class="flex gap-2">
399
+ <button class="btn btn-success btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','continue')">▶ Continue</button>
400
+ <button class="btn btn-primary btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','redirect')">↪ Redirect</button>
401
+ <button class="btn btn-error btn-sm flex-1" onclick="respondAutoPause('${msg.requestId}','stop')">⏹ Stop</button>
402
+ </div>
403
+ `;
404
+ messagesEl.appendChild(el);
405
+ scrollToBottom();
406
+ setTimeout(() => document.getElementById(`pause-input-${msg.requestId}`)?.focus(), 100);
407
+ }
408
+
380
409
  function handleRoundProgress(msg) {
381
410
  const progressBar = document.getElementById('round-progress');
382
411
  const progressBarEl = document.getElementById('round-progress-bar');
@@ -496,7 +525,16 @@ function handleStatus(msg) {
496
525
  btnPlan.classList.toggle('btn-active-toggle', msg.planMode);
497
526
  statusSession.textContent = `📋 ${msg.sessionId?.slice(0, 8) || '—'} (${msg.messageCount} msgs)`;
498
527
  if (msg.tokenUsage) {
499
- statusTokens.textContent = `📊 in: ${msg.tokenUsage.inputTokens} out: ${msg.tokenUsage.outputTokens}`;
528
+ const u = msg.tokenUsage;
529
+ const cacheRead = u.cacheReadTokens || 0;
530
+ let line = `📊 in: ${u.inputTokens.toLocaleString()} out: ${u.outputTokens.toLocaleString()}`;
531
+ if (cacheRead > 0) line += ` cache: ${cacheRead.toLocaleString()}`;
532
+ if (msg.costUsd != null) {
533
+ const cost = msg.costUsd;
534
+ const costStr = cost === 0 ? '$0' : cost < 0.01 ? `$${cost.toFixed(4)}` : cost < 1 ? `$${cost.toFixed(3)}` : `$${cost.toFixed(2)}`;
535
+ line += ` 💰 ${costStr}`;
536
+ }
537
+ statusTokens.textContent = line;
500
538
  }
501
539
  sessionListEl.querySelectorAll('.session-item').forEach(el => {
502
540
  el.classList.toggle('active', el.dataset.sessionId === msg.sessionId);
@@ -559,6 +597,30 @@ window.respondAskUser = function(requestId) {
559
597
  }
560
598
  };
561
599
 
600
+ window.respondAutoPause = function(requestId, action) {
601
+ const card = document.getElementById(`pause-card-${requestId}`);
602
+ const input = document.getElementById(`pause-input-${requestId}`);
603
+ const typed = input ? input.value.trim() : '';
604
+
605
+ // 若用户在输入框敲回车,action 是 'redirect';若内容为空则视作 continue
606
+ let effective = action;
607
+ let message;
608
+ if (action === 'redirect') {
609
+ if (!typed) { effective = 'continue'; }
610
+ else { message = typed; }
611
+ }
612
+
613
+ send({ type: 'auto_pause_response', requestId, action: effective, message });
614
+
615
+ if (card) {
616
+ card.className = 'confirm-card tool-border-safe my-1 opacity-60';
617
+ const label = effective === 'continue' ? '▶ Continued'
618
+ : effective === 'stop' ? '⏹ Stopped'
619
+ : `↪ Redirected: ${escapeHtml(message || '')}`;
620
+ card.innerHTML = `<span class="text-sm">${label}</span>`;
621
+ }
622
+ };
623
+
562
624
  // DaisyUI light themes → highlight.js light stylesheet; others → dark
563
625
  const LIGHT_DAISYUI_THEMES = new Set(['light', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'garden', 'lofi', 'pastel', 'fantasy', 'wireframe', 'cmyk', 'autumn', 'acid', 'lemonade', 'winter', 'nord']);
564
626
  const HLJS_CDN = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.54",
3
+ "version": "0.4.56",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",