lark-remote 0.1.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.
Files changed (201) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +162 -0
  4. package/README.md +161 -0
  5. package/dist/binder.d.ts +57 -0
  6. package/dist/binder.js +105 -0
  7. package/dist/bridge/index.d.ts +338 -0
  8. package/dist/bridge/index.js +1281 -0
  9. package/dist/bridge/queue-manager.d.ts +277 -0
  10. package/dist/bridge/queue-manager.js +729 -0
  11. package/dist/card/bash-card-session.d.ts +78 -0
  12. package/dist/card/bash-card-session.js +174 -0
  13. package/dist/card/bash-renderer.d.ts +24 -0
  14. package/dist/card/bash-renderer.js +187 -0
  15. package/dist/card/card-budget.d.ts +31 -0
  16. package/dist/card/card-budget.js +280 -0
  17. package/dist/card/card-session.d.ts +83 -0
  18. package/dist/card/card-session.js +185 -0
  19. package/dist/card/card-shared.d.ts +28 -0
  20. package/dist/card/card-shared.js +99 -0
  21. package/dist/card/collapsible.d.ts +26 -0
  22. package/dist/card/collapsible.js +87 -0
  23. package/dist/card/run-card-session.d.ts +78 -0
  24. package/dist/card/run-card-session.js +165 -0
  25. package/dist/card/run-renderer.d.ts +66 -0
  26. package/dist/card/run-renderer.js +841 -0
  27. package/dist/card/run-state.d.ts +103 -0
  28. package/dist/card/run-state.js +313 -0
  29. package/dist/card/text-truncate.d.ts +56 -0
  30. package/dist/card/text-truncate.js +184 -0
  31. package/dist/card/time.d.ts +1 -0
  32. package/dist/card/time.js +14 -0
  33. package/dist/card/tool-render.d.ts +25 -0
  34. package/dist/card/tool-render.js +176 -0
  35. package/dist/cli.d.ts +28 -0
  36. package/dist/cli.js +89 -0
  37. package/dist/common/guards.d.ts +37 -0
  38. package/dist/common/guards.js +65 -0
  39. package/dist/config/codex-config.d.ts +113 -0
  40. package/dist/config/codex-config.js +428 -0
  41. package/dist/config/dir.d.ts +17 -0
  42. package/dist/config/dir.js +81 -0
  43. package/dist/config/index.d.ts +219 -0
  44. package/dist/config/index.js +388 -0
  45. package/dist/config/kimi-config.d.ts +33 -0
  46. package/dist/config/kimi-config.js +111 -0
  47. package/dist/config/opencode-config.d.ts +20 -0
  48. package/dist/config/opencode-config.js +124 -0
  49. package/dist/config/pi-config.d.ts +49 -0
  50. package/dist/config/pi-config.js +151 -0
  51. package/dist/config/wizard.d.ts +12 -0
  52. package/dist/config/wizard.js +84 -0
  53. package/dist/connector/file-limits.d.ts +7 -0
  54. package/dist/connector/file-limits.js +8 -0
  55. package/dist/connector/index.d.ts +86 -0
  56. package/dist/connector/index.js +340 -0
  57. package/dist/error-classification.d.ts +15 -0
  58. package/dist/error-classification.js +24 -0
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +580 -0
  61. package/dist/instance-lock.d.ts +30 -0
  62. package/dist/instance-lock.js +184 -0
  63. package/dist/logger/index.d.ts +57 -0
  64. package/dist/logger/index.js +178 -0
  65. package/dist/order/index.d.ts +27 -0
  66. package/dist/order/index.js +90 -0
  67. package/dist/persistence/atomic-write.d.ts +10 -0
  68. package/dist/persistence/atomic-write.js +65 -0
  69. package/dist/persistence/load-json-file.d.ts +9 -0
  70. package/dist/persistence/load-json-file.js +23 -0
  71. package/dist/restart.d.ts +24 -0
  72. package/dist/restart.js +73 -0
  73. package/dist/router/card-helpers.d.ts +28 -0
  74. package/dist/router/card-helpers.js +65 -0
  75. package/dist/router/config/claude.d.ts +14 -0
  76. package/dist/router/config/claude.js +72 -0
  77. package/dist/router/config/codex.d.ts +22 -0
  78. package/dist/router/config/codex.js +100 -0
  79. package/dist/router/config/common/render.d.ts +16 -0
  80. package/dist/router/config/common/render.js +158 -0
  81. package/dist/router/config/index.d.ts +18 -0
  82. package/dist/router/config/index.js +38 -0
  83. package/dist/router/config/kimi.d.ts +21 -0
  84. package/dist/router/config/kimi.js +63 -0
  85. package/dist/router/config/opencode.d.ts +12 -0
  86. package/dist/router/config/opencode.js +44 -0
  87. package/dist/router/config/pi.d.ts +12 -0
  88. package/dist/router/config/pi.js +50 -0
  89. package/dist/router/config/types.d.ts +43 -0
  90. package/dist/router/config/types.js +8 -0
  91. package/dist/router/index.d.ts +318 -0
  92. package/dist/router/index.js +2715 -0
  93. package/dist/router/order-exec-dispatch.d.ts +30 -0
  94. package/dist/router/order-exec-dispatch.js +48 -0
  95. package/dist/router/utils.d.ts +44 -0
  96. package/dist/router/utils.js +116 -0
  97. package/dist/runner/bash/index.d.ts +4 -0
  98. package/dist/runner/bash/index.js +5 -0
  99. package/dist/runner/bash/runner.d.ts +55 -0
  100. package/dist/runner/bash/runner.js +213 -0
  101. package/dist/runner/claude/index.d.ts +4 -0
  102. package/dist/runner/claude/index.js +5 -0
  103. package/dist/runner/claude/runner.d.ts +29 -0
  104. package/dist/runner/claude/runner.js +83 -0
  105. package/dist/runner/codex/argv.d.ts +33 -0
  106. package/dist/runner/codex/argv.js +40 -0
  107. package/dist/runner/codex/index.d.ts +1 -0
  108. package/dist/runner/codex/index.js +2 -0
  109. package/dist/runner/codex/jsonl.d.ts +37 -0
  110. package/dist/runner/codex/jsonl.js +221 -0
  111. package/dist/runner/codex/runner.d.ts +65 -0
  112. package/dist/runner/codex/runner.js +124 -0
  113. package/dist/runner/common/exec-translator.d.ts +73 -0
  114. package/dist/runner/common/exec-translator.js +105 -0
  115. package/dist/runner/common/jsonl-stream.d.ts +39 -0
  116. package/dist/runner/common/jsonl-stream.js +203 -0
  117. package/dist/runner/common/process-stopper.d.ts +28 -0
  118. package/dist/runner/common/process-stopper.js +79 -0
  119. package/dist/runner/common/runner-utils.d.ts +21 -0
  120. package/dist/runner/common/runner-utils.js +65 -0
  121. package/dist/runner/common/spawn-heartbeat.d.ts +33 -0
  122. package/dist/runner/common/spawn-heartbeat.js +51 -0
  123. package/dist/runner/common/spawning-runner.d.ts +321 -0
  124. package/dist/runner/common/spawning-runner.js +634 -0
  125. package/dist/runner/common/usage.d.ts +31 -0
  126. package/dist/runner/common/usage.js +29 -0
  127. package/dist/runner/index.d.ts +19 -0
  128. package/dist/runner/index.js +24 -0
  129. package/dist/runner/kimi/index.d.ts +1 -0
  130. package/dist/runner/kimi/index.js +2 -0
  131. package/dist/runner/kimi/jsonl.d.ts +28 -0
  132. package/dist/runner/kimi/jsonl.js +143 -0
  133. package/dist/runner/kimi/runner.d.ts +50 -0
  134. package/dist/runner/kimi/runner.js +105 -0
  135. package/dist/runner/opencode/argv.d.ts +32 -0
  136. package/dist/runner/opencode/argv.js +35 -0
  137. package/dist/runner/opencode/index.d.ts +1 -0
  138. package/dist/runner/opencode/index.js +2 -0
  139. package/dist/runner/opencode/jsonl.d.ts +68 -0
  140. package/dist/runner/opencode/jsonl.js +295 -0
  141. package/dist/runner/opencode/runner.d.ts +63 -0
  142. package/dist/runner/opencode/runner.js +93 -0
  143. package/dist/runner/pi/index.d.ts +1 -0
  144. package/dist/runner/pi/index.js +2 -0
  145. package/dist/runner/pi/jsonl.d.ts +128 -0
  146. package/dist/runner/pi/jsonl.js +249 -0
  147. package/dist/runner/pi/runner.d.ts +37 -0
  148. package/dist/runner/pi/runner.js +76 -0
  149. package/dist/runner/registry.d.ts +54 -0
  150. package/dist/runner/registry.js +75 -0
  151. package/dist/runner/resolve-agent-choices.d.ts +7 -0
  152. package/dist/runner/resolve-agent-choices.js +76 -0
  153. package/dist/runner/sync-agent-choices.d.ts +6 -0
  154. package/dist/runner/sync-agent-choices.js +73 -0
  155. package/dist/runner/types.d.ts +250 -0
  156. package/dist/runner/types.js +8 -0
  157. package/dist/session/claude/index.d.ts +1 -0
  158. package/dist/session/claude/index.js +2 -0
  159. package/dist/session/claude/session-reader.d.ts +34 -0
  160. package/dist/session/claude/session-reader.js +46 -0
  161. package/dist/session/claude/sessions.d.ts +53 -0
  162. package/dist/session/claude/sessions.js +528 -0
  163. package/dist/session/codex/index.d.ts +1 -0
  164. package/dist/session/codex/index.js +2 -0
  165. package/dist/session/codex/rollout-reader.d.ts +88 -0
  166. package/dist/session/codex/rollout-reader.js +473 -0
  167. package/dist/session/codex/sessions.d.ts +21 -0
  168. package/dist/session/codex/sessions.js +40 -0
  169. package/dist/session/common/catch-up.d.ts +12 -0
  170. package/dist/session/common/catch-up.js +31 -0
  171. package/dist/session/common/constants.d.ts +11 -0
  172. package/dist/session/common/constants.js +12 -0
  173. package/dist/session/common/content-blocks.d.ts +37 -0
  174. package/dist/session/common/content-blocks.js +68 -0
  175. package/dist/session/common/jsonl.d.ts +77 -0
  176. package/dist/session/common/jsonl.js +351 -0
  177. package/dist/session/common/usage-accumulator.d.ts +58 -0
  178. package/dist/session/common/usage-accumulator.js +72 -0
  179. package/dist/session/index.d.ts +7 -0
  180. package/dist/session/index.js +11 -0
  181. package/dist/session/kimi/index.d.ts +1 -0
  182. package/dist/session/kimi/index.js +2 -0
  183. package/dist/session/kimi/sessions.d.ts +81 -0
  184. package/dist/session/kimi/sessions.js +527 -0
  185. package/dist/session/opencode/index.d.ts +1 -0
  186. package/dist/session/opencode/index.js +2 -0
  187. package/dist/session/opencode/sessions.d.ts +81 -0
  188. package/dist/session/opencode/sessions.js +388 -0
  189. package/dist/session/pi/index.d.ts +1 -0
  190. package/dist/session/pi/index.js +2 -0
  191. package/dist/session/pi/sessions.d.ts +27 -0
  192. package/dist/session/pi/sessions.js +399 -0
  193. package/dist/session/registry.d.ts +12 -0
  194. package/dist/session/registry.js +20 -0
  195. package/dist/session/session-store.d.ts +86 -0
  196. package/dist/session/session-store.js +450 -0
  197. package/dist/startup-contact.d.ts +18 -0
  198. package/dist/startup-contact.js +51 -0
  199. package/dist/workspace/index.d.ts +19 -0
  200. package/dist/workspace/index.js +53 -0
  201. package/package.json +93 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared helpers for building CardKit collapsible_panel elements.
3
+ *
4
+ * Docs: https://open.feishu.cn/document/uAjLw4CM/ukzMukzMukzM/feishu-cards/card-components/containers/collapsible-panel
5
+ *
6
+ * Requires Feishu client V7.9+. Lower versions show a placeholder.
7
+ * Used by all card types (run, bash, /resume, /ws, /active, etc.).
8
+ */
9
+ export type PanelBorder = 'grey' | 'red' | 'blue' | 'green' | 'orange';
10
+ /**
11
+ * Convenience: build a collapsible panel whose body is a single markdown div.
12
+ * Common case for thinking blocks and tool details.
13
+ */
14
+ export declare function collapsibleMarkdownPanel(opts: {
15
+ title: string;
16
+ expanded: boolean;
17
+ border?: PanelBorder;
18
+ content: string;
19
+ textSize?: string;
20
+ }): object;
21
+ /**
22
+ * Markdown div. `textSize` is optional ('notation' for small text).
23
+ * Content backslashes are escaped via escapeMarkdown to avoid unintended
24
+ * markdown escape sequences (see escapeMarkdown for the []| decision).
25
+ */
26
+ export declare function markdownDiv(content: string, textSize?: string): object;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Shared helpers for building CardKit collapsible_panel elements.
3
+ *
4
+ * Docs: https://open.feishu.cn/document/uAjLw4CM/ukzMukzMukzM/feishu-cards/card-components/containers/collapsible-panel
5
+ *
6
+ * Requires Feishu client V7.9+. Lower versions show a placeholder.
7
+ * Used by all card types (run, bash, /resume, /ws, /active, etc.).
8
+ */
9
+ /**
10
+ * Build a `collapsible_panel` element.
11
+ *
12
+ * The panel's `elements` are always present in the JSON payload (folding is
13
+ * purely visual), so they still count toward the card byte budget. Folding
14
+ * reduces visual height, not serialized size.
15
+ */
16
+ function collapsiblePanel(opts) {
17
+ return {
18
+ tag: 'collapsible_panel',
19
+ expanded: opts.expanded,
20
+ header: panelHeader(opts.title),
21
+ border: { color: opts.border ?? 'grey', corner_radius: '5px' },
22
+ vertical_spacing: '8px',
23
+ padding: '8px 8px 8px 8px',
24
+ elements: opts.elements,
25
+ };
26
+ }
27
+ /**
28
+ * Standard panel header with a down-arrow icon that rotates on expand.
29
+ * Used by all collapsible panels for visual consistency.
30
+ */
31
+ function panelHeader(titleMd) {
32
+ return {
33
+ title: { tag: 'markdown', content: titleMd },
34
+ vertical_align: 'center',
35
+ icon: {
36
+ tag: 'standard_icon',
37
+ token: 'down-small-ccm_outlined',
38
+ size: '16px 16px',
39
+ },
40
+ icon_position: 'follow_text',
41
+ icon_expanded_angle: -180,
42
+ };
43
+ }
44
+ /**
45
+ * Convenience: build a collapsible panel whose body is a single markdown div.
46
+ * Common case for thinking blocks and tool details.
47
+ */
48
+ export function collapsibleMarkdownPanel(opts) {
49
+ return collapsiblePanel({
50
+ title: opts.title,
51
+ expanded: opts.expanded,
52
+ border: opts.border,
53
+ elements: [markdownDiv(opts.content, opts.textSize)],
54
+ });
55
+ }
56
+ /**
57
+ * Markdown div. `textSize` is optional ('notation' for small text).
58
+ * Content backslashes are escaped via escapeMarkdown to avoid unintended
59
+ * markdown escape sequences (see escapeMarkdown for the []| decision).
60
+ */
61
+ export function markdownDiv(content, textSize) {
62
+ return {
63
+ tag: 'div',
64
+ text: {
65
+ tag: 'lark_md',
66
+ content: escapeMarkdown(content),
67
+ ...(textSize ? { text_size: textSize } : {}),
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * Escape backslash to prevent unintended markdown escape sequences.
73
+ *
74
+ * Backslash is markdown's escape character. Unpaired backslashes in content
75
+ * (e.g. Windows path `C:\Users`, regex `\d`) may cause the following character
76
+ * to be interpreted as a literal. Escape it to preserve the raw backslash.
77
+ *
78
+ * Note: Square brackets [] and pipe | are NOT escaped -- they are valid markdown
79
+ * (links, tables) and escaping them breaks link rendering. Verified safe via
80
+ * tests/escape-bracket-experiment.test.ts: unescaped []| does not trigger 11311.
81
+ */
82
+ function escapeMarkdown(content) {
83
+ if (!content)
84
+ return content;
85
+ return content.replace(/\\/g, '\\\\');
86
+ }
87
+ //# sourceMappingURL=collapsible.js.map
@@ -0,0 +1,78 @@
1
+ import { CardSession, type CardChannel, type StreamSettleResult } from './card-session.js';
2
+ import type { AgentEvent } from '../runner/index.js';
3
+ import { type FinishMeta, type RunState, type RunTerminal } from './run-state.js';
4
+ import { type RunCardRenderOptions } from './run-renderer.js';
5
+ export type { CardChannel as RunCardChannel };
6
+ export declare class RunCardSession extends CardSession<RunState, RunCardRenderOptions> {
7
+ /**
8
+ * P1-3 push 合批窗口(ms)。连续 text delta 各触发一次全卡 render + patch 是
9
+ * 流式 CPU 主要来源(§P1-3)。窗口内多个事件复用同一次
10
+ * 延迟 flush,只 render + controller.update 一次。100ms 内人眼无感,且 text
11
+ * 流式 patch 次数 -80%。窗口大小可通过 constructor 覆盖(测试用小窗口)。
12
+ */
13
+ private readonly coalesceMs;
14
+ private flushTimer;
15
+ private flushInFlight;
16
+ /**
17
+ * P2-1:in-flight flush 期间到达的 push 标记。scheduleFlush 在 flushInFlight
18
+ * 时早退不调度新 timer,但事件已 reduce 进 state;若无此标志,in-flight flush
19
+ * 完成后该事件无 follow-up render,停留为 stale 中间帧(生产 SDK 的
20
+ * controller.update 经 throttle + setTimeout 异步 resolve,故 flush 与
21
+ * 下一个 push 交叠是真实场景)。finally 检查此标志后重新 scheduleFlush,
22
+ * 保证 in-flight 期间累积的事件最终被渲染。
23
+ */
24
+ private pendingReschedule;
25
+ /**
26
+ * 当前 in-flight flush 的 promise(若 flush 正在 await updateCard)。
27
+ * finish() 在终态 patch 前 await 它,保证 in-flight 的 pre-terminal patch
28
+ * 先落地、terminal patch 后落地——否则两条 controller.update 并发无 FIFO,
29
+ * pre-terminal 可能在 terminal 之后 resolve 留下"思考中"终帧(P2)。
30
+ */
31
+ private flushP;
32
+ constructor(opts: {
33
+ connector: CardChannel;
34
+ chatId: string;
35
+ replyTo?: string;
36
+ runId: string;
37
+ renderOptions?: RunCardRenderOptions;
38
+ startTimeoutMs?: number;
39
+ settleTimeoutMs?: number;
40
+ /** push 合批窗口,默认 100ms(0 = 禁用合批,每事件立即 flush)。 */
41
+ coalesceMs?: number;
42
+ });
43
+ protected get logPrefix(): string;
44
+ protected get errorPrefix(): string;
45
+ protected renderCard(state: RunState, options: RunCardRenderOptions): object;
46
+ protected hasOwnBudgetProtection(): boolean;
47
+ push(event: AgentEvent): Promise<void>;
48
+ /**
49
+ * 调度一次延迟 flush。已有 pending 调度则复用(合批核心);否则起一个
50
+ * coalesceMs 定时器。flushInFlight 期间不再起 timer(避免重入叠加),但置
51
+ * pendingReschedule 以便 in-flight flush 完成后重新调度——否则 in-flight 期间
52
+ * 到达的 push 会停留为 stale 中间帧(P2-1)。
53
+ */
54
+ private scheduleFlush;
55
+ /**
56
+ * 执行一次 render + updateCard。合批窗口到期或 finish 立即 flush 时调用。
57
+ * flushInFlight 防止 timer 回调与 finish 的即时 flush 重入。
58
+ *
59
+ * 终态守卫:若 finish() 已把 state 转为终态(done/error/interrupted/idle_timeout),
60
+ * 跳过本次 patch——避免 in-flight flush 用 pre-terminal state 覆盖 finish 已发的
61
+ * 终态卡。finish() 自身会 await 任何 in-flight flush 保证顺序,此守卫是双保险。
62
+ */
63
+ private flush;
64
+ /**
65
+ * 取消待 flush 的合批调度(finish/settle 前清理,避免延迟 update 与终态
66
+ * 渲染竞争)。返回是否有被取消的 pending 调度。同时清掉 pendingReschedule,
67
+ * 防止 in-flight flush 的 finally 在 finish await 之后又起 dangling timer
68
+ * (finish 自身会渲染含累积事件的终态卡,无需 follow-up)。
69
+ */
70
+ private cancelPendingFlush;
71
+ finish(terminal: Exclude<RunTerminal, 'running' | 'finalizing'>, meta?: FinishMeta): Promise<void>;
72
+ /**
73
+ * settle 前兜底取消合批调度。bridge 正常流程 finish→settle,finish 已 cancel;
74
+ * 但若未来有直调 settle(未经 finish)的路径,避免 dangling timer 在 release
75
+ * 后触发 void flush() 去更新已释放的 stream(harmless 但无谓)。
76
+ */
77
+ settle(): Promise<StreamSettleResult>;
78
+ }
@@ -0,0 +1,165 @@
1
+ import { CardSession } from './card-session.js';
2
+ import { createInitialRunState, finishRun, reduceRunState, } from './run-state.js';
3
+ import { renderRunCard } from './run-renderer.js';
4
+ export class RunCardSession extends CardSession {
5
+ /**
6
+ * P1-3 push 合批窗口(ms)。连续 text delta 各触发一次全卡 render + patch 是
7
+ * 流式 CPU 主要来源(§P1-3)。窗口内多个事件复用同一次
8
+ * 延迟 flush,只 render + controller.update 一次。100ms 内人眼无感,且 text
9
+ * 流式 patch 次数 -80%。窗口大小可通过 constructor 覆盖(测试用小窗口)。
10
+ */
11
+ coalesceMs;
12
+ flushTimer;
13
+ flushInFlight = false;
14
+ /**
15
+ * P2-1:in-flight flush 期间到达的 push 标记。scheduleFlush 在 flushInFlight
16
+ * 时早退不调度新 timer,但事件已 reduce 进 state;若无此标志,in-flight flush
17
+ * 完成后该事件无 follow-up render,停留为 stale 中间帧(生产 SDK 的
18
+ * controller.update 经 throttle + setTimeout 异步 resolve,故 flush 与
19
+ * 下一个 push 交叠是真实场景)。finally 检查此标志后重新 scheduleFlush,
20
+ * 保证 in-flight 期间累积的事件最终被渲染。
21
+ */
22
+ pendingReschedule = false;
23
+ /**
24
+ * 当前 in-flight flush 的 promise(若 flush 正在 await updateCard)。
25
+ * finish() 在终态 patch 前 await 它,保证 in-flight 的 pre-terminal patch
26
+ * 先落地、terminal patch 后落地——否则两条 controller.update 并发无 FIFO,
27
+ * pre-terminal 可能在 terminal 之后 resolve 留下"思考中"终帧(P2)。
28
+ */
29
+ flushP;
30
+ constructor(opts) {
31
+ super(createInitialRunState(opts.runId), {
32
+ connector: opts.connector,
33
+ chatId: opts.chatId,
34
+ replyTo: opts.replyTo,
35
+ renderOptions: opts.renderOptions,
36
+ startTimeoutMs: opts.startTimeoutMs,
37
+ settleTimeoutMs: opts.settleTimeoutMs,
38
+ });
39
+ this.coalesceMs = opts.coalesceMs ?? 100;
40
+ }
41
+ get logPrefix() {
42
+ return '[card]';
43
+ }
44
+ get errorPrefix() {
45
+ return 'card';
46
+ }
47
+ renderCard(state, options) {
48
+ return renderRunCard(state, options);
49
+ }
50
+ hasOwnBudgetProtection() {
51
+ // renderRunCard has 3-tier degraded/extreme fallback budget protection
52
+ // that guarantees the returned card is <= 28KB, so the base-class
53
+ // enforceCardBudget stringify pass is redundant for run cards.
54
+ return true;
55
+ }
56
+ async push(event) {
57
+ // state 每事件立即 reduce(始终最新),但 updateCard 延迟到合批窗口末尾。
58
+ // 窗口内多次 push 复用同一 flushTimer,只 render + controller.update 一次
59
+ // (P1-3)。push 立即 resolve(fire-and-forget render)—— bridge 串行
60
+ // await 不被 render 阻塞,循环快速灌入事件,多个事件落进同一窗口被合并。
61
+ //
62
+ // 首 push 也走窗口(无 immediate-first-flush):start() 已发初始卡(streaming
63
+ // footer),用户立即看到活动;首 text delta 落进 100ms 窗口后人眼无感。
64
+ this.state = reduceRunState(this.state, event);
65
+ if (this.coalesceMs <= 0) {
66
+ // 合批禁用:立即同步 flush,保留 push 后立即生效的既有契约(错误处理
67
+ // 测试依赖此)。await 确保 flush 完成(含 fallback)后再 resolve。
68
+ await this.flush();
69
+ return;
70
+ }
71
+ this.scheduleFlush();
72
+ }
73
+ /**
74
+ * 调度一次延迟 flush。已有 pending 调度则复用(合批核心);否则起一个
75
+ * coalesceMs 定时器。flushInFlight 期间不再起 timer(避免重入叠加),但置
76
+ * pendingReschedule 以便 in-flight flush 完成后重新调度——否则 in-flight 期间
77
+ * 到达的 push 会停留为 stale 中间帧(P2-1)。
78
+ */
79
+ scheduleFlush() {
80
+ if (this.flushTimer)
81
+ return;
82
+ if (this.flushInFlight) {
83
+ this.pendingReschedule = true;
84
+ return;
85
+ }
86
+ this.flushTimer = setTimeout(() => {
87
+ this.flushTimer = undefined;
88
+ void this.flush();
89
+ }, this.coalesceMs);
90
+ }
91
+ /**
92
+ * 执行一次 render + updateCard。合批窗口到期或 finish 立即 flush 时调用。
93
+ * flushInFlight 防止 timer 回调与 finish 的即时 flush 重入。
94
+ *
95
+ * 终态守卫:若 finish() 已把 state 转为终态(done/error/interrupted/idle_timeout),
96
+ * 跳过本次 patch——避免 in-flight flush 用 pre-terminal state 覆盖 finish 已发的
97
+ * 终态卡。finish() 自身会 await 任何 in-flight flush 保证顺序,此守卫是双保险。
98
+ */
99
+ async flush() {
100
+ if (this.flushInFlight)
101
+ return;
102
+ // finish() 已转终态:in-flight/延迟 flush 不再用旧 state patch
103
+ if (this.state.terminal !== 'running' && this.state.terminal !== 'finalizing') {
104
+ return;
105
+ }
106
+ this.flushInFlight = true;
107
+ const flushPromise = (async () => {
108
+ try {
109
+ await this.updateCard();
110
+ }
111
+ finally {
112
+ this.flushInFlight = false;
113
+ // flushInFlight 守卫保证同一时刻只有一个 in-flight flush,故可无条件清空。
114
+ this.flushP = undefined;
115
+ // P2-1:in-flight 期间若有 push 到达(pendingReschedule),重新调度一次
116
+ // flush 渲染累积事件,避免 stale 中间帧。终态守卫在 flush() 入口已拦截,
117
+ // 故 finish 后不会误触发。
118
+ if (this.pendingReschedule) {
119
+ this.pendingReschedule = false;
120
+ this.scheduleFlush();
121
+ }
122
+ }
123
+ })();
124
+ this.flushP = flushPromise;
125
+ return flushPromise;
126
+ }
127
+ /**
128
+ * 取消待 flush 的合批调度(finish/settle 前清理,避免延迟 update 与终态
129
+ * 渲染竞争)。返回是否有被取消的 pending 调度。同时清掉 pendingReschedule,
130
+ * 防止 in-flight flush 的 finally 在 finish await 之后又起 dangling timer
131
+ * (finish 自身会渲染含累积事件的终态卡,无需 follow-up)。
132
+ */
133
+ cancelPendingFlush() {
134
+ this.pendingReschedule = false;
135
+ if (this.flushTimer) {
136
+ clearTimeout(this.flushTimer);
137
+ this.flushTimer = undefined;
138
+ return true;
139
+ }
140
+ return false;
141
+ }
142
+ async finish(terminal, meta = {}) {
143
+ // 终态必须即时显示(spec):取消合批窗口,不等延迟 flush 调度。
144
+ this.cancelPendingFlush();
145
+ // 若有 in-flight flush 正在 await updateCard,必须等它完成后再发终态 patch。
146
+ // 否则两条并发 controller.update 无 FIFO 保证,in-flight 的 pre-terminal
147
+ // patch 可能在终态 patch 之后 resolve → 用户看到"思考中"终帧(P2)。
148
+ // await 保证顺序:pre-terminal 先落地 → terminal 后落地 → terminal 胜出。
149
+ if (this.flushP)
150
+ await this.flushP;
151
+ this.state = finishRun(this.state, terminal, meta);
152
+ await this.updateCard();
153
+ this.release();
154
+ }
155
+ /**
156
+ * settle 前兜底取消合批调度。bridge 正常流程 finish→settle,finish 已 cancel;
157
+ * 但若未来有直调 settle(未经 finish)的路径,避免 dangling timer 在 release
158
+ * 后触发 void flush() 去更新已释放的 stream(harmless 但无谓)。
159
+ */
160
+ async settle() {
161
+ this.cancelPendingFlush();
162
+ return super.settle();
163
+ }
164
+ }
165
+ //# sourceMappingURL=run-card-session.js.map
@@ -0,0 +1,66 @@
1
+ import type { RunState, ToolEntry } from './run-state.js';
2
+ export interface RunCardRenderOptions {
3
+ showThinking?: boolean;
4
+ showToolUse?: boolean;
5
+ showToolResult?: boolean;
6
+ /**
7
+ * Agent kind for the run card header title (e.g. "Claude · 思考中").
8
+ * Defaults to 'claude' when not provided; the bridge passes `config.defaultAgent`.
9
+ */
10
+ agentKind?: string;
11
+ }
12
+ type BlockGroup = {
13
+ kind: 'thinking';
14
+ content: string;
15
+ active: boolean;
16
+ timestamp?: string;
17
+ } | {
18
+ kind: 'text';
19
+ content: string;
20
+ timestamp?: string;
21
+ } | {
22
+ kind: 'tool';
23
+ tool: ToolEntry;
24
+ } | {
25
+ kind: 'plan';
26
+ content: string;
27
+ active: boolean;
28
+ timestamp?: string;
29
+ } | {
30
+ kind: 'file_change';
31
+ path: string;
32
+ operation: 'create' | 'edit' | 'delete' | 'read';
33
+ diff?: string;
34
+ timestamp?: string;
35
+ };
36
+ /** 每个块的截断后内容(thinking/plan/text),测量与渲染共用(P1-2 建议项)。 */
37
+ type GroupContentPrepared = Map<BlockGroup, string>;
38
+ /**
39
+ * 影子测量:精确复刻 normal 路径(buildChronologicalContent + renderRunCard 的
40
+ * 骨架)产物 stringify 后的字节数,避免"先建完整卡再丢弃"的浪费。
41
+ *
42
+ * 第一性原理:估算应当测量"将要渲染的内容",而非近似它。
43
+ * - 骨架(schema/config/header/statusRow/summary/buttons/spacer)用小对象实测
44
+ * (measureJson),不依赖固定常数。
45
+ * - 块内容经与渲染完全相同的变换后精确测量:truncateUtf8 截断 →
46
+ * markdownDiv 的 escapeMarkdown(反斜杠 2×)→ JSON.stringify。因此 CJK
47
+ * (3 字节/字符无转义)与 ASCII 高转义(\n/" 膨胀)双向精确,无需转义因子。
48
+ * - tool 块直接 measureJson(renderTool(...))——与渲染共用同一函数,无复制。
49
+ * - 块结构基于 groupBlocks(与渲染一致,连续 text 合并),保证合并语义一致。
50
+ *
51
+ * 唯一系统性低估:elements 数组元素间逗号/括号 ~n+1 字节(忽略,4KB 裕度内)。
52
+ *
53
+ * 注意:估算只是优化路径选择,不替代最终 stringify 兜底。即使估算说"低于阈值"
54
+ * 走正常路径,renderRunCard 仍会 stringify 确认 ≤28KB;若估算低估,仍 fallback
55
+ * 到 degraded(安全网不丢)。
56
+ */
57
+ /** 导出供测试断言估算精度(估算 ≈ 实际渲染字节,见 tests/anchor/run-card/run-card.test.ts)。 */
58
+ export declare function estimateCardBytes(state: RunState, options?: RunCardRenderOptions, shared?: {
59
+ groups?: BlockGroup[];
60
+ prepared?: GroupContentPrepared;
61
+ }): number;
62
+ /**
63
+ * CardKit 2.0 renderer - simplified for streaming (no tabs)
64
+ */
65
+ export declare function renderRunCard(state: RunState, options?: RunCardRenderOptions): object;
66
+ export {};