berry-agent 0.1.0-alpha.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 (401) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/dist/.api-emit.stamp +1 -0
  4. package/dist/.build-meta.json +6 -0
  5. package/dist/agent/events.js +1 -0
  6. package/dist/agent/index.js +2 -0
  7. package/dist/agent/loop.js +139 -0
  8. package/dist/agent/queue.js +119 -0
  9. package/dist/agent/stream.js +62 -0
  10. package/dist/agent/tools-batch.js +221 -0
  11. package/dist/agent/types.js +8 -0
  12. package/dist/api/berry-agent-llm.d.ts +18 -0
  13. package/dist/api/berry-agent.d.ts +9 -0
  14. package/dist/api/surface.json +1632 -0
  15. package/dist/api/tsconfig.paths.json +12 -0
  16. package/dist/api/typebox-compile.d.ts +2 -0
  17. package/dist/api/typebox-value.d.ts +2 -0
  18. package/dist/api/typebox.d.ts +2 -0
  19. package/dist/browser/cdp.js +256 -0
  20. package/dist/browser/codes.js +31 -0
  21. package/dist/browser/discover.js +107 -0
  22. package/dist/browser/engine.js +169 -0
  23. package/dist/browser/index.js +17 -0
  24. package/dist/browser/install.js +342 -0
  25. package/dist/browser/page.js +321 -0
  26. package/dist/browser/service.js +158 -0
  27. package/dist/browser/tools.js +198 -0
  28. package/dist/browser/types.js +81 -0
  29. package/dist/channels/ask-queue.js +82 -0
  30. package/dist/channels/commands.js +129 -0
  31. package/dist/channels/engine/cell.js +143 -0
  32. package/dist/channels/engine/diff.js +143 -0
  33. package/dist/channels/engine/engine.js +416 -0
  34. package/dist/channels/engine/index.js +8 -0
  35. package/dist/channels/engine/input-keys.js +86 -0
  36. package/dist/channels/engine/input.js +596 -0
  37. package/dist/channels/engine/memory-io.js +71 -0
  38. package/dist/channels/engine/process-io.js +38 -0
  39. package/dist/channels/engine/types.js +17 -0
  40. package/dist/channels/engine/width.js +122 -0
  41. package/dist/channels/index.js +15 -0
  42. package/dist/channels/registry.js +56 -0
  43. package/dist/channels/sdk/admit.js +29 -0
  44. package/dist/channels/sdk/backend.js +102 -0
  45. package/dist/channels/sdk/cursor.js +56 -0
  46. package/dist/channels/sdk/index.js +20 -0
  47. package/dist/channels/sdk/jsonl.js +208 -0
  48. package/dist/channels/sdk/protocol.js +24 -0
  49. package/dist/channels/sdk/schema.js +91 -0
  50. package/dist/channels/sdk/wire-core.js +446 -0
  51. package/dist/channels/service.js +169 -0
  52. package/dist/channels/tui/autocomplete/autocomplete.js +42 -0
  53. package/dist/channels/tui/autocomplete/file-mentions.js +106 -0
  54. package/dist/channels/tui/autocomplete/popup.js +148 -0
  55. package/dist/channels/tui/autocomplete/provider.js +7 -0
  56. package/dist/channels/tui/autocomplete/token.js +68 -0
  57. package/dist/channels/tui/backend/ansi-rows.js +211 -0
  58. package/dist/channels/tui/backend/main-screen.js +207 -0
  59. package/dist/channels/tui/backend/osc.js +91 -0
  60. package/dist/channels/tui/backend/transcript.js +230 -0
  61. package/dist/channels/tui/backend/tui-backend.js +915 -0
  62. package/dist/channels/tui/editor/editor-model.js +543 -0
  63. package/dist/channels/tui/editor/editor-view.js +140 -0
  64. package/dist/channels/tui/editor/editor.js +275 -0
  65. package/dist/channels/tui/editor/undo-stack.js +35 -0
  66. package/dist/channels/tui/editor/visual-lines.js +126 -0
  67. package/dist/channels/tui/editor/word-nav.js +106 -0
  68. package/dist/channels/tui/history/history-viewer.js +403 -0
  69. package/dist/channels/tui/index.js +3 -0
  70. package/dist/channels/tui/layout.js +167 -0
  71. package/dist/channels/tui/markdown/blocks.js +111 -0
  72. package/dist/channels/tui/markdown/inline.js +75 -0
  73. package/dist/channels/tui/markdown/markdown.js +182 -0
  74. package/dist/channels/tui/memory/memory-viewer.js +540 -0
  75. package/dist/channels/tui/overlay/alt-screen.js +105 -0
  76. package/dist/channels/tui/overlay/overlay.js +57 -0
  77. package/dist/channels/tui/overlay/select-confirm.js +156 -0
  78. package/dist/channels/tui/panels/todo-panel.js +54 -0
  79. package/dist/channels/tui/panels/tool-progress-panel.js +93 -0
  80. package/dist/channels/tui/scroll/scroll-view.js +254 -0
  81. package/dist/channels/tui/status/status-line.js +76 -0
  82. package/dist/channels/tui/text.js +38 -0
  83. package/dist/channels/tui/theme.js +41 -0
  84. package/dist/channels/types.js +11 -0
  85. package/dist/channels/ui-core.js +209 -0
  86. package/dist/checkpoint/capture.js +45 -0
  87. package/dist/checkpoint/codes.js +33 -0
  88. package/dist/checkpoint/command.js +82 -0
  89. package/dist/checkpoint/gate.js +48 -0
  90. package/dist/checkpoint/index.js +20 -0
  91. package/dist/checkpoint/restore.js +172 -0
  92. package/dist/checkpoint/store.js +247 -0
  93. package/dist/checkpoint/types.js +4 -0
  94. package/dist/checkpoint/walk.js +146 -0
  95. package/dist/compaction/ccr-tools.js +112 -0
  96. package/dist/compaction/ccr.js +84 -0
  97. package/dist/compaction/codes.js +21 -0
  98. package/dist/compaction/index.js +18 -0
  99. package/dist/compaction/policy.js +200 -0
  100. package/dist/compaction/service.js +422 -0
  101. package/dist/compaction/slots.js +121 -0
  102. package/dist/compaction/types.js +10 -0
  103. package/dist/context/codes.js +40 -0
  104. package/dist/context/events.js +160 -0
  105. package/dist/context/index.js +13 -0
  106. package/dist/context/logger.js +136 -0
  107. package/dist/context/scope.js +182 -0
  108. package/dist/context/workspace.js +104 -0
  109. package/dist/contracts/agent-events.d.ts +73 -0
  110. package/dist/contracts/agent-events.js +16 -0
  111. package/dist/contracts/api.d.ts +249 -0
  112. package/dist/contracts/api.js +299 -0
  113. package/dist/contracts/approval.d.ts +37 -0
  114. package/dist/contracts/approval.js +9 -0
  115. package/dist/contracts/env-ref.d.ts +41 -0
  116. package/dist/contracts/env-ref.js +32 -0
  117. package/dist/contracts/errors.d.ts +56 -0
  118. package/dist/contracts/errors.js +266 -0
  119. package/dist/contracts/events.d.ts +70 -0
  120. package/dist/contracts/events.js +329 -0
  121. package/dist/contracts/index.d.ts +32 -0
  122. package/dist/contracts/index.js +26 -0
  123. package/dist/contracts/llm.d.ts +275 -0
  124. package/dist/contracts/llm.js +12 -0
  125. package/dist/contracts/messages.d.ts +63 -0
  126. package/dist/contracts/messages.js +65 -0
  127. package/dist/contracts/redact.d.ts +46 -0
  128. package/dist/contracts/redact.js +223 -0
  129. package/dist/contracts/tools.d.ts +197 -0
  130. package/dist/contracts/tools.js +33 -0
  131. package/dist/contracts/types.d.ts +302 -0
  132. package/dist/contracts/types.js +50 -0
  133. package/dist/contracts/ui.d.ts +122 -0
  134. package/dist/contracts/ui.js +15 -0
  135. package/dist/conversation/agent-service.js +61 -0
  136. package/dist/conversation/approval-wiring.js +82 -0
  137. package/dist/conversation/backoff.js +38 -0
  138. package/dist/conversation/codes.js +47 -0
  139. package/dist/conversation/control-tools.js +102 -0
  140. package/dist/conversation/control.js +210 -0
  141. package/dist/conversation/driver.js +922 -0
  142. package/dist/conversation/index.js +12 -0
  143. package/dist/conversation/model-visible.js +169 -0
  144. package/dist/conversation/open-tools.js +126 -0
  145. package/dist/conversation/reseed.js +132 -0
  146. package/dist/conversation/sessions.js +215 -0
  147. package/dist/conversation/todo.js +203 -0
  148. package/dist/conversation/types.js +35 -0
  149. package/dist/conversation/wiring.js +204 -0
  150. package/dist/credentials/codes.js +59 -0
  151. package/dist/credentials/commands.js +186 -0
  152. package/dist/credentials/env-ref.js +34 -0
  153. package/dist/credentials/index.js +28 -0
  154. package/dist/credentials/migration.js +18 -0
  155. package/dist/credentials/oauth.js +266 -0
  156. package/dist/credentials/refresh.js +152 -0
  157. package/dist/credentials/secrets.js +118 -0
  158. package/dist/credentials/types.js +30 -0
  159. package/dist/exec/bash.js +279 -0
  160. package/dist/exec/codes.js +37 -0
  161. package/dist/exec/env.js +99 -0
  162. package/dist/exec/environment.js +39 -0
  163. package/dist/exec/git-guard.js +623 -0
  164. package/dist/exec/index.js +24 -0
  165. package/dist/exec/registry.js +120 -0
  166. package/dist/exec/spawn.js +281 -0
  167. package/dist/exec/tail.js +77 -0
  168. package/dist/exec/types.js +5 -0
  169. package/dist/goal/codes.js +33 -0
  170. package/dist/goal/command.js +175 -0
  171. package/dist/goal/fold.js +121 -0
  172. package/dist/goal/gates.js +122 -0
  173. package/dist/goal/index.js +22 -0
  174. package/dist/goal/migration.js +48 -0
  175. package/dist/goal/service.js +501 -0
  176. package/dist/goal/todo-tool.js +174 -0
  177. package/dist/goal/types.js +1 -0
  178. package/dist/goal/update-tool.js +46 -0
  179. package/dist/host/approval-cmd.js +276 -0
  180. package/dist/host/assembly.js +1146 -0
  181. package/dist/host/boot-failures.js +104 -0
  182. package/dist/host/budget-advisory.js +74 -0
  183. package/dist/host/budget-broadcast.js +50 -0
  184. package/dist/host/builtins.js +54 -0
  185. package/dist/host/cli.js +544 -0
  186. package/dist/host/codes.js +120 -0
  187. package/dist/host/config-schema.js +195 -0
  188. package/dist/host/conversation-stack.js +654 -0
  189. package/dist/host/core-plugins.js +1685 -0
  190. package/dist/host/credentials-cmd.js +42 -0
  191. package/dist/host/disclosure.js +30 -0
  192. package/dist/host/dispatch.js +105 -0
  193. package/dist/host/doors-cmd.js +154 -0
  194. package/dist/host/dump-config.js +44 -0
  195. package/dist/host/hook-dispatch-guard.js +27 -0
  196. package/dist/host/import-gate.js +153 -0
  197. package/dist/host/index.js +54 -0
  198. package/dist/host/issue-session.js +241 -0
  199. package/dist/host/loader.js +372 -0
  200. package/dist/host/main.js +161 -0
  201. package/dist/host/manifest.js +381 -0
  202. package/dist/host/mcp-entry.js +53 -0
  203. package/dist/host/plugin-boot.js +952 -0
  204. package/dist/host/plugin-context.js +672 -0
  205. package/dist/host/plugin-install.js +480 -0
  206. package/dist/host/plugin-reload.js +124 -0
  207. package/dist/host/plugin-store.js +462 -0
  208. package/dist/host/plugin-tools.js +359 -0
  209. package/dist/host/plugin-uninstall.js +257 -0
  210. package/dist/host/plugins-cmd.js +302 -0
  211. package/dist/host/plugins-command.js +130 -0
  212. package/dist/host/plugins-config.js +195 -0
  213. package/dist/host/prompt-sections.js +133 -0
  214. package/dist/host/run-entry.js +544 -0
  215. package/dist/host/runtime.js +209 -0
  216. package/dist/host/scheduler-tick.js +294 -0
  217. package/dist/host/serve-daemon.js +501 -0
  218. package/dist/host/serve-entry.js +279 -0
  219. package/dist/host/session-anchor.js +41 -0
  220. package/dist/host/sessions-cmd.js +219 -0
  221. package/dist/host/sessions-face.js +47 -0
  222. package/dist/host/settings-store.js +112 -0
  223. package/dist/host/signals.js +69 -0
  224. package/dist/host/single-instance.js +105 -0
  225. package/dist/host/subagent-factory.js +291 -0
  226. package/dist/host/testkit/harness.js +283 -0
  227. package/dist/host/testkit/index.js +14 -0
  228. package/dist/host/testkit/matrix.js +302 -0
  229. package/dist/host/tool-policy-store.js +177 -0
  230. package/dist/host/triggers.js +212 -0
  231. package/dist/host/tui-entry.js +216 -0
  232. package/dist/host/webui-bridge.js +165 -0
  233. package/dist/issue/codes.js +38 -0
  234. package/dist/issue/filter.js +146 -0
  235. package/dist/issue/github.js +116 -0
  236. package/dist/issue/index.js +17 -0
  237. package/dist/issue/mount.js +70 -0
  238. package/dist/issue/poll.js +58 -0
  239. package/dist/issue/service.js +341 -0
  240. package/dist/issue/tools.js +70 -0
  241. package/dist/issue/types.js +34 -0
  242. package/dist/issue/webhook.js +126 -0
  243. package/dist/llm/codes.js +77 -0
  244. package/dist/llm/complete.js +196 -0
  245. package/dist/llm/events.js +15 -0
  246. package/dist/llm/index.js +24 -0
  247. package/dist/llm/inflight.js +65 -0
  248. package/dist/llm/model-id.js +58 -0
  249. package/dist/llm/provider-face.d.ts +32 -0
  250. package/dist/llm/provider-face.js +34 -0
  251. package/dist/llm/recovery.js +146 -0
  252. package/dist/llm/runtime.js +46 -0
  253. package/dist/llm/stream-fn.js +147 -0
  254. package/dist/lsp/codes.js +31 -0
  255. package/dist/lsp/connection.js +147 -0
  256. package/dist/lsp/frame.js +110 -0
  257. package/dist/lsp/index.js +16 -0
  258. package/dist/lsp/inject.js +58 -0
  259. package/dist/lsp/instance.js +264 -0
  260. package/dist/lsp/service.js +522 -0
  261. package/dist/lsp/tools.js +53 -0
  262. package/dist/lsp/types.js +201 -0
  263. package/dist/mcp/bridge.js +222 -0
  264. package/dist/mcp/codes.js +21 -0
  265. package/dist/mcp/index.js +19 -0
  266. package/dist/mcp/jsonrpc.js +230 -0
  267. package/dist/mcp/service.js +125 -0
  268. package/dist/mcp/tools.js +145 -0
  269. package/dist/mcp/types.js +108 -0
  270. package/dist/memory/cite.js +131 -0
  271. package/dist/memory/codes.js +50 -0
  272. package/dist/memory/command.js +71 -0
  273. package/dist/memory/consolidate.js +219 -0
  274. package/dist/memory/cycle.js +109 -0
  275. package/dist/memory/dao.js +878 -0
  276. package/dist/memory/diff.js +238 -0
  277. package/dist/memory/extract.js +181 -0
  278. package/dist/memory/fts.js +58 -0
  279. package/dist/memory/index.js +42 -0
  280. package/dist/memory/inject.js +354 -0
  281. package/dist/memory/merge.js +178 -0
  282. package/dist/memory/migration.js +132 -0
  283. package/dist/memory/pollution.js +59 -0
  284. package/dist/memory/port.js +385 -0
  285. package/dist/memory/review.js +201 -0
  286. package/dist/memory/scan.js +89 -0
  287. package/dist/memory/tools.js +508 -0
  288. package/dist/memory/types.js +140 -0
  289. package/dist/obs/codes.js +38 -0
  290. package/dist/obs/db.js +115 -0
  291. package/dist/obs/index.js +20 -0
  292. package/dist/obs/rollup.js +61 -0
  293. package/dist/obs/service.js +363 -0
  294. package/dist/obs/session-tools.js +282 -0
  295. package/dist/obs/session-view.js +225 -0
  296. package/dist/obs/tool.js +97 -0
  297. package/dist/obs/types.js +1 -0
  298. package/dist/persist/audit.js +63 -0
  299. package/dist/persist/aux.js +39 -0
  300. package/dist/persist/codes.js +35 -0
  301. package/dist/persist/index.js +30 -0
  302. package/dist/persist/load-history.js +50 -0
  303. package/dist/persist/migrations.js +37 -0
  304. package/dist/persist/paths.js +108 -0
  305. package/dist/persist/persistence.js +229 -0
  306. package/dist/persist/schema.js +99 -0
  307. package/dist/persist/secret-box.js +95 -0
  308. package/dist/persist/store.js +706 -0
  309. package/dist/persist/write-behind.js +238 -0
  310. package/dist/safety/approval.js +171 -0
  311. package/dist/safety/bwrap.js +123 -0
  312. package/dist/safety/codes.js +57 -0
  313. package/dist/safety/danger.js +505 -0
  314. package/dist/safety/gate.js +226 -0
  315. package/dist/safety/index.js +24 -0
  316. package/dist/safety/presets.js +73 -0
  317. package/dist/safety/roots.js +139 -0
  318. package/dist/safety/sandbox.js +214 -0
  319. package/dist/safety/seatbelt.js +88 -0
  320. package/dist/safety/sensitive.js +34 -0
  321. package/dist/safety/tool-policy.js +203 -0
  322. package/dist/safety/types.js +13 -0
  323. package/dist/scheduler/codes.js +54 -0
  324. package/dist/scheduler/cron-backend.js +135 -0
  325. package/dist/scheduler/engine.js +280 -0
  326. package/dist/scheduler/gates.js +60 -0
  327. package/dist/scheduler/index.js +25 -0
  328. package/dist/scheduler/migration.js +22 -0
  329. package/dist/scheduler/runner.js +171 -0
  330. package/dist/scheduler/schedule.js +193 -0
  331. package/dist/scheduler/service.js +283 -0
  332. package/dist/scheduler/tick.js +139 -0
  333. package/dist/scheduler/types.js +1 -0
  334. package/dist/sdk/http.js +865 -0
  335. package/dist/sdk/index.js +22 -0
  336. package/dist/sdk/mcp.js +289 -0
  337. package/dist/sdk/plugin-route-registry.js +126 -0
  338. package/dist/sdk/plugin-routes.js +149 -0
  339. package/dist/sdk/security.js +116 -0
  340. package/dist/sdk/types.js +32 -0
  341. package/dist/session/budget.js +159 -0
  342. package/dist/session/codes.js +40 -0
  343. package/dist/session/derive.js +180 -0
  344. package/dist/session/event-data.js +1 -0
  345. package/dist/session/fork.js +72 -0
  346. package/dist/session/import-gates.js +148 -0
  347. package/dist/session/index.js +18 -0
  348. package/dist/session/recover.js +103 -0
  349. package/dist/session/session.js +279 -0
  350. package/dist/session/snapshot.js +107 -0
  351. package/dist/skills/agents.js +242 -0
  352. package/dist/skills/codes.js +51 -0
  353. package/dist/skills/discovery.js +266 -0
  354. package/dist/skills/frontmatter.js +215 -0
  355. package/dist/skills/index.js +28 -0
  356. package/dist/skills/load.js +118 -0
  357. package/dist/skills/manage.js +201 -0
  358. package/dist/skills/registry.js +138 -0
  359. package/dist/skills/render.js +124 -0
  360. package/dist/skills/sections.js +171 -0
  361. package/dist/skills/types.js +21 -0
  362. package/dist/subagent/codes.js +50 -0
  363. package/dist/subagent/declarative.js +55 -0
  364. package/dist/subagent/index.js +19 -0
  365. package/dist/subagent/notify.js +51 -0
  366. package/dist/subagent/provide.js +7 -0
  367. package/dist/subagent/registry.js +149 -0
  368. package/dist/subagent/service.js +338 -0
  369. package/dist/subagent/surface.js +44 -0
  370. package/dist/subagent/tool.js +153 -0
  371. package/dist/subagent/types.js +25 -0
  372. package/dist/tools/apply-patch.js +164 -0
  373. package/dist/tools/codes.js +109 -0
  374. package/dist/tools/fs.js +501 -0
  375. package/dist/tools/index.js +17 -0
  376. package/dist/tools/observed.js +99 -0
  377. package/dist/tools/pipeline.js +176 -0
  378. package/dist/tools/protected-read.js +54 -0
  379. package/dist/tools/registry.js +250 -0
  380. package/dist/tools/search.js +451 -0
  381. package/dist/tools/worktree.js +327 -0
  382. package/dist/web/codes.js +37 -0
  383. package/dist/web/gate.js +43 -0
  384. package/dist/web/hygiene.js +195 -0
  385. package/dist/web/index.js +16 -0
  386. package/dist/web/service.js +207 -0
  387. package/dist/web/ssrf-guard.js +44 -0
  388. package/dist/web/tool.js +76 -0
  389. package/dist/web/types.js +10 -0
  390. package/dist/webui/assets/index-B-t2O8gh.js +22 -0
  391. package/dist/webui/assets/index-DOlgSZ9Q.css +2 -0
  392. package/dist/webui/index.html +14 -0
  393. package/dist/webui/index.js +14 -0
  394. package/dist/webui/server.js +549 -0
  395. package/dist/webui/types.js +38 -0
  396. package/examples/README.md +26 -0
  397. package/examples/minimal-code-plugin/entry.js +43 -0
  398. package/examples/minimal-code-plugin/package.json +12 -0
  399. package/examples/pure-skill-pack/package.json +14 -0
  400. package/examples/pure-skill-pack/skills/markdown-table/SKILL.md +32 -0
  401. package/package.json +86 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * 主屏 inline 形态 ANSI 编舞基件(07 §4.1「屏幕模型双形态」主屏条——批 10e)。
3
+ *
4
+ * 主屏物理形态:DECSTBM 滚动区承载正文(区底写满自动滚——滚出视口整行交
5
+ * 终端原生 scrollback),固定区(输入框 + 状态行)钉屏底区外、绝对位可知。
6
+ * 本件提供四组原语:
7
+ * - 网格行 → ANSI 序列化(cell 样式 + 字素——与 diff 件行写出同语义:续格
8
+ * 跳过、语义空格字面、行尾不填充);
9
+ * - 网格行 → 带样式行(StyleRun 段族——批 10f-4 件 8 回看器 cell 写出形的
10
+ * 数据源;带样式行 → ANSI 序列化同件在位,主屏直写与回看器零第二渲染器);
11
+ * - 定位序列(绝对 CUP / 相对 CUU·CUD / CR / EL)与光标保存恢复;
12
+ * - 行级差分(固定区重画——变行重写、未变行零写出,行粒度复用 cellEquals)。
13
+ */
14
+ import { cellEquals, EMPTY_STYLE, styleEquals } from '../../engine/index.js';
15
+ /** ESC 前缀 */
16
+ const ESC = '\x1b';
17
+ /** 缺省样式判据:styleEquals 对 EMPTY_STYLE 单源(CellStyle 增字段自动同步——
18
+ * 勿手工罗列字段判缺省,10f-4 核验轮勘正) */
19
+ function isDefaultStyle(style) {
20
+ return styleEquals(style, EMPTY_STYLE);
21
+ }
22
+ /** SGR 全复位 */
23
+ export const SGR_RESET = `${ESC}[0m`;
24
+ /** EL 0:自光标至行尾 */
25
+ export const EL_TO_EOL = `${ESC}[K`;
26
+ /** 回行首(列 0——不滚行) */
27
+ export const CR = '\r';
28
+ /** 换行(滚动区内:区底自动滚、区外/中部只下移) */
29
+ export const LF = '\n';
30
+ /** 光标保存 / 恢复(DECSC/DECRC——编舞对(擦写远端区)后还原位) */
31
+ export const SAVE_CURSOR = `${ESC}7`;
32
+ export const RESTORE_CURSOR = `${ESC}8`;
33
+ /** 清全屏 + 光标归位(repaint 路) */
34
+ export const CLEAR_SCREEN = `${ESC}[2J${ESC}[H`;
35
+ /** 设滚动区 DECSTBM(1 基行号——底部固定区留区外) */
36
+ export function setScrollRegion(bottomRow1Based) {
37
+ return `${ESC}[1;${bottomRow1Based}r`;
38
+ }
39
+ /** 绝对定位(0 基入参——固定区专用:区外钉屏底、绝对位可知) */
40
+ export function cup(row, col) {
41
+ return `${ESC}[${row + 1};${col + 1}H`;
42
+ }
43
+ /** 相对上移 n 行(CUU——滚动区内槽位重画定位;边缘安全不滚) */
44
+ export function cuu(n) {
45
+ return n > 0 ? `${ESC}[${n}A` : '';
46
+ }
47
+ /** 相对下移 n 行(CUD——边缘安全不滚) */
48
+ export function cud(n) {
49
+ return n > 0 ? `${ESC}[${n}B` : '';
50
+ }
51
+ /** 样式 → SGR(与 diff 件 buildSgr 同语义:属性位 + 16 色前景背景全量形;简行块直拼消费) */
52
+ export function buildSgr(style) {
53
+ const params = [];
54
+ if (style.bold)
55
+ params.push('1');
56
+ if (style.dim)
57
+ params.push('2');
58
+ if (style.italic)
59
+ params.push('3');
60
+ if (style.underline)
61
+ params.push('4');
62
+ if (style.inverse)
63
+ params.push('7');
64
+ if (style.fg !== undefined)
65
+ params.push(String(style.fg < 8 ? 30 + style.fg : 82 + style.fg));
66
+ if (style.bg !== undefined)
67
+ params.push(String(style.bg < 8 ? 40 + style.bg : 92 + style.bg));
68
+ return params.length > 0 ? `${ESC}[${params.join(';')}m` : '';
69
+ }
70
+ /**
71
+ * 网格行 → ANSI 文本(无内容返回空串——调用方据此跳过写出)。
72
+ * 行尾变空白不产 EL(直写正文行是追加语义、行尾从无旧残留——EL 归擦区编舞)。
73
+ */
74
+ export function gridRowToAnsi(grid, row) {
75
+ // 内容末列 = 最右非空格格(续格属首格字素占位、算内容延伸)
76
+ let last = -1;
77
+ for (let col = grid.columns - 1; col >= 0; col--) {
78
+ if (!cellEquals(grid.getCell(row, col), null)) {
79
+ last = col;
80
+ break;
81
+ }
82
+ }
83
+ if (last < 0)
84
+ return '';
85
+ let out = '';
86
+ // 当前样式序列('' = 裸文本态;同样式零冗余、变化点复位再设——与 diff 件同纪律)
87
+ let currentSgr = '';
88
+ for (let col = 0; col <= last; col++) {
89
+ const cell = grid.getCell(row, col);
90
+ if (cell !== null && cell.width === 0)
91
+ continue; // 续格跳过
92
+ if (cell === null) {
93
+ out += ' '; // 语义空格(行中内容洞)字面写
94
+ continue;
95
+ }
96
+ const sgr = buildSgr(cell.style);
97
+ if (sgr !== currentSgr) {
98
+ out += (currentSgr !== '' ? SGR_RESET : '') + sgr;
99
+ currentSgr = sgr;
100
+ }
101
+ out += cell.grapheme;
102
+ }
103
+ if (currentSgr !== '')
104
+ out += SGR_RESET; // 行尾归零——不染后续写出
105
+ return out;
106
+ }
107
+ /**
108
+ * 网格行 → 带样式行(批 10f-4 件 8——回看器 cell 写出形的数据源)。
109
+ * 行走序与 gridRowToAnsi 同构(末列内容界 + 续格跳过 + 空洞空格),相邻同样式
110
+ * 格合并为单段;缺省样式段不产段(与空隙同形——序列化零冗余的基础)。
111
+ * 无内容行返回 null(调用方按需补空行——与 gridRowToAnsi 空串约定同构)。
112
+ */
113
+ export function gridRowToStyled(grid, row) {
114
+ // 内容末列 = 最右非空格格(续格属首格字素占位、算内容延伸)
115
+ let last = -1;
116
+ for (let col = grid.columns - 1; col >= 0; col--) {
117
+ if (!cellEquals(grid.getCell(row, col), null)) {
118
+ last = col;
119
+ break;
120
+ }
121
+ }
122
+ if (last < 0)
123
+ return null;
124
+ let plain = '';
125
+ const runs = [];
126
+ for (let col = 0; col <= last; col++) {
127
+ const cell = grid.getCell(row, col);
128
+ if (cell !== null && cell.width === 0)
129
+ continue; // 续格跳过(首格已携整字素)
130
+ const grapheme = cell === null ? ' ' : cell.grapheme; // 语义空格(行中内容洞)
131
+ const style = cell === null ? EMPTY_STYLE : cell.style;
132
+ const start = plain.length;
133
+ plain += grapheme; // 全字素入 plain(缺省样式字素是空隙文本——不丢字)
134
+ if (isDefaultStyle(style))
135
+ continue; // 缺省段不产段(gap 即裸文本)
136
+ // 同样式紧邻续段合并(段端点连续才并——中间有缺省格即断开)
137
+ const prev = runs[runs.length - 1];
138
+ if (prev !== undefined && styleEquals(prev.style, style) && prev.end === start) {
139
+ runs[runs.length - 1] = { start: prev.start, end: start + grapheme.length, style };
140
+ }
141
+ else {
142
+ runs.push({ start, end: start + grapheme.length, style });
143
+ }
144
+ }
145
+ return { plain, runs };
146
+ }
147
+ /**
148
+ * 带样式行 → ANSI 串(主屏直写形——与 gridRowToAnsi 同语义的行序列化:
149
+ * 样式变化点复位再设、同样式零冗余、行尾归零不染后续写出)。
150
+ * renderBlockLines 经本函数从带样式行导出 ANSI 形——两形零第二渲染器。
151
+ */
152
+ export function styledLineToAnsi(line) {
153
+ const { plain, runs } = line;
154
+ if (runs.length === 0)
155
+ return plain;
156
+ let out = '';
157
+ let currentSgr = '';
158
+ let pos = 0;
159
+ for (const run of runs) {
160
+ if (run.start > pos) {
161
+ // 段前空隙:在身样式先归零再写裸文本
162
+ if (currentSgr !== '') {
163
+ out += SGR_RESET;
164
+ currentSgr = '';
165
+ }
166
+ out += plain.slice(pos, run.start);
167
+ }
168
+ const sgr = buildSgr(run.style);
169
+ if (sgr !== currentSgr) {
170
+ out += (currentSgr !== '' ? SGR_RESET : '') + sgr;
171
+ currentSgr = sgr;
172
+ }
173
+ out += plain.slice(run.start, run.end);
174
+ pos = run.end;
175
+ }
176
+ if (pos < plain.length) {
177
+ // 段尾空隙:归零后写裸文本
178
+ if (currentSgr !== '') {
179
+ out += SGR_RESET;
180
+ currentSgr = '';
181
+ }
182
+ out += plain.slice(pos);
183
+ }
184
+ if (currentSgr !== '')
185
+ out += SGR_RESET;
186
+ return out;
187
+ }
188
+ /**
189
+ * 固定区行级差分(行粒度——变行整行重写、未变行零写出)。
190
+ * 返回 ANSI 串(含定位与行尾 EL——擦除只走 EL 禁空格填充);行位以
191
+ * baseRow 屏偏移绝对定位(固定区钉屏底、区外位可知)。
192
+ */
193
+ export function renderFixedRegionDiff(from, to, baseRow) {
194
+ let out = '';
195
+ for (let row = 0; row < to.rows; row++) {
196
+ if (from !== null && row < from.rows && gridRowEquals(from, to, row))
197
+ continue; // 未变行零写出
198
+ const text = gridRowToAnsi(to, row);
199
+ // 定位行首 → 复位 →(有内容写内容)→ 无条件 EL(行尾残留擦除——与 diff 件同律)
200
+ out += cup(baseRow + row, 0) + SGR_RESET + (text !== '' ? text + SGR_RESET : '') + EL_TO_EOL;
201
+ }
202
+ return out;
203
+ }
204
+ /** 行相等判定(逐格 cellEquals 直比) */
205
+ function gridRowEquals(a, b, row) {
206
+ for (let col = 0; col < b.columns; col++) {
207
+ if (!cellEquals(a.getCell(row, col), b.getCell(row, col)))
208
+ return false;
209
+ }
210
+ return true;
211
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * 主屏 inline 形态编舞件(07 §4.1「屏幕模型双形态」主屏条——批 10e)。
3
+ *
4
+ * 物理形态(与副屏全屏网格分立、共用 cell/diff/width 基座——07 篇定形):
5
+ * - **DECSTBM 滚动区**承载正文(顶行到固定区上方):区底写满自动滚、
6
+ * 滚出视口整行交终端原生 scrollback(历史真相在 durable 事件日志,行集
7
+ * 是帽内最近段的内存上限语义);
8
+ * - **固定区钉屏底**(区外行不被滚动卷入、绝对位可知):输入框 + 状态行,
9
+ * 行级差分重画(变行整行重写、未变行零写出——擦除只走 EL 禁空格填充);
10
+ * - **正文块直写**:块 → 行序列化 → 追加写(追加不回改——直播路「消息事件
11
+ * 唯一渲染源」的物理形);流式槽原位换装是视口内唯一差分。序列化单源走
12
+ * renderBlockLines(批 10f-4 提取——件 8 回看器全量档复用同一渲染管线,
13
+ * 零第二渲染器)。
14
+ *
15
+ * 编舞状态模型:**绝对行跟踪**。件内维护光标绝对行 cursorRow 与 durable
16
+ * 末行 durableEndRow(= 下一条追加的落笔行 = 流式槽首行),每次写行经
17
+ * writeLine 单点维护(区底 LF 触滚钉区底——滚动量不外显);present 尾光标
18
+ * 归固定区末行行首(呈现不变式——编舞内相对定位全由此出发;固定区自身用
19
+ * 绝对 CUP,钉屏底位可知)。
20
+ *
21
+ * v1 已知边界:流式槽 partial 回缩(新行数少于旧)按余行 EL 清——inline
22
+ * 终端无删行机制;固定区高度变化时光标账按区底钳制(内容可能被固定区
23
+ * 覆盖——装配宜随高度变化触发 repaint 重建)。
24
+ */
25
+ import { CellGrid } from '../../engine/index.js';
26
+ import { CLEAR_SCREEN, CR, cud, cuu, EL_TO_EOL, LF, renderFixedRegionDiff, setScrollRegion, cup } from './ansi-rows.js';
27
+ import { renderBlockLines } from './transcript.js';
28
+ /** 缺省几何(真值以 io.size() 为准——resize 路装配调 handleResize) */
29
+ const DEFAULT_COLUMNS = 80;
30
+ const DEFAULT_ROWS = 24;
31
+ /**
32
+ * 主屏编舞:正文直写 + 槽换装 + 固定区差分。无自驱时钟(请求合并归装配),
33
+ * 所有公开方法同步产写出(测试经 MemoryTerminalIO 收帧断言)。
34
+ */
35
+ export class MainScreen {
36
+ io;
37
+ columns = DEFAULT_COLUMNS;
38
+ rows = DEFAULT_ROWS;
39
+ fixedHeight;
40
+ /** 已直写 durable 块数(增量追加起点——repaint 归零全量重写) */
41
+ writtenBlocks = 0;
42
+ /** 光标绝对行(0 基——写行/定位单点维护) */
43
+ cursorRow = 0;
44
+ /** durable 末行(追加落笔行 = 槽首行;滚动时钉区底) */
45
+ durableEndRow = 0;
46
+ /** 当前槽直写行数(余行清除上界依据) */
47
+ slotLineCount = 0;
48
+ /** 固定区网格(装配画好交入——null = 固定区未装配只归位光标) */
49
+ fixedGrid = null;
50
+ /** 固定区上次呈现网格(行级差分基准;null = 全量重画) */
51
+ prevFixed = null;
52
+ constructor(io, options) {
53
+ this.io = io;
54
+ this.fixedHeight = options.fixedHeight;
55
+ const size = io.size();
56
+ this.columns = size.columns;
57
+ this.rows = size.rows;
58
+ }
59
+ /** 几何观测(装配层 resize 编舞消费) */
60
+ get geometry() {
61
+ return { columns: this.columns, rows: this.rows };
62
+ }
63
+ /** 启动编舞:清屏 + 滚动区确立 + 光标归位(呈现不变式自洽) */
64
+ start() {
65
+ this.io.write(CLEAR_SCREEN);
66
+ this.applyScrollRegion();
67
+ }
68
+ /**
69
+ * 增量呈现(追加块直写 → 槽换装 → 余行清除 → 固定区差分)。
70
+ * 前置:光标在固定区末行行首(start / setFixed / 上次 present 归位)。
71
+ */
72
+ present(blocks) {
73
+ // 末块为 streaming 时即流式槽(const 绑定经 kind 判别收窄)
74
+ const last = blocks.length > 0 ? blocks[blocks.length - 1] : null;
75
+ const slot = last !== null && last.kind === 'streaming' ? last : null;
76
+ const durableCount = slot === null ? blocks.length : blocks.length - 1;
77
+ // 余行清除上界:上次呈现的槽末行(= durable 末 + 槽行数 - 1——本帧前的账)
78
+ const prevBottomRow = Math.min(this.rows - this.fixedHeight - 1, this.durableEndRow + this.slotLineCount - 1);
79
+ // 光标归 durable 末(B/C 共同起点——无新增块时 C 段也从这里起笔)
80
+ this.gotoRow(this.durableEndRow);
81
+ // B. 追加块直写(每行 CR 起笔 + LF 推进,区底触滚交 scrollback)
82
+ if (durableCount > this.writtenBlocks) {
83
+ for (let i = this.writtenBlocks; i < durableCount; i++) {
84
+ this.writeBlockLines(blocks[i]);
85
+ }
86
+ this.writtenBlocks = durableCount;
87
+ this.durableEndRow = this.cursorRow;
88
+ }
89
+ // C. 槽换装(光标已在槽首 = durable 末;partial 是完整快照——逐行整写)
90
+ const slotLines = slot === null ? [] : this.renderSlotLines(slot);
91
+ for (const line of slotLines) {
92
+ this.writeLine(line);
93
+ }
94
+ // D. 余行清除(新槽末到上次槽末之间的 stale 行——EL 擦除禁空格填充)
95
+ const fixedTop = this.rows - this.fixedHeight;
96
+ const clearEnd = Math.min(fixedTop - 1, prevBottomRow);
97
+ for (let row = this.cursorRow; row <= clearEnd; row++) {
98
+ this.io.write((row > this.cursorRow ? cud(1) : '') + CR + EL_TO_EOL);
99
+ }
100
+ // E. 固定区差分重画 + 光标归位(呈现不变式)
101
+ this.redrawFixed();
102
+ this.slotLineCount = slotLines.length;
103
+ }
104
+ /**
105
+ * 瞬时行直写(非聚焦摘要行 / notify 通知行——不进行集不记 writtenBlocks,
106
+ * repaint 不重建)。编舞同追加块:归 durable 末 → 写行 → 余行清除 → 归位。
107
+ * 行内容由调用方(TuiBackend)序列化——本件纯编舞不做着色决策。
108
+ */
109
+ appendTransient(lines) {
110
+ if (lines.length === 0)
111
+ return;
112
+ this.gotoRow(this.durableEndRow);
113
+ for (const line of lines) {
114
+ this.writeLine(line);
115
+ }
116
+ this.durableEndRow = this.cursorRow;
117
+ const fixedTop = this.rows - this.fixedHeight;
118
+ const clearEnd = Math.min(fixedTop - 1, this.durableEndRow + this.slotLineCount - 1);
119
+ for (let row = this.cursorRow; row <= clearEnd; row++) {
120
+ this.io.write((row > this.cursorRow ? cud(1) : '') + CR + EL_TO_EOL);
121
+ }
122
+ this.redrawFixed();
123
+ }
124
+ /** 固定区网格更新(装配直呼——行级差分;几何变化时滚动区重设 + 差分基准失效) */
125
+ setFixed(grid) {
126
+ if (grid.rows !== this.fixedHeight) {
127
+ this.fixedHeight = grid.rows;
128
+ this.applyScrollRegion();
129
+ this.prevFixed = null; // 高度变化——差分基准失效全量重画;durable 末行按区底钳
130
+ this.durableEndRow = Math.min(this.durableEndRow, this.rows - this.fixedHeight - 1);
131
+ this.slotLineCount = 0;
132
+ }
133
+ this.fixedGrid = grid;
134
+ this.redrawFixed();
135
+ }
136
+ /** repaint(焦点切换):清屏 + 行集全量重写(投影即真相——scrollback 旧内容不参与) */
137
+ repaint(blocks) {
138
+ this.writtenBlocks = 0;
139
+ this.slotLineCount = 0;
140
+ this.durableEndRow = 0;
141
+ this.io.write(CLEAR_SCREEN);
142
+ this.applyScrollRegion();
143
+ this.present(blocks);
144
+ }
145
+ /** resize 编舞:几何重取 + 滚动区重设 + 全量重画(弃旧换新——宽字符 reflow 必失真) */
146
+ handleResize(blocks) {
147
+ const size = this.io.size();
148
+ this.columns = size.columns;
149
+ this.rows = size.rows;
150
+ this.repaint(blocks);
151
+ }
152
+ /* ---------------- 内部编舞 ---------------- */
153
+ /** 滚动区确立/重设(DECSTBM 规范行为是光标归 home——随后显式归位呈现不变式位) */
154
+ applyScrollRegion() {
155
+ const bottom = Math.max(1, this.rows - this.fixedHeight);
156
+ this.io.write(setScrollRegion(bottom));
157
+ this.io.write(cup(this.rows - 1, 0));
158
+ this.cursorRow = this.rows - 1;
159
+ }
160
+ /** 写一行(CR 起笔 + LF 推进)并维护 cursorRow;区底 LF 触滚——内容整体上移一格、durableEndRow 同步减一(账不漂移) */
161
+ writeLine(text) {
162
+ this.io.write(CR + text + LF);
163
+ const regionBottom = this.rows - this.fixedHeight - 1;
164
+ if (this.cursorRow < regionBottom)
165
+ this.cursorRow++;
166
+ else if (this.durableEndRow > 0)
167
+ this.durableEndRow--; // 触滚:已写内容上移(B 段随后整账重赋、槽写路保持真值)
168
+ }
169
+ /** 光标归行(present 入口光标在固定区末行——CUU 相对定位即可达全屏任意行) */
170
+ gotoRow(row) {
171
+ const distance = this.cursorRow - row;
172
+ if (distance > 0)
173
+ this.io.write(cuu(distance));
174
+ else if (distance < 0)
175
+ this.io.write(cud(-distance));
176
+ this.cursorRow = row;
177
+ }
178
+ /** 固定区差分重画 + 光标落位(编辑光标外显的物理位) */
179
+ redrawFixed() {
180
+ if (this.fixedGrid !== null) {
181
+ const baseRow = this.rows - this.fixedGrid.rows;
182
+ this.io.write(renderFixedRegionDiff(this.prevFixed, this.fixedGrid, baseRow));
183
+ this.prevFixed = this.fixedGrid;
184
+ // 光标声明位落位(EditorView 编辑位经 setCursor 声明)——绝对 CUP 且
185
+ // 行账同步(gotoRow 相对定位数学依赖 cursorRow 真值);无声明回退
186
+ // 屏底行首(呈现不变式原样)
187
+ const declared = this.fixedGrid.cursor;
188
+ if (declared !== null) {
189
+ const row = Math.min(baseRow + declared.row, this.rows - 1); // 越界声明防御钳屏底
190
+ this.io.write(cup(row, declared.col));
191
+ this.cursorRow = row;
192
+ return;
193
+ }
194
+ }
195
+ this.io.write(cup(this.rows - 1, 0));
196
+ this.cursorRow = this.rows - 1;
197
+ }
198
+ /** 块 → 行序列化直写(序列化单源 renderBlockLines——件 8 回看器复用同一管线,批 10f-4) */
199
+ writeBlockLines(block) {
200
+ for (const line of renderBlockLines(block, this.columns))
201
+ this.writeLine(line);
202
+ }
203
+ /** 流式槽行(纯文本直推——性能:不走网格不走样式;序列化同源 renderBlockLines) */
204
+ renderSlotLines(slot) {
205
+ return renderBlockLines(slot, this.columns);
206
+ }
207
+ }
@@ -0,0 +1,91 @@
1
+ /** 保活重发周期(ms——pi-tui TERMINAL_PROGRESS_KEEPALIVE_MS 实证值) */
2
+ export const OSC_PROGRESS_KEEPALIVE_MS = 1000;
3
+ /** OSC 9;4 忙态序列(indeterminate progress——state 3) */
4
+ const PROGRESS_ACTIVE_SEQUENCE = '\x1b]9;4;3\x07';
5
+ /** OSC 9;4 清零序列(state 0) */
6
+ const PROGRESS_CLEAR_SEQUENCE = '\x1b]9;4;0\x07';
7
+ /**
8
+ * OSC 52 选区复制序列(07 §4.1 呈现面件 8 细则——BEL 终界):
9
+ * `\x1b]52;c;<base64>\x07`,`c` = 系统剪贴板主位。终端支持不可探测(无标准
10
+ * 应答——kitty/Alacritty/wezterm/foot 支持、iTerm2 默认禁需用户开、tmux 需
11
+ * set-clipboard on、xterm 兼容族受 allowWindowOps 门控)——尽力写出无反馈,
12
+ * 不构成故障面。写出柄归装配层注入(onCopy),本函数只铸字节。
13
+ */
14
+ export function buildOsc52Copy(text) {
15
+ return `\x1b]52;c;${Buffer.from(text, 'utf8').toString('base64')}\x07`;
16
+ }
17
+ /**
18
+ * 终端外显件:OSC title + 进度态两写点的最小自持件。
19
+ * 忙态期间保活自重排(触发即清位再续针——armTick 同形);restore/停针后
20
+ * 句柄位归 null,重复停针是 no-op。
21
+ */
22
+ export class OscDisplay {
23
+ /** title 值缓存(null = 未写过——同值不重写判据位) */
24
+ lastTitle = null;
25
+ /** 保活在飞句柄(null = 无在飞——名册语义判据位) */
26
+ keepaliveHandle = null;
27
+ io;
28
+ baseline;
29
+ schedule;
30
+ cancel;
31
+ constructor(io, options) {
32
+ this.io = io;
33
+ this.baseline = options.baseline;
34
+ this.schedule = options.schedule ?? null;
35
+ // 取消缺省兜底 setTimeout 句柄族(与 TuiBackend cancelFn 缺省同形)
36
+ this.cancel = options.cancel ?? ((handle) => clearTimeout(handle));
37
+ }
38
+ /** 在飞保活句柄现值(null = 无在飞——名册摘除/测试断言位) */
39
+ get activeKeepalive() {
40
+ return this.keepaliveHandle;
41
+ }
42
+ /** 标题写出(OSC 0;… BEL——值缓存去重:同值不重写字节) */
43
+ setTitle(title) {
44
+ if (title === this.lastTitle)
45
+ return; // 去重律——同值零字节
46
+ this.lastTitle = title;
47
+ this.io.write(`\x1b]0;${title}\x07`);
48
+ }
49
+ /**
50
+ * 进度态写出(OSC 9;4):
51
+ * - true = 写忙态序列 + 起 1s 保活周期重发(已在飞不重复起针——重发维持
52
+ * 单链不叠针);同值重复调用仍重写忙态序列(去重不适用于 progress);
53
+ * - false = 写清零序列 + 停保活 timer。
54
+ */
55
+ setProgress(active) {
56
+ if (active) {
57
+ this.io.write(PROGRESS_ACTIVE_SEQUENCE);
58
+ this.armKeepalive();
59
+ }
60
+ else {
61
+ this.stopKeepalive();
62
+ this.io.write(PROGRESS_CLEAR_SEQUENCE);
63
+ }
64
+ }
65
+ /**
66
+ * 退出复原两写点齐(07 §4.1 件 7 条款):title 复原基线(经值缓存——
67
+ * 已在基线则零字节)+ 进度清零(无条件写——终态收口,含停保活)。
68
+ */
69
+ restore() {
70
+ this.stopKeepalive();
71
+ this.setTitle(this.baseline);
72
+ this.io.write(PROGRESS_CLEAR_SEQUENCE);
73
+ }
74
+ /** 停保活定时器(名册语义:句柄非空才取消、停后清位——重复停针 no-op) */
75
+ stopKeepalive() {
76
+ if (this.keepaliveHandle === null)
77
+ return;
78
+ this.cancel(this.keepaliveHandle);
79
+ this.keepaliveHandle = null;
80
+ }
81
+ /** 起保活(schedule 缺席 = 缺位;触发后清位自重排续针——周期重发维持) */
82
+ armKeepalive() {
83
+ if (this.schedule === null || this.keepaliveHandle !== null)
84
+ return;
85
+ this.keepaliveHandle = this.schedule(() => {
86
+ this.keepaliveHandle = null;
87
+ this.io.write(PROGRESS_ACTIVE_SEQUENCE); // 周期重发(超时衰减型终端维持)
88
+ this.armKeepalive();
89
+ }, OSC_PROGRESS_KEEPALIVE_MS);
90
+ }
91
+ }