mocode-ai 1.5.13 → 1.6.0

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.
@@ -172,7 +172,8 @@ export async function spawnAgent(opts) {
172
172
  const childIndex = parentId ? batch.getGroupChildIndex(opts.callId) : undefined;
173
173
  liveBatchId = batch.beginBatch(t('subagent.running'), {
174
174
  parentId,
175
- indent: parentId ? ' ' : undefined,
175
+ // 子批摘要行缩进 = 两层 entry 缩进(随 ENTRY_INDENT 联动,不硬编码空格数)。
176
+ indent: parentId ? batch.SUB_BATCH_INDENT : undefined,
176
177
  groupChildIndex: childIndex,
177
178
  running: true,
178
179
  });
@@ -157,9 +157,7 @@ export async function askJev(request) {
157
157
  inheritPrevious = finiteProbability(inheritAnswer.noul);
158
158
  }
159
159
  const usage = record.usage && typeof record.usage === 'object' ? record.usage : {};
160
- const inputTokens = typeof usage.input_tokens === 'number' && Number.isFinite(usage.input_tokens)
161
- ? usage.input_tokens
162
- : undefined;
160
+ const inputTokens = typeof usage.input_tokens === 'number' && Number.isFinite(usage.input_tokens) ? usage.input_tokens : undefined;
163
161
  return {
164
162
  ok: true,
165
163
  probabilities,
package/dist/ui/batch.js CHANGED
@@ -413,7 +413,7 @@ function buildSummaryLine(record, live = false) {
413
413
  /** 把 batch 的详情行展开成自洽行数组(供 layout.contentInsertAfter 走 mid-buffer 插入)。
414
414
  * 每行末尾必须以 \x1B[0m 收尾(SGR 自洽模型),行内允许含 SGR(行末 reset 不影响行内样式),
415
415
  * 但**绝不**带 \n——rows[] 是行数组,不是流输出。 */
416
- function buildEntryDetailLines(e, indent = ' ') {
416
+ function buildEntryDetailLines(e, indent = DETAIL_INDENT) {
417
417
  const lines = [];
418
418
  if (e.diffBlock) {
419
419
  // diff 块多行文本(由 renderFileChange 渲染);按 \n 拆成物理行,
@@ -478,8 +478,16 @@ const CARET_COLLAPSED = '▸';
478
478
  const CARET_EXPANDED = '▾';
479
479
  /** 无详情可展开时占位,保持与 "▸ " 同宽,让工具名列对齐。 */
480
480
  const CARET_NONE = ' ';
481
- /** 详情行缩进:统一 7 空格(对齐 entry 行的 " ▸ " 之后再右移一列,形成层次)。 */
482
- const DETAIL_INDENT = ' ';
481
+ /** 第一层(工具调用行)相对摘要行的前导缩进:2 空格。
482
+ * 曾为 4 空格——摘要行(`◆ 探索 …`)与工具调用行之间留白过大,工具名被推得过右(用户实测反馈);
483
+ * 子批再叠 8 空格后层级被拉散。收成 2 空格后每层固定递进 2 列,层级仍清晰但不空旷。 */
484
+ const ENTRY_INDENT = ' ';
485
+ /** 子 agent 批摘要行的缩进(= 两层 entry 缩进)。
486
+ * 子批摘要挂在父批的 entry 行之下,是第二层;硬编码会与 ENTRY_INDENT 脱钩,
487
+ * 故由 ENTRY_INDENT 推得。 */
488
+ export const SUB_BATCH_INDENT = ENTRY_INDENT + ENTRY_INDENT;
489
+ /** 详情行缩进:5 空格(对齐 entry 行的 " ▸ " 之后再右移一列,形成层次)。 */
490
+ const DETAIL_INDENT = ' ';
483
491
  /**
484
492
  * 详情行结果标记:·(U+00B7 MIDDLE DOT, Latin-1 Supplement, **EAW = Narrow**)。
485
493
  * 比 ▸/▾ 更稳——Narrow 是 Unicode 层面的硬保证,不依赖字体对 Ambiguous 的取舍。
@@ -504,7 +512,7 @@ function buildEntryLine(b, index, extraIndent = '', cols = maxCols) {
504
512
  const result = e.resultSummary ? ` ${ui.gray}↳ ${e.resultSummary}${ui.reset}` : '';
505
513
  const caret = entryCaret(e, b.expandedEntries.has(index));
506
514
  const failure = e.failed ? `${ui.red}×${ui.reset} ` : '';
507
- return sanitizeRow(`${extraIndent} ${caret}${failure}${ui.accent}${e.name}${ui.reset} ${ui.dim}${e.callSummary}${ui.reset}${result}`, cols);
515
+ return sanitizeRow(`${extraIndent}${ENTRY_INDENT}${caret}${failure}${ui.accent}${e.name}${ui.reset} ${ui.dim}${e.callSummary}${ui.reset}${result}`, cols);
508
516
  }
509
517
  /** 第一层只展示有哪些调用及其简短结果,不展开完整输出。extraIndent 供子批嵌套加深缩进。
510
518
  * fromIndex:运行态追加渲染时只产出 entries[fromIndex, ...) 的行(已渲染的不能重复输出)。 */
@@ -515,9 +523,11 @@ export function buildExpandedLines(b, extraIndent = '', fromIndex = 0, cols = ma
515
523
  out.push(buildEntryLine(b, i, extraIndent, cols));
516
524
  return out;
517
525
  }
518
- /** 详情行缩进(现在与 index 无关,统一常量;保留签名以免调用点全改)。 */
519
- function entryDetailIndent(_entries, _index) {
520
- return DETAIL_INDENT;
526
+ /** 详情行缩进(随批层级联动):批量缩进 + 5 空格。
527
+ * 5 = ENTRY_INDENT(2) + caret 宽(2) + 1,即相对 entry 行的工具名右移一列形成层次。
528
+ * 必须跟随 b.indent——否则子批(挂在父批下、entry 行更深)的详情行会跑回父层左侧(错位)。 */
529
+ function entryDetailIndent(indent) {
530
+ return indent + DETAIL_INDENT;
521
531
  }
522
532
  /** 在 batch 收尾时(onToolBatchEnd):写摘要行 + 登记 summaryAbsIdx;若已展开(回放场景)立即插详情。 */
523
533
  export function endBatch(id, layout) {
@@ -684,7 +694,7 @@ export function refreshBatchExpanded(id, layout) {
684
694
  let anchor = b.summaryAbsIdx + b.renderedCount;
685
695
  for (const j of b.expandedEntries) {
686
696
  if (j < b.renderedCount) {
687
- anchor += buildEntryDetailLines(b.entries[j], entryDetailIndent(b.entries, j)).length;
697
+ anchor += buildEntryDetailLines(b.entries[j], entryDetailIndent(b.indent ?? '')).length;
688
698
  }
689
699
  }
690
700
  if (b.groupParent) {
@@ -694,8 +704,7 @@ export function refreshBatchExpanded(id, layout) {
694
704
  continue;
695
705
  // entry 行自身(含其二层明细)的块末位置
696
706
  const end = b.expandedEntries.has(target.entryIndex)
697
- ? idx +
698
- buildEntryDetailLines(b.entries[target.entryIndex], entryDetailIndent(b.entries, target.entryIndex)).length
707
+ ? idx + buildEntryDetailLines(b.entries[target.entryIndex], entryDetailIndent(b.indent ?? '')).length
699
708
  : idx;
700
709
  if (end > maxIdx)
701
710
  maxIdx = end;
@@ -706,7 +715,7 @@ export function refreshBatchExpanded(id, layout) {
706
715
  if (expandedBatches.has(child.id)) {
707
716
  childEnd += child.renderedCount;
708
717
  for (const j of child.expandedEntries) {
709
- childEnd += buildEntryDetailLines(child.entries[j], entryDetailIndent(child.entries, j)).length;
718
+ childEnd += buildEntryDetailLines(child.entries[j], entryDetailIndent(child.indent ?? '')).length;
710
719
  }
711
720
  }
712
721
  if (childEnd > maxIdx)
@@ -780,7 +789,7 @@ export function expandSingleEntryFully(id, layout) {
780
789
  const b = batches.get(id);
781
790
  if (!b || b.entries.length !== 1 || expandedBatches.has(id))
782
791
  return;
783
- const lines = [...buildExpandedLines(b), ...buildEntryDetailLines(b.entries[0], entryDetailIndent(b.entries, 0))];
792
+ const lines = [...buildExpandedLines(b), ...buildEntryDetailLines(b.entries[0], entryDetailIndent(b.indent ?? ''))];
784
793
  // keepViewport=false:这是 agent 产出后的**自动**展开,属于「新内容」而非用户点击的回看式展开,
785
794
  // 视口必须跟随屏底(同其它工具输出 / 子 agent 实时嵌套)。传 true 会让视口锚定在摘要行、
786
795
  // 把 scrollOffset 顶到插入行数(diff 常几百行),此后 contentWriteMd 见 offset>0 就只喂缓冲
@@ -804,7 +813,7 @@ function collapse(b, layout) {
804
813
  // entries 可能多于已渲染行,按 entries.length 会多删并行批量子 agent 的行。
805
814
  let lineCount = b.renderedCount;
806
815
  for (const i of b.expandedEntries) {
807
- lineCount += buildEntryDetailLines(b.entries[i], entryDetailIndent(b.entries, i)).length;
816
+ lineCount += buildEntryDetailLines(b.entries[i], entryDetailIndent(b.indent ?? '')).length;
808
817
  }
809
818
  // 组容器批折叠时:一并移除嵌套子批的摘要行(及其已展开详情),
810
819
  // 否则父批再次展开后子批摘要仍残留在明细区,出现重复行。
@@ -824,7 +833,7 @@ function collapse(b, layout) {
824
833
  // 否则删少了会留下孤儿明细行。
825
834
  lineCount += child.renderedCount;
826
835
  for (const j of child.expandedEntries) {
827
- lineCount += buildEntryDetailLines(child.entries[j], entryDetailIndent(child.entries, j)).length;
836
+ lineCount += buildEntryDetailLines(child.entries[j], entryDetailIndent(child.indent ?? '')).length;
828
837
  }
829
838
  child.expandedEntries.clear();
830
839
  expandedBatches.delete(child.id);
@@ -863,7 +872,7 @@ export function toggleEntry(batchId, entryIndex, layout) {
863
872
  // 子批自己的摘要行(● 子 Agent 完成 ...)来控制。
864
873
  if (b.groupParent) {
865
874
  // 如果该 sub-agent entry 没有可展开的详情,fallback 到 toggle 子批工具列表。
866
- const details = buildEntryDetailLines(b.entries[entryIndex], entryDetailIndent(b.entries, entryIndex));
875
+ const details = buildEntryDetailLines(b.entries[entryIndex], entryDetailIndent(b.indent ?? ''));
867
876
  if (details.length === 0) {
868
877
  const child = [...batches.values()].find((x) => x.parentId === batchId && x.groupChildIndex === entryIndex);
869
878
  if (child) {
@@ -879,7 +888,7 @@ export function toggleEntry(batchId, entryIndex, layout) {
879
888
  }
880
889
  if (headerIdx < 0)
881
890
  return;
882
- const details = buildEntryDetailLines(b.entries[entryIndex], entryDetailIndent(b.entries, entryIndex));
891
+ const details = buildEntryDetailLines(b.entries[entryIndex], entryDetailIndent(b.indent ?? ''));
883
892
  if (details.length === 0)
884
893
  return;
885
894
  const wasExpanded = b.expandedEntries.has(entryIndex);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocode-ai",
3
- "version": "1.5.13",
3
+ "version": "1.6.0",
4
4
  "description": "终端编码 agent:LLM + tool-call 循环 + 流式输出(含思考)+ 25 个工具,接任意 OpenAI 兼容后端。",
5
5
  "type": "module",
6
6
  "bin": {