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,169 @@
1
+ /**
2
+ * channels 件聚合工厂(L4 宿主固定件——02 篇席 10;批 10a 通道核)。
3
+ *
4
+ * 组合四件:SessionChannels(多会话注册/焦点/重画)+ CommandRegistry(命令面)
5
+ * + UiCore(七原语分发核)+ AskQueue(提问队列)。后端(UiBackend)经
6
+ * addBackend 注入——TUI 实装批接线 pi-tui、webui 件同面接入;核零 pi-tui
7
+ * import(件分两面定形——07 §4.1 channels 纵切批注记)。
8
+ *
9
+ * host 装配序(批 12):createChannels → addBackend(tui) → registerSession/
10
+ * focus(TUI 启动会话策略——07 §5)→ conversation 侧 per-run sink 汇入 emit。
11
+ */
12
+ import { BaseError } from '../contracts/index.js';
13
+ import { CommandRegistry } from './commands.js';
14
+ import { AskQueue } from './ask-queue.js';
15
+ import { UiCore } from './ui-core.js';
16
+ import { SessionChannels } from './registry.js';
17
+ /** 通道核工厂(投影类型 TProjection 由装配侧钉——不 import session 的边表执法) */
18
+ export function createChannels(opts = {}) {
19
+ const backends = [];
20
+ // 插件域后端集合(03 §2.7 分域律——U3 批 U3-3):upsert/撞名/disposer 全只
21
+ // 在本域内匹配,宿主域结构性不可及。插件实装形 = UiBackend<never>——投影
22
+ // 泛型宿主装配钉入,插件后端走信封驱动呈现、不参与投影重画(03 §2.2
23
+ // registerUiBackend 定形注记;SDK/webui 后端同形先例)
24
+ const pluginBackends = [];
25
+ // 全量扇出视图(宿主域在前、插件域在后——注册序内稳定):插件域 never 形
26
+ // 经方法双变结构兼容并入 TProjection 序列(onRepaint 等均方法语法声明);
27
+ // UiCore 持 never 形以保自身非泛型——边界处 as(单域时代同洞)
28
+ const allBackends = () => [...backends, ...pluginBackends];
29
+ const backendsForCore = () => allBackends();
30
+ const askQueue = new AskQueue();
31
+ const uiCore = new UiCore(backendsForCore, askQueue, opts.onApprovalAlways);
32
+ const commands = new CommandRegistry();
33
+ const registry = new SessionChannels(opts.fetchProjection, (sessionId, projection) => {
34
+ // repaint 扇出:带上该会话当前 widget 槽值(07 §4.3 单槽重放)——双域合流
35
+ const widget = uiCore.widgetOf(sessionId);
36
+ for (const b of allBackends())
37
+ b.onRepaint?.(sessionId, projection, widget);
38
+ });
39
+ // 件 8 /history 注册面(07 §4.1 条款:history() 注入在场即注册、缺席不注册
40
+ // 不虚报——真源 = 聚焦会话 → 拉全量 durable 正文投影 → 扇出后端 openHistory;
41
+ // 生产数据源接线归批 12 host 装配)
42
+ if (opts.history !== undefined) {
43
+ const fetchHistory = opts.history;
44
+ commands.register('history', async () => {
45
+ const sessionId = registry.focusedId;
46
+ if (sessionId === null)
47
+ return; // 焦点空悬——无回看对象(静默返回不虚报)
48
+ const messages = await fetchHistory(sessionId);
49
+ for (const b of allBackends())
50
+ b.openHistory?.(sessionId, messages);
51
+ }, '全屏回看会话历史');
52
+ }
53
+ // /memory 注册面(06 §7 形态定形注①——memory 注入在场即注册、缺席不注册
54
+ // 不虚报;同 /history 注册面律):管理面是全局面无会话归属,扇出后端
55
+ // openMemory(零参——材料后端自持);全体 falsy(后端不支持或件缺席)时
56
+ // notify 降级提示(warn 档——不静默假装已开);无观众后端时静默返回
57
+ // (notify 扇出无人接帧——与焦点空悬同族的不虚报位)
58
+ if (opts.memory !== undefined) {
59
+ commands.register('memory', async () => {
60
+ let opened = false;
61
+ for (const b of allBackends()) {
62
+ if (b.openMemory?.() === true)
63
+ opened = true; // 任一后端已开即成功(先开胜出)
64
+ }
65
+ if (!opened) {
66
+ // notify 非阻塞不分会话呈现位(service.notify 同律——uiCore 直扇出)
67
+ uiCore.notify('当前通道不支持记忆管理面(或 memory 件未装载)', { level: 'warn' });
68
+ }
69
+ }, '记忆管理面(冻结/忘掉/恢复/导出)');
70
+ }
71
+ return {
72
+ commands,
73
+ addBackend(backend) {
74
+ backends.push(backend);
75
+ },
76
+ removeBackend(id) {
77
+ const index = backends.findIndex((b) => b.id === id);
78
+ if (index !== -1)
79
+ backends.splice(index, 1);
80
+ },
81
+ registerPluginBackend(backend) {
82
+ // 分域律执法(03 §2.7):撞宿主域 id 顶替拒——比保留 id 名单更根治
83
+ // (名单漏一项即穿,分域无此面;07 §4 宿主后端恒在场条款承载)
84
+ if (backends.some((b) => b.id === backend.id)) {
85
+ throw new BaseError('CHANNEL_BACKEND_RESERVED', `后端 id「${backend.id}」是宿主域后端——插件结构性不可顶替(03 §2.7 分域律;07 §4 宿主后端恒在场),请换 id 注册`);
86
+ }
87
+ // 插件域内按 id 后写胜出(upsert 原位顶替——扇出序稳定不移尾;同
88
+ // registerCommand 换装哲学:同 id 顶替 = 显式换装后端正道)
89
+ const index = pluginBackends.findIndex((b) => b.id === backend.id);
90
+ if (index === -1)
91
+ pluginBackends.push(backend);
92
+ else
93
+ pluginBackends[index] = backend;
94
+ // disposer 三律②:仅当仍是本后端时摘除(indexOf 身份闸——同 id 后写
95
+ // 顶替后旧 disposer 调用为无操作)
96
+ return () => {
97
+ const at = pluginBackends.indexOf(backend);
98
+ if (at !== -1)
99
+ pluginBackends.splice(at, 1);
100
+ };
101
+ },
102
+ listPluginBackendIds() {
103
+ return pluginBackends.map((b) => b.id);
104
+ },
105
+ registerSession(sessionId) {
106
+ registry.registerSession(sessionId);
107
+ },
108
+ unregisterSession(sessionId) {
109
+ registry.unregisterSession(sessionId);
110
+ uiCore.closeSession(sessionId);
111
+ },
112
+ hasSession(sessionId) {
113
+ return registry.has(sessionId);
114
+ },
115
+ focus(sessionId) {
116
+ return registry.focus(sessionId);
117
+ },
118
+ isFocused(sessionId) {
119
+ return registry.isFocused(sessionId);
120
+ },
121
+ get focusedId() {
122
+ return registry.focusedId;
123
+ },
124
+ emit(env) {
125
+ const focused = registry.isFocused(env.sessionId);
126
+ // 信封路由双域合流扇出(聚焦全渲染/非聚焦摘要行——形态归后端)
127
+ for (const b of allBackends())
128
+ b.onEnvelope?.(env, focused);
129
+ },
130
+ notify(sessionId, message, notifyOpts) {
131
+ // notify 非阻塞不分会话呈现位——sessionId 位保留给呈现侧路由(后端自决)
132
+ void sessionId;
133
+ uiCore.notify(message, notifyOpts);
134
+ },
135
+ confirm(sessionId, message, askOpts) {
136
+ return uiCore.confirm(sessionId, message, askOpts);
137
+ },
138
+ select(sessionId, message, choices, askOpts) {
139
+ return uiCore.select(sessionId, message, choices, askOpts);
140
+ },
141
+ input(sessionId, message, askOpts) {
142
+ return uiCore.input(sessionId, message, askOpts);
143
+ },
144
+ askApproval(sessionId, request, askOpts) {
145
+ return uiCore.askApproval(sessionId, request, askOpts);
146
+ },
147
+ setStatus(sessionId, status) {
148
+ uiCore.setStatus(sessionId, status);
149
+ },
150
+ setWidget(sessionId, node) {
151
+ uiCore.setWidget(sessionId, node);
152
+ },
153
+ hasAudience() {
154
+ return uiCore.hasAudience();
155
+ },
156
+ pendingAsks(sessionId) {
157
+ return uiCore.pending(sessionId);
158
+ },
159
+ registerCommand(name, handler, description) {
160
+ return commands.register(name, handler, description);
161
+ },
162
+ dispatchCommand(input, sessionId) {
163
+ return commands.dispatch(input, sessionId);
164
+ },
165
+ listCommands() {
166
+ return commands.list();
167
+ },
168
+ };
169
+ }
@@ -0,0 +1,42 @@
1
+ import { firstTokenRange, tokenAtCursor } from './token.js';
2
+ /** 三源合一补全器 */
3
+ export class CombinedAutocompleteProvider {
4
+ sources;
5
+ constructor(sources = {}) {
6
+ this.sources = sources;
7
+ }
8
+ /** 取补全(无 token / 无对应源 / 源空 → null——弹层不显) */
9
+ getCompletions(context) {
10
+ const line = context.lines[context.cursorLine] ?? '';
11
+ const token = tokenAtCursor(line, context.cursorCol);
12
+ if (token === null || token.text === '')
13
+ return null; // 无 token 段——不补
14
+ // 路由一:@ 前缀(任意位置——命令参数段内也可 mention 文件)
15
+ if (token.text.startsWith('@')) {
16
+ return this.collect(this.sources.mentions?.(token.text.slice(1)), token.start, token.end);
17
+ }
18
+ // 命令段判据只在首逻辑行(多行输入的斜杠命令 = 首行起手式)
19
+ if (context.cursorLine !== 0)
20
+ return null;
21
+ const first = firstTokenRange(line);
22
+ if (first === null)
23
+ return null; // 行首无 token(光标 token 不在行首矛盾——防御位)
24
+ if (token.start === first.start) {
25
+ // 路由二:光标就在首 token 内且 / 前缀——命令名段
26
+ if (!token.text.startsWith('/'))
27
+ return null; // 首 token 非命令——普通文本不补
28
+ return this.collect(this.sources.commands?.(token.text.slice(1)), token.start, token.end);
29
+ }
30
+ // 路由三:首 token 是已终结命令(/xxx)——光标 token 属参数段
31
+ const firstText = line.slice(first.start, first.end);
32
+ if (!firstText.startsWith('/'))
33
+ return null; // 行首非命令——后续 token 也不补
34
+ return this.collect(this.sources.commandArguments?.(firstText.slice(1), token.text), token.start, token.end);
35
+ }
36
+ /** 源结果收口(空源 / 空条目统一 null) */
37
+ collect(items, replaceStart, replaceEnd) {
38
+ if (items === undefined || items.length === 0)
39
+ return null;
40
+ return { items, replaceStart, replaceEnd };
41
+ }
42
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @ 文件段补全源(07 §4.1 补全三合一第三源——mentions 源的实机行走件)。
3
+ *
4
+ * 语义承 berry 蓝本 getFileSuggestions 的 v1 子集:
5
+ * - **目录列举 + 前缀过滤**(非模糊行走——fuzzy 全库发现随 fd 批挂账,
6
+ * `BERRY_AGENT_FD_PATH` 环境变量是其消费点、本批不触);
7
+ * - 路径段解析:`src/ap` → 列 `src` 滤 `ap`;尾 `/` = 列该目录;相对段
8
+ * 对 basePath(装配注入 cwd)、`~/` 展开家目录、`/` 起绝对位列举;
9
+ * - `.git` 跳过(版本库内脏);符号链指目录归类为目录(label 尾 `/`
10
+ * 续深——断链/不可及跳过);
11
+ * - 排序:目录优先、再 label 字典序;条目帽防大目录全量;
12
+ * - **引号形**(provider 协议条款):路径含空白或前缀已引号 → replacement
13
+ * 以 `@"…"` 包裹防尾空格击穿(tokenAtCursor 引号感知整 token 的续补位)。
14
+ *
15
+ * 件内零缓存(同一目录反复列举随实机批定 cache 策略);同步 IO——
16
+ * mentions 源协议面是同步(单目录列举毫秒级,弹层窗口 10 行消费帽内)。
17
+ */
18
+ import { readdirSync, statSync } from 'node:fs';
19
+ import { homedir } from 'node:os';
20
+ import * as path from 'node:path';
21
+ /** 条目帽缺省(源侧规模锁——弹层自身另有窗口滚动帽) */
22
+ const DEFAULT_MAX_ITEMS = 50;
23
+ /** 引号前缀解析(`"my f` / `"my file.txt"` → 内文;`~/` 形原样) */
24
+ function parseQuotedPrefix(query) {
25
+ const head = query[0];
26
+ if (head === '"' || head === "'") {
27
+ let inner = query.slice(1);
28
+ if (inner.endsWith(head))
29
+ inner = inner.slice(0, -1); // 已敲闭引号——内文为准
30
+ return { rawPrefix: inner, isQuoted: true };
31
+ }
32
+ return { rawPrefix: query, isQuoted: false };
33
+ }
34
+ /** @ 文件段补全源:`mentions` 源协议面的实机适配(注入 autocomplete.mentions) */
35
+ export class FileMentionSource {
36
+ basePath;
37
+ maxItems;
38
+ constructor(options) {
39
+ this.basePath = options.basePath;
40
+ this.maxItems = options.maxItems ?? DEFAULT_MAX_ITEMS;
41
+ }
42
+ /** 源适配面(query = 去 @ 前缀的 token 内文——可能带引号头) */
43
+ get = (query) => {
44
+ const { rawPrefix, isQuoted } = parseQuotedPrefix(query);
45
+ // '~' 裸形与 '~/' 同为家目录根列举(filePart 空)
46
+ const isHomeRoot = rawPrefix === '~' || rawPrefix === '~/';
47
+ const slash = rawPrefix.lastIndexOf('/');
48
+ const dirPart = isHomeRoot ? '~/' : slash >= 0 ? rawPrefix.slice(0, slash + 1) : '';
49
+ const filePart = isHomeRoot ? '' : slash >= 0 ? rawPrefix.slice(slash + 1) : rawPrefix;
50
+ const searchDir = this.resolveSearchDir(dirPart);
51
+ if (searchDir === null)
52
+ return []; // 不可解析形态
53
+ let entries;
54
+ try {
55
+ entries = readdirSync(searchDir, { withFileTypes: true });
56
+ }
57
+ catch {
58
+ return []; // 缺目录 / 不可及——空集(补全静默退场)
59
+ }
60
+ const lowerPrefix = filePart.toLowerCase();
61
+ const items = [];
62
+ for (const entry of entries) {
63
+ if (entry.name === '.git')
64
+ continue; // 版本库内脏
65
+ if (!entry.name.toLowerCase().startsWith(lowerPrefix))
66
+ continue;
67
+ // 目录归类:符号链补一次 stat(断链/竞态不可及——跳过)
68
+ let isDirectory = entry.isDirectory();
69
+ if (!isDirectory && entry.isSymbolicLink()) {
70
+ try {
71
+ isDirectory = statSync(path.join(searchDir, entry.name)).isDirectory();
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ }
77
+ // replacement 保用户敲的 dirPart 形(~/ 与绝对形原样续接)
78
+ const pathValue = `${dirPart}${entry.name}${isDirectory ? '/' : ''}`;
79
+ items.push({
80
+ label: `${entry.name}${isDirectory ? '/' : ''}`,
81
+ replacement: `@${isQuoted || /\s/.test(pathValue) ? `"${pathValue}"` : pathValue}`,
82
+ });
83
+ }
84
+ // 目录优先、再 label 字典序(承 berry 排序语义)
85
+ items.sort((a, b) => {
86
+ const aDir = a.label.endsWith('/');
87
+ const bDir = b.label.endsWith('/');
88
+ if (aDir !== bDir)
89
+ return aDir ? -1 : 1;
90
+ return a.label.localeCompare(b.label);
91
+ });
92
+ return items.slice(0, this.maxItems);
93
+ };
94
+ /** 列举目录解析(相对段对 basePath;~/ 家目录;/ 起绝对位) */
95
+ resolveSearchDir(dirPart) {
96
+ if (dirPart === '' || dirPart === './')
97
+ return path.resolve(this.basePath, dirPart);
98
+ if (dirPart === '~/')
99
+ return homedir();
100
+ if (dirPart.startsWith('~/'))
101
+ return path.resolve(homedir(), dirPart.slice(2));
102
+ if (dirPart.startsWith('/'))
103
+ return path.resolve(dirPart);
104
+ return path.resolve(this.basePath, dirPart);
105
+ }
106
+ }
@@ -0,0 +1,148 @@
1
+ import { ansiColor } from '../../engine/index.js';
2
+ import { ACCENT_INDEX } from '../theme.js';
3
+ import { prefixDisplayWidth } from '../editor/visual-lines.js';
4
+ /** 弹层可见条目帽(超出窗口滚动跟随高亮) */
5
+ const MAX_VISIBLE_ITEMS = 10;
6
+ /** 高亮行样式(整行反色——与 SelectPanel 同视觉语言) */
7
+ const ACTIVE_STYLE = Object.freeze({ inverse: true });
8
+ /** 补充说明段样式(dim) */
9
+ const DETAIL_STYLE = Object.freeze({ dim: true });
10
+ /** 无候选提示样式(accent——theme 单源) */
11
+ const EMPTY_STYLE = Object.freeze({ fg: ansiColor(ACCENT_INDEX) });
12
+ /** 弹层件:provider 只读消费 + 模型代换原语调用 */
13
+ export class AutocompletePopup {
14
+ result = null;
15
+ activeIndex = 0;
16
+ windowStart = 0;
17
+ provider;
18
+ model;
19
+ constructor(provider, model) {
20
+ this.provider = provider;
21
+ this.model = model;
22
+ }
23
+ /** 弹层在场态(无补全不显) */
24
+ get visible() {
25
+ return this.result !== null;
26
+ }
27
+ /** 高亮下标(测试观测面) */
28
+ get activeItemIndex() {
29
+ return this.activeIndex;
30
+ }
31
+ /** 重取补全(每次输入变更后装配调——无补全自然隐藏) */
32
+ refresh() {
33
+ const cursor = this.model.getCursor();
34
+ this.result = this.provider.getCompletions({
35
+ lines: this.model.getLines(),
36
+ cursorLine: cursor.line,
37
+ cursorCol: cursor.col,
38
+ });
39
+ this.activeIndex = 0;
40
+ this.windowStart = 0;
41
+ }
42
+ /** 量高:可见条目数(不可见 = 0——浮层不占布局) */
43
+ measure(width) {
44
+ void width;
45
+ const count = this.result?.items.length ?? 0;
46
+ return Math.min(count, MAX_VISIBLE_ITEMS);
47
+ }
48
+ /** 落位:铺底空格 → 可见窗条目行(高亮反色 + 说明右对齐) */
49
+ render(buffer, region) {
50
+ if (this.result === null)
51
+ return;
52
+ // 铺底空格(写格覆盖——未写格会透出主树文字)
53
+ for (let r = 0; r < region.height; r++) {
54
+ for (let c = 0; c < region.width; c++) {
55
+ buffer.setCell(region.row + r, region.col + c, ' ');
56
+ }
57
+ }
58
+ const items = this.result.items;
59
+ if (items.length === 0) {
60
+ buffer.writeText(region.row, region.col, '无匹配', EMPTY_STYLE);
61
+ return;
62
+ }
63
+ const end = Math.min(items.length, this.windowStart + MAX_VISIBLE_ITEMS);
64
+ for (let i = this.windowStart; i < end; i++) {
65
+ const row = region.row + (i - this.windowStart);
66
+ const item = items[i];
67
+ const active = i === this.activeIndex;
68
+ const prefix = active ? '❯ ' : ' ';
69
+ buffer.writeText(row, region.col, `${prefix}${item.label}`, active ? ACTIVE_STYLE : undefined);
70
+ if (item.detail !== undefined && item.detail.length > 0) {
71
+ // 右对齐按显示宽(CJK 段宽 ≠ 码位数)
72
+ const detailCols = prefixDisplayWidth(item.detail, item.detail.length);
73
+ buffer.writeText(row, region.col + region.width - detailCols, item.detail, DETAIL_STYLE);
74
+ }
75
+ }
76
+ }
77
+ /** 事件分发(弹层可见才有意义):↑/↓ / enter / tab / escape,其余穿透 */
78
+ handleEvent(event) {
79
+ if (this.result === null)
80
+ return false; // 不在场不挡键
81
+ if (event.kind !== 'key')
82
+ return false; // 文本 / IME / 粘贴穿透回输入件
83
+ if (event.phase === 'release')
84
+ return false;
85
+ if (event.ctrl || event.alt || event.shift || event.meta)
86
+ return false; // 修饰组合穿透(编辑键面)
87
+ if (event.key === 'up') {
88
+ this.moveActive(-1);
89
+ return true;
90
+ }
91
+ if (event.key === 'down') {
92
+ this.moveActive(1);
93
+ return true;
94
+ }
95
+ if (event.key === 'enter' || event.key === 'tab') {
96
+ // 全量输入穿透律(2026-09-13 真模型五轮实测定罪修复):当前 token 已是
97
+ // 高亮项 replacement 全文时,enter 的「应用」是无净代换的空动作——吞键
98
+ // 会把完整输入的斜杠命令逼成「先应用再提交」双 enter 形(且第二次输入
99
+ // 与残留拼接进模型)。此处 enter 穿透回编辑器走提交语义;tab 专职填充
100
+ // 无提交歧义、恒应用(键面分离)。
101
+ if (event.key === 'enter' && this.selectionAlreadyTyped())
102
+ return false;
103
+ this.applySelection();
104
+ return true;
105
+ }
106
+ if (event.key === 'escape') {
107
+ this.result = null; // 关本轮(后续输入再 refresh 重开)
108
+ return true;
109
+ }
110
+ return false; // 其余键穿透(backspace / 字符等继续编辑——弹层随下次 refresh 重算)
111
+ }
112
+ /** 高亮循环移动 + 窗口跟随(10 行帽外的条目滚动入窗) */
113
+ moveActive(delta) {
114
+ const count = this.result?.items.length ?? 0;
115
+ if (count === 0)
116
+ return;
117
+ this.activeIndex = (this.activeIndex + delta + count) % count;
118
+ if (this.activeIndex < this.windowStart)
119
+ this.windowStart = this.activeIndex;
120
+ if (this.activeIndex >= this.windowStart + MAX_VISIBLE_ITEMS) {
121
+ this.windowStart = this.activeIndex - MAX_VISIBLE_ITEMS + 1;
122
+ }
123
+ }
124
+ /** 应用选中项:整 token 代换(模型原语——光标落代换尾) */
125
+ applySelection() {
126
+ const result = this.result;
127
+ if (result === null || result.items.length === 0)
128
+ return;
129
+ const item = result.items[this.activeIndex];
130
+ this.result = null; // 应用即隐(先隐后代换——代换的 notify 可能再触发 refresh 重开,属新轮)
131
+ const cursor = this.model.getCursor();
132
+ this.model.replaceToken(cursor.line, result.replaceStart, result.replaceEnd, item.replacement);
133
+ }
134
+ /**
135
+ * 全量输入判定:光标 token 现文本 === 高亮项 replacement 全文(弹层可见期
136
+ * 间输入变更必经 refresh 重开——本判定取值恒新鲜,无陈旧区间风险)。
137
+ */
138
+ selectionAlreadyTyped() {
139
+ const result = this.result;
140
+ if (result === null || result.items.length === 0)
141
+ return false;
142
+ const item = result.items[this.activeIndex];
143
+ const line = this.model.getLines()[this.model.getCursor().line];
144
+ if (line === undefined)
145
+ return false;
146
+ return line.slice(result.replaceStart, result.replaceEnd) === item.replacement;
147
+ }
148
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 补全协议件(07 §4.1 引擎节件 6(组件与呈现装配件)):多行坐标协议 + 候选条目 + 取补全接口。
3
+ *
4
+ * 多行坐标协议(承 berry @-mention 协议签名):上下文带 lines/cursorLine/
5
+ * cursorCol——按多行输入件坐标定位光标 token,弹层与源都只认本协议。
6
+ */
7
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * token 判据件(07 §4.1 引擎节件 6(组件与呈现装配件) 补全段):光标 token 提取(引号感知)
3
+ * 与行首 token 区间。
4
+ *
5
+ * 引号感知:token 边界 = 最近的**未引用空白**——从行首正向扫描推进引号
6
+ * 开闭态(引号内空白不断 token——@"my file" 形整段为一个 token);引号
7
+ * 字符本身属 token(代换区间含引号)。
8
+ * 已知边界:未闭合引号一路吸到引号头(输入中间态本就未闭合,可接受)。
9
+ */
10
+ /** 光标前 token 提取(col === 0 或紧邻空白 → null) */
11
+ export function tokenAtCursor(line, col) {
12
+ if (col <= 0 || col > line.length)
13
+ return null;
14
+ // 正向扫描(引号态须从行首推进——反向扫遇空白先于遇引号头,无法判定
15
+ // 该空白是否被右侧引号包住)
16
+ let start = 0;
17
+ let quote = null;
18
+ for (let i = 0; i < col; i++) {
19
+ const ch = line[i];
20
+ if (quote !== null) {
21
+ if (ch === quote)
22
+ quote = null; // 引号闭
23
+ continue; // 引号内字符(含空白)不断 token
24
+ }
25
+ if (ch === '"' || ch === "'") {
26
+ quote = ch; // 引号开
27
+ continue;
28
+ }
29
+ if (ch === ' ' || ch === '\t')
30
+ start = i + 1; // 未引用空白——token 边界
31
+ }
32
+ const text = line.slice(start, col);
33
+ if (text === '')
34
+ return null; // 紧邻空白——无 token
35
+ return { start, end: col, text };
36
+ }
37
+ /** 行首 token 区间(引号感知;空白行 / 空串 → null) */
38
+ export function firstTokenRange(line) {
39
+ if (line === '')
40
+ return null;
41
+ let start = 0;
42
+ // 跳过行首空白(首 token 起点后移)
43
+ while (start < line.length && (line[start] === ' ' || line[start] === '\t'))
44
+ start++;
45
+ if (start >= line.length)
46
+ return null; // 全空白行
47
+ // 扫到未引用空白为终(与 tokenAtCursor 同边界判据)
48
+ let end = start;
49
+ let quote = null;
50
+ for (let i = start; i < line.length; i++) {
51
+ const ch = line[i];
52
+ if (quote !== null) {
53
+ if (ch === quote)
54
+ quote = null;
55
+ end = i + 1;
56
+ continue;
57
+ }
58
+ if (ch === '"' || ch === "'") {
59
+ quote = ch;
60
+ end = i + 1;
61
+ continue;
62
+ }
63
+ if (ch === ' ' || ch === '\t')
64
+ break;
65
+ end = i + 1;
66
+ }
67
+ return { start, end };
68
+ }