lark-remote 0.1.13 → 0.2.1

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.
Files changed (132) hide show
  1. package/CHANGELOG.md +36 -3
  2. package/README.en.md +7 -11
  3. package/README.md +7 -11
  4. package/dist/bridge/final-usage.d.ts +24 -0
  5. package/dist/bridge/final-usage.js +55 -0
  6. package/dist/bridge/index.d.ts +0 -7
  7. package/dist/bridge/index.js +22 -64
  8. package/dist/bridge/queue-manager.js +4 -3
  9. package/dist/card/approval-render.js +39 -94
  10. package/dist/card/bash-renderer.js +12 -21
  11. package/dist/card/card-shared.d.ts +13 -1
  12. package/dist/card/card-shared.js +21 -30
  13. package/dist/card/run-renderer.d.ts +0 -22
  14. package/dist/card/run-renderer.js +206 -153
  15. package/dist/card/run-state.d.ts +29 -1
  16. package/dist/card/run-state.js +172 -84
  17. package/dist/card/text-truncate.d.ts +0 -9
  18. package/dist/card/text-truncate.js +0 -9
  19. package/dist/card/tool-render.d.ts +4 -1
  20. package/dist/card/tool-render.js +76 -32
  21. package/dist/cli.js +2 -2
  22. package/dist/common/fs.d.ts +14 -0
  23. package/dist/common/fs.js +57 -3
  24. package/dist/config/codex-config.js +2 -0
  25. package/dist/config/index.d.ts +0 -5
  26. package/dist/config/index.js +0 -11
  27. package/dist/config/kimi-config.js +1 -0
  28. package/dist/config/opencode-config.js +13 -2
  29. package/dist/config/qr.d.ts +36 -0
  30. package/dist/config/qr.js +124 -0
  31. package/dist/config/wizard.js +69 -27
  32. package/dist/connector/index.d.ts +3 -2
  33. package/dist/connector/index.js +5 -4
  34. package/dist/index.js +15 -42
  35. package/dist/instance-lock.js +7 -0
  36. package/dist/persistence/atomic-write.d.ts +2 -0
  37. package/dist/persistence/atomic-write.js +20 -5
  38. package/dist/persistence/load-json-file.d.ts +1 -2
  39. package/dist/persistence/load-json-file.js +2 -3
  40. package/dist/platform/agent-stopper.d.ts +22 -0
  41. package/dist/platform/agent-stopper.js +19 -0
  42. package/dist/platform/command.d.ts +11 -0
  43. package/dist/platform/command.js +107 -0
  44. package/dist/platform/identity.d.ts +33 -0
  45. package/dist/platform/identity.js +187 -0
  46. package/dist/platform/path.d.ts +24 -0
  47. package/dist/platform/path.js +110 -0
  48. package/dist/platform/select.d.ts +9 -0
  49. package/dist/platform/select.js +12 -0
  50. package/dist/platform/shell.d.ts +51 -0
  51. package/dist/platform/shell.js +75 -0
  52. package/dist/platform/spawn.d.ts +41 -0
  53. package/dist/platform/spawn.js +77 -0
  54. package/dist/platform/terminator-posix.d.ts +38 -0
  55. package/dist/platform/terminator-posix.js +103 -0
  56. package/dist/platform/terminator-win32.d.ts +21 -0
  57. package/dist/platform/terminator-win32.js +110 -0
  58. package/dist/platform/terminator.d.ts +44 -0
  59. package/dist/platform/terminator.js +24 -0
  60. package/dist/platform/types.d.ts +23 -0
  61. package/dist/platform/types.js +5 -0
  62. package/dist/restart.js +4 -2
  63. package/dist/router/card-helpers.d.ts +33 -6
  64. package/dist/router/card-helpers.js +67 -11
  65. package/dist/router/config/common/render.d.ts +1 -1
  66. package/dist/router/config/common/render.js +1 -1
  67. package/dist/router/config-diff.d.ts +4 -0
  68. package/dist/router/config-diff.js +70 -0
  69. package/dist/router/index.d.ts +66 -6
  70. package/dist/router/index.js +451 -316
  71. package/dist/router/utils.d.ts +6 -0
  72. package/dist/router/utils.js +12 -0
  73. package/dist/runner/bash/runner.d.ts +9 -2
  74. package/dist/runner/bash/runner.js +45 -20
  75. package/dist/runner/claude/session.d.ts +4 -1
  76. package/dist/runner/claude/session.js +18 -4
  77. package/dist/runner/codex/app-server/protocol-types.d.ts +35 -1
  78. package/dist/runner/codex/app-server/translator.d.ts +18 -0
  79. package/dist/runner/codex/app-server/translator.js +146 -0
  80. package/dist/runner/common/acp/base-acp-runner.d.ts +116 -0
  81. package/dist/runner/common/acp/base-acp-runner.js +248 -0
  82. package/dist/runner/common/acp/base-translator.d.ts +141 -0
  83. package/dist/runner/common/acp/base-translator.js +271 -0
  84. package/dist/runner/common/acp/protocol-helpers.d.ts +39 -0
  85. package/dist/runner/common/acp/protocol-helpers.js +34 -0
  86. package/dist/runner/common/jsonrpc/transport.d.ts +2 -0
  87. package/dist/runner/common/jsonrpc/transport.js +25 -5
  88. package/dist/runner/common/process-stopper.d.ts +4 -26
  89. package/dist/runner/common/process-stopper.js +4 -76
  90. package/dist/runner/common/runner-utils.d.ts +0 -5
  91. package/dist/runner/common/runner-utils.js +0 -7
  92. package/dist/runner/common/spawning-runner.d.ts +42 -132
  93. package/dist/runner/common/spawning-runner.js +52 -271
  94. package/dist/runner/common/usage.d.ts +2 -0
  95. package/dist/runner/common/usage.js +2 -0
  96. package/dist/runner/index.d.ts +1 -1
  97. package/dist/runner/kimi/acp/runner.d.ts +45 -42
  98. package/dist/runner/kimi/acp/runner.js +140 -293
  99. package/dist/runner/kimi/acp/translator.d.ts +18 -100
  100. package/dist/runner/kimi/acp/translator.js +56 -242
  101. package/dist/runner/opencode/acp/runner.d.ts +22 -46
  102. package/dist/runner/opencode/acp/runner.js +73 -256
  103. package/dist/runner/opencode/acp/translator.d.ts +28 -103
  104. package/dist/runner/opencode/acp/translator.js +51 -236
  105. package/dist/runner/pi/rpc/translator.d.ts +3 -0
  106. package/dist/runner/pi/rpc/translator.js +47 -9
  107. package/dist/runner/probe.d.ts +8 -11
  108. package/dist/runner/probe.js +17 -21
  109. package/dist/runner/registry.d.ts +4 -4
  110. package/dist/runner/registry.js +4 -4
  111. package/dist/runner/types.d.ts +38 -1
  112. package/dist/session/claude/sessions.js +35 -58
  113. package/dist/session/codex/rollout-reader.d.ts +10 -0
  114. package/dist/session/codex/rollout-reader.js +123 -41
  115. package/dist/session/common/constants.d.ts +7 -0
  116. package/dist/session/common/constants.js +9 -0
  117. package/dist/session/common/two-pass.d.ts +22 -0
  118. package/dist/session/common/two-pass.js +70 -0
  119. package/dist/session/dsh/sessions.js +7 -6
  120. package/dist/session/kimi/sessions.d.ts +6 -0
  121. package/dist/session/kimi/sessions.js +50 -73
  122. package/dist/session/opencode/sessions.js +26 -8
  123. package/dist/session/pi/sessions.js +54 -73
  124. package/dist/update/index.d.ts +1 -2
  125. package/dist/update/index.js +1 -2
  126. package/dist/update/install.d.ts +2 -1
  127. package/dist/update/install.js +58 -9
  128. package/dist/update/version-check.d.ts +5 -0
  129. package/dist/update/version-check.js +9 -0
  130. package/package.json +9 -6
  131. package/dist/update/startup-hint.d.ts +0 -5
  132. package/dist/update/startup-hint.js +0 -11
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { collapsibleMarkdownPanel } from './collapsible.js';
8
8
  import { truncateUtf8, truncateMarkdownTables } from './text-truncate.js';
9
+ import { cardButton } from './card-shared.js';
9
10
  /** Unique nonce for card button callbacks (SDK dedup + router validation). */
10
11
  function mkNonce() {
11
12
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
@@ -100,23 +101,13 @@ function renderDecisionButtons(approval, expired, runId) {
100
101
  // 决策按钮逐个作为 body 直接元素返回(CardKit body 纵向堆叠 = 上下排列)。
101
102
  // 不能用 column_set width:auto 横排:手机窄屏下多个按钮挤在一行,按钮文字
102
103
  // 被截断成省略号,用户无法辨认审批动作。
103
- return offered.map((d) => ({
104
- tag: 'button',
105
- text: { tag: 'plain_text', content: BUTTONS[d].label },
106
- type: BUTTONS[d].type,
107
- behaviors: [
108
- {
109
- type: 'callback',
110
- value: {
111
- cmd: 'approval.respond',
112
- decision: d,
113
- requestId: approval.requestId,
114
- runId,
115
- nonce: mkNonce(),
116
- },
117
- },
118
- ],
119
- }));
104
+ return offered.map((d) => cardButton(BUTTONS[d].label, {
105
+ cmd: 'approval.respond',
106
+ decision: d,
107
+ requestId: approval.requestId,
108
+ runId,
109
+ nonce: mkNonce(),
110
+ }, { type: BUTTONS[d].type }));
120
111
  }
121
112
  // =============================================================================
122
113
  // Tool Approval (non-command tools, e.g. ExitPlanMode)
@@ -350,28 +341,14 @@ function renderPermissionsApproval(approval, expired, runId) {
350
341
  tag: 'div',
351
342
  text: { tag: 'lark_md', content: `${icon} ${item.label}` },
352
343
  });
353
- elements.push({
354
- tag: 'button',
355
- text: {
356
- tag: 'plain_text',
357
- content: item.selected ? '取消授予' : '授予',
358
- },
359
- type: item.selected ? 'default' : 'primary',
360
- size: 'small',
361
- behaviors: [
362
- {
363
- type: 'callback',
364
- value: {
365
- cmd: 'approval.toggle',
366
- requestId: approval.requestId,
367
- permId: item.id,
368
- selected: !item.selected,
369
- runId,
370
- nonce: mkNonce(),
371
- },
372
- },
373
- ],
374
- });
344
+ elements.push(cardButton(item.selected ? '取消授予' : '授予', {
345
+ cmd: 'approval.toggle',
346
+ requestId: approval.requestId,
347
+ permId: item.id,
348
+ selected: !item.selected,
349
+ runId,
350
+ nonce: mkNonce(),
351
+ }, { type: item.selected ? 'default' : 'primary', size: 'small' }));
375
352
  }
376
353
  }
377
354
  // Action buttons
@@ -473,46 +450,26 @@ function renderQuestionApproval(approval, expired, runId) {
473
450
  }
474
451
  // 多选问题:有勾选才给提交按钮(单选在协调器内即时提交)。
475
452
  if (q.multiSelect && selected.length > 0) {
476
- elements.push({
477
- tag: 'button',
478
- text: { tag: 'plain_text', content: `✅ 提交答案(已选 ${selected.length} 项)` },
479
- type: 'primary',
480
- behaviors: [
481
- {
482
- type: 'callback',
483
- value: {
484
- cmd: 'approval.answerSubmit',
485
- requestId: approval.requestId,
486
- questionIndex: i,
487
- runId,
488
- nonce: mkNonce(),
489
- },
490
- },
491
- ],
492
- });
453
+ elements.push(cardButton(`✅ 提交答案(已选 ${selected.length} 项)`, {
454
+ cmd: 'approval.answerSubmit',
455
+ requestId: approval.requestId,
456
+ questionIndex: i,
457
+ runId,
458
+ nonce: mkNonce(),
459
+ }, { type: 'primary' }));
493
460
  }
494
461
  }
495
462
  // 提问卡底部统一「跳过回答」:走现有 approval.respond + decision=decline
496
463
  // (coordinator 对 question 允许 decline 作为安全兜底),runner 按协议映射
497
464
  // 跳过语义(Claude deny / Codex 空 answers / Kimi action decline / Pi cancelled)。
498
465
  if (!expired) {
499
- elements.push({
500
- tag: 'button',
501
- text: { tag: 'plain_text', content: '⏭️ 跳过回答' },
502
- type: 'default',
503
- behaviors: [
504
- {
505
- type: 'callback',
506
- value: {
507
- cmd: 'approval.respond',
508
- decision: 'decline',
509
- requestId: approval.requestId,
510
- runId,
511
- nonce: mkNonce(),
512
- },
513
- },
514
- ],
515
- });
466
+ elements.push(cardButton('⏭️ 跳过回答', {
467
+ cmd: 'approval.respond',
468
+ decision: 'decline',
469
+ requestId: approval.requestId,
470
+ runId,
471
+ nonce: mkNonce(),
472
+ }, { type: 'default' }));
516
473
  }
517
474
  return elements;
518
475
  }
@@ -530,27 +487,15 @@ function renderOptionRow(requestId, questionIndex, option, isSelected, icon, run
530
487
  width: 'auto',
531
488
  vertical_align: 'center',
532
489
  elements: [
533
- {
534
- tag: 'button',
535
- text: { tag: 'plain_text', content: isSelected ? '取消选择' : '选择' },
536
- type: isSelected ? 'default' : 'primary',
537
- size: 'small',
538
- // 固定宽度让所有选项行的按钮左对齐(描述长短不影响按钮列)。
539
- width: '100px',
540
- behaviors: [
541
- {
542
- type: 'callback',
543
- value: {
544
- cmd: 'approval.answer',
545
- requestId,
546
- questionIndex,
547
- option: option.label,
548
- runId,
549
- nonce: mkNonce(),
550
- },
551
- },
552
- ],
553
- },
490
+ // 固定宽度让所有选项行的按钮左对齐(描述长短不影响按钮列)。
491
+ cardButton(isSelected ? '取消选择' : '选择', {
492
+ cmd: 'approval.answer',
493
+ requestId,
494
+ questionIndex,
495
+ option: option.label,
496
+ runId,
497
+ nonce: mkNonce(),
498
+ }, { type: isSelected ? 'default' : 'primary', size: 'small', width: '100px' }),
554
499
  ],
555
500
  },
556
501
  {
@@ -78,16 +78,17 @@ function assembleCard(state, elements) {
78
78
  };
79
79
  }
80
80
  /**
81
- * Build elements for the normal path: full output/stderr (each ≤ OUTPUT_MAX_BYTES).
81
+ * Build output/stderr elements shared by normal & degraded paths (W2.3 单源):
82
+ * 除截断预算(OUTPUT_MAX_BYTES / DEGRADED_OUTPUT_BYTES)外逐行同构。
82
83
  */
83
- function buildNormalElements(state) {
84
+ function buildOutputElements(state, maxBytes) {
84
85
  const { terminal, runId, output, stderr } = state;
85
86
  const elements = [buildStatusRow(state)];
86
87
  const truncatedOutput = output
87
- ? truncateUtf8(output, OUTPUT_MAX_BYTES, true, '…(输出已截断,共 ' + output.length + ' 字符)')
88
+ ? truncateUtf8(output, maxBytes, true, '…(输出已截断,共 ' + output.length + ' 字符)')
88
89
  : '';
89
90
  const truncatedStderr = stderr
90
- ? truncateUtf8(stderr, OUTPUT_MAX_BYTES, true, '…(诊断输出已截断,共 ' + stderr.length + ' 字符)')
91
+ ? truncateUtf8(stderr, maxBytes, true, '…(诊断输出已截断,共 ' + stderr.length + ' 字符)')
91
92
  : '';
92
93
  if (truncatedOutput)
93
94
  elements.push(markdownDiv(`\`\`\`\n${truncatedOutput}\n\`\`\``));
@@ -99,28 +100,18 @@ function buildNormalElements(state) {
99
100
  elements.push(...buildExitFooter(state));
100
101
  return elements;
101
102
  }
103
+ /**
104
+ * Build elements for the normal path: full output/stderr (each ≤ OUTPUT_MAX_BYTES).
105
+ */
106
+ function buildNormalElements(state) {
107
+ return buildOutputElements(state, OUTPUT_MAX_BYTES);
108
+ }
102
109
  /**
103
110
  * Build elements for the degraded path: output/stderr truncated to a smaller
104
111
  * budget. Preserves command (status row) and exitCode (footer).
105
112
  */
106
113
  function buildDegradedElements(state) {
107
- const { terminal, runId, output, stderr } = state;
108
- const elements = [buildStatusRow(state)];
109
- const truncatedOutput = output
110
- ? truncateUtf8(output, DEGRADED_OUTPUT_BYTES, true, '…(输出已截断,共 ' + output.length + ' 字符)')
111
- : '';
112
- const truncatedStderr = stderr
113
- ? truncateUtf8(stderr, DEGRADED_OUTPUT_BYTES, true, '…(诊断输出已截断,共 ' + stderr.length + ' 字符)')
114
- : '';
115
- if (truncatedOutput)
116
- elements.push(markdownDiv(`\`\`\`\n${truncatedOutput}\n\`\`\``));
117
- if (truncatedStderr)
118
- elements.push(markdownDiv(`📋 诊断输出:\n\`\`\`\n${truncatedStderr}\n\`\`\``));
119
- if (terminal === 'running') {
120
- elements.push({ tag: 'div', text: { content: '‎', tag: 'lark_md' } }, stopButton(runId));
121
- }
122
- elements.push(...buildExitFooter(state));
123
- return elements;
114
+ return buildOutputElements(state, DEGRADED_OUTPUT_BYTES);
124
115
  }
125
116
  /**
126
117
  * Build elements for the extreme fallback: no output/stderr body, only
@@ -7,9 +7,21 @@ import { type AgentKind } from '../runner/index.js';
7
7
  * wire up the registry (notably unit tests of pure render helpers).
8
8
  */
9
9
  export declare function agentDisplayName(kind: string): string;
10
- export { truncate } from '../common/truncate.js';
11
10
  export declare function terminalToLabel(terminal: string): string;
12
11
  export declare function terminalToColor(terminal: string): string;
12
+ /**
13
+ * Create a CardKit 2.0 button with a callback behavior. Shared constructor
14
+ * for every callback button in the card module (stop/new-session/compact/
15
+ * approval decisions/question options/…) — previously each render site
16
+ * hand-rolled the `{tag, text, type, behaviors}` object and they had drifted
17
+ * (plain_text tag sometimes present, sometimes omitted).
18
+ */
19
+ export declare function cardButton(text: string, value: Record<string, unknown>, opts?: {
20
+ type?: 'primary' | 'default' | 'danger';
21
+ size?: 'small' | 'medium' | 'large';
22
+ /** Fixed width, e.g. '100px' (keeps option-row buttons left-aligned). */
23
+ width?: string;
24
+ }): object;
13
25
  /**
14
26
  * Create a CardKit 2.0 stop button with behaviors callback.
15
27
  */
@@ -25,7 +25,6 @@ export function agentDisplayName(kind) {
25
25
  return 'Kimi';
26
26
  return kind;
27
27
  }
28
- export { truncate } from '../common/truncate.js';
29
28
  const TERMINAL_LABELS = {
30
29
  done: '已完成',
31
30
  error: '出错',
@@ -53,27 +52,34 @@ export function terminalToColor(terminal) {
53
52
  return 'red';
54
53
  }
55
54
  /**
56
- * Create a CardKit 2.0 stop button with behaviors callback.
55
+ * Create a CardKit 2.0 button with a callback behavior. Shared constructor
56
+ * for every callback button in the card module (stop/new-session/compact/
57
+ * approval decisions/question options/…) — previously each render site
58
+ * hand-rolled the `{tag, text, type, behaviors}` object and they had drifted
59
+ * (plain_text tag sometimes present, sometimes omitted).
57
60
  */
58
- export function stopButton(runId) {
61
+ export function cardButton(text, value, opts = {}) {
59
62
  return {
60
63
  tag: 'button',
61
- text: { content: '⏹ 停止' },
62
- type: 'danger',
63
- behaviors: [{ type: 'callback', value: { cmd: 'stop', runId } }],
64
+ text: { tag: 'plain_text', content: text },
65
+ ...(opts.type ? { type: opts.type } : {}),
66
+ ...(opts.size ? { size: opts.size } : {}),
67
+ ...(opts.width ? { width: opts.width } : {}),
68
+ behaviors: [{ type: 'callback', value }],
64
69
  };
65
70
  }
71
+ /**
72
+ * Create a CardKit 2.0 stop button with behaviors callback.
73
+ */
74
+ export function stopButton(runId) {
75
+ return cardButton('⏹ 停止', { cmd: 'stop', runId }, { type: 'danger' });
76
+ }
66
77
  /**
67
78
  * Create a CardKit 2.0 "new session" button with behaviors callback.
68
79
  * Standardized: ✨ 新会话 + primary type
69
80
  */
70
81
  export function newSessionButton() {
71
- return {
72
- tag: 'button',
73
- text: { content: '✨ 新会话' },
74
- type: 'primary',
75
- behaviors: [{ type: 'callback', value: { cmd: 'new-session' } }],
76
- };
82
+ return cardButton('✨ 新会话', { cmd: 'new-session' }, { type: 'primary' });
77
83
  }
78
84
  /**
79
85
  * Create a CardKit 2.0 Compact button for any runCompact-capable runner
@@ -82,12 +88,7 @@ export function newSessionButton() {
82
88
  * runId 解析 agentKind,claude 走 stream-json 内建 /compact)。
83
89
  */
84
90
  export function compactButton(runId) {
85
- return {
86
- tag: 'button',
87
- text: { content: '🗜 Compact' },
88
- type: 'primary',
89
- behaviors: [{ type: 'callback', value: { cmd: 'codex.compact', runId } }],
90
- };
91
+ return cardButton('🗜 Compact', { cmd: 'codex.compact', runId }, { type: 'primary' });
91
92
  }
92
93
  /**
93
94
  * Create a CardKit 2.0 Compact button for resume cards (auto-resume / `/resume <id>`).
@@ -98,19 +99,9 @@ export function compactButton(runId) {
98
99
  * non-default agent.
99
100
  */
100
101
  export function resumeCompactButton(sessionId, agent) {
101
- return {
102
- tag: 'button',
103
- text: { content: '🗜 Compact' },
104
- type: 'primary',
105
- behaviors: [{ type: 'callback', value: { cmd: 'resume.compact', sessionId, agent } }],
106
- };
102
+ return cardButton('🗜 Compact', { cmd: 'resume.compact', sessionId, agent }, { type: 'primary' });
107
103
  }
108
104
  export function resumeUseButton(sessionId, agent) {
109
- return {
110
- tag: 'button',
111
- text: { tag: 'plain_text', content: '🔁 切换到此会话' },
112
- type: 'primary',
113
- behaviors: [{ type: 'callback', value: { cmd: 'resume.use', sessionId, agent } }],
114
- };
105
+ return cardButton('🔁 切换到此会话', { cmd: 'resume.use', sessionId, agent }, { type: 'primary' });
115
106
  }
116
107
  //# sourceMappingURL=card-shared.js.map
@@ -1,8 +1,5 @@
1
1
  import type { RunState, ToolEntry } from './run-state.js';
2
2
  export interface RunCardRenderOptions {
3
- showThinking?: boolean;
4
- showToolUse?: boolean;
5
- showToolResult?: boolean;
6
3
  /**
7
4
  * Agent kind for the run card header title (e.g. "Claude · 思考中").
8
5
  * Defaults to 'claude' when not provided; the bridge passes `config.defaultAgent`.
@@ -42,25 +39,6 @@ type BlockGroup = {
42
39
  };
43
40
  /** 每个块的截断后内容(thinking/plan/text),测量与渲染共用(P1-2 建议项)。 */
44
41
  type GroupContentPrepared = Map<BlockGroup, string>;
45
- /**
46
- * 影子测量:精确复刻 normal 路径(buildChronologicalContent + renderRunCard 的
47
- * 骨架)产物 stringify 后的字节数,避免"先建完整卡再丢弃"的浪费。
48
- *
49
- * 第一性原理:估算应当测量"将要渲染的内容",而非近似它。
50
- * - 骨架(schema/config/header/statusRow/summary/buttons/spacer)用小对象实测
51
- * (measureJson),不依赖固定常数。
52
- * - 块内容经与渲染完全相同的变换后精确测量:truncateUtf8 截断 →
53
- * markdownDiv 的 escapeMarkdown(反斜杠 2×)→ JSON.stringify。因此 CJK
54
- * (3 字节/字符无转义)与 ASCII 高转义(\n/" 膨胀)双向精确,无需转义因子。
55
- * - tool 块直接 measureJson(renderTool(...))——与渲染共用同一函数,无复制。
56
- * - 块结构基于 groupBlocks(与渲染一致,连续 text 合并),保证合并语义一致。
57
- *
58
- * 唯一系统性低估:elements 数组元素间逗号/括号 ~n+1 字节(忽略,4KB 裕度内)。
59
- *
60
- * 注意:估算只是优化路径选择,不替代最终 stringify 兜底。即使估算说"低于阈值"
61
- * 走正常路径,renderRunCard 仍会 stringify 确认 ≤28KB;若估算低估,仍 fallback
62
- * 到 degraded(安全网不丢)。
63
- */
64
42
  /** 导出供测试断言估算精度(估算 ≈ 实际渲染字节,见 tests/anchor/run-card/run-card.test.ts)。 */
65
43
  export declare function estimateCardBytes(state: RunState, options?: RunCardRenderOptions, shared?: {
66
44
  groups?: BlockGroup[];