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,225 @@
1
+ /** 树判定上溯帽深(防环/防长链——超帽 = 不可判 = 跨树 fail-closed) */
2
+ const LINEAGE_MAX_DEPTH = 64;
3
+ /** session_list 进程内清单帽(装配侧投影帽之外的服务侧二道帽) */
4
+ const LIST_CAP = 100;
5
+ /** session_read 尾窗缺省条数与硬帽(03 §10.8 e-2 定形注) */
6
+ const TAIL_LIMIT_DEFAULT = 50;
7
+ const TAIL_LIMIT_MAX = 200;
8
+ /** session_trace 尾窗定值(「当前进行态」固定窗——无参数面) */
9
+ const TRACE_WINDOW = 20;
10
+ /**
11
+ * 尾条推导回扫窗(03 §10.8 obs-b 例外注):压缩族词(compaction/*)是
12
+ * run 收场后的后台任务审计面——落账不改变会话粗状态,尾条为压缩族词时
13
+ * 回扫窗内最近一条非压缩族事件按其映射定档。窗 20 条 = 防御帽(真实流
14
+ * 压缩族连发至多数条——fire 前提是 settled,连续压缩族词间必夹 turn 事件)。
15
+ */
16
+ const STATE_SCAN_WINDOW = 20;
17
+ /** 呈现摘要文本截断帽(单行 200 字符——模型消费面有界) */
18
+ const SUMMARY_MAX_CHARS = 200;
19
+ /** request/header 尾查页帽(initial/resume/change 少量词——防御帽) */
20
+ const HEADER_SCAN_LIMIT = 100;
21
+ /** createSessionView 工厂(装载面/测试唯一入口) */
22
+ export function createSessionView(deps) {
23
+ const workspaceRoot = deps.workspaceRoot ?? (() => undefined);
24
+ /**
25
+ * 尾条推导(lastSeq 精确定位——fromSeq 含边界取 1 条即尾行)。obs-b 例外
26
+ * (03 §10.8):尾行为压缩族词时回扫窗内最近一条非压缩族事件——压缩审计
27
+ * 落账不改变粗状态(skip 的 cooldown/pending 门每轮收场可落,不回扫则
28
+ * idle 恒误报 running);尾行非压缩族时行为与例外前逐字一致(write-behind
29
+ * 滞后 → undefined → idle 的保守语义不变)。窗内全压缩族(防御极端形)→
30
+ * undefined 兜底 idle。
31
+ */
32
+ const tailEvent = (row) => {
33
+ if (row.lastSeq < 0)
34
+ return undefined; // 零事件会话
35
+ const exact = deps.events.queryEvents({ sessionId: row.id, fromSeq: row.lastSeq, limit: 1 });
36
+ const tail = exact.events.at(-1);
37
+ if (tail === undefined || !tail.type.startsWith('compaction/'))
38
+ return tail;
39
+ const fromSeq = Math.max(0, row.lastSeq - (STATE_SCAN_WINDOW - 1));
40
+ const window = deps.events.queryEvents({ sessionId: row.id, fromSeq, limit: STATE_SCAN_WINDOW });
41
+ for (let i = window.events.length - 1; i >= 0; i -= 1) {
42
+ if (!window.events[i].type.startsWith('compaction/'))
43
+ return window.events[i];
44
+ }
45
+ return undefined;
46
+ };
47
+ /** 粗状态映射(文件头映射表的执法位——paused 档 u-3 落码:置于「其余一切 running」兜底前〔03 §10.8 u-1 定形注〕,唤醒消息落账尾条翻位即恢复 running——零推导面特判) */
48
+ const deriveLiveState = (tail) => {
49
+ if (tail === undefined)
50
+ return 'idle';
51
+ if (tail.type === 'turn/end')
52
+ return 'idle';
53
+ if (tail.type === 'approval/asked')
54
+ return 'waiting-approval';
55
+ if (tail.type === 'session/paused')
56
+ return 'paused';
57
+ return 'running';
58
+ };
59
+ /** 会话行 + 尾条推导合并态(list/selfStatus/trace 共用) */
60
+ const derivedState = (sessionId) => {
61
+ const row = deps.sessions.getSessionRow(sessionId);
62
+ if (row === undefined)
63
+ return { row: undefined, live: 'idle' }; // 零事件新会话(进程内在管无 durable 行)
64
+ return { row, live: deriveLiveState(tailEvent(row)) };
65
+ };
66
+ /** model 尾查(最后一条 request/header 的 config.model——防御读取) */
67
+ const lastModel = (sessionId) => {
68
+ const result = deps.events.queryEvents({ sessionId, types: ['request/header'], limit: HEADER_SCAN_LIMIT });
69
+ for (let i = result.events.length - 1; i >= 0; i -= 1) {
70
+ const data = result.events[i].data;
71
+ const model = data && typeof data === 'object' ? data.config?.model : undefined;
72
+ if (typeof model === 'string' && model.length > 0)
73
+ return model;
74
+ }
75
+ return undefined;
76
+ };
77
+ /** 尾窗读(fromSeq 精确定位——升序取帽值条,零全表扫) */
78
+ const tailWindow = (row, count) => {
79
+ const from = Math.max(0, row.lastSeq - count + 1);
80
+ const result = deps.events.queryEvents({ sessionId: row.id, fromSeq: from, limit: count });
81
+ return result.events;
82
+ };
83
+ /** 单事件呈现摘要(事件型感知提取——text/name/reason 优先,兜底 JSON 截断) */
84
+ const summarize = (event) => {
85
+ const data = event.data;
86
+ if (data !== null && typeof data === 'object') {
87
+ const record = data;
88
+ // 消息类:content 块数组或裸文本——取文本块拼接
89
+ const content = record.content ?? record.text;
90
+ if (typeof content === 'string')
91
+ return clip(content);
92
+ if (Array.isArray(content)) {
93
+ const text = content
94
+ .map((block) => block !== null && typeof block === 'object' && typeof block.text === 'string'
95
+ ? block.text
96
+ : '')
97
+ .join(' ')
98
+ .trim();
99
+ if (text.length > 0)
100
+ return clip(text);
101
+ }
102
+ // 工具类:name 优先(tool/call 携工具名)
103
+ if (typeof record.name === 'string')
104
+ return clip(`name=${record.name}`);
105
+ // turn 边界:reason
106
+ if (typeof record.reason === 'string')
107
+ return clip(`reason=${record.reason}`);
108
+ }
109
+ return clip(safeJson(data));
110
+ };
111
+ const toTailItem = (event) => ({
112
+ seq: event.seq,
113
+ time: event.time,
114
+ type: event.type,
115
+ summary: summarize(event),
116
+ });
117
+ /** 进程内在管判定(exists 语义:进程内 ∪ durable 任一在场) */
118
+ const inActive = (sessionId) => deps.liveSessions.listActive().find((info) => info.sessionId === sessionId);
119
+ return {
120
+ listSessions() {
121
+ const active = deps.liveSessions.listActive().slice(0, LIST_CAP);
122
+ return active.map((info) => {
123
+ const { row, live } = derivedState(info.sessionId);
124
+ return {
125
+ id: info.sessionId,
126
+ // 行缺席(零事件新会话):origin 兜底进程内记录、updatedAt 兜底 0
127
+ title: row?.title,
128
+ origin: row?.origin ?? info.origin,
129
+ parentId: row?.parentId,
130
+ live,
131
+ model: row === undefined ? undefined : lastModel(info.sessionId),
132
+ updatedAt: row?.updatedAt ?? 0,
133
+ };
134
+ });
135
+ },
136
+ readTail(sessionId, opts) {
137
+ const limit = Math.min(Math.max(1, opts?.limit ?? TAIL_LIMIT_DEFAULT), TAIL_LIMIT_MAX);
138
+ const row = deps.sessions.getSessionRow(sessionId);
139
+ const exists = row !== undefined || inActive(sessionId) !== undefined;
140
+ if (row === undefined)
141
+ return { exists, items: [] }; // 零事件或不在场——空窗
142
+ return { exists, items: tailWindow(row, limit).map(toTailItem) };
143
+ },
144
+ trace(sessionId) {
145
+ const row = deps.sessions.getSessionRow(sessionId);
146
+ const exists = row !== undefined || inActive(sessionId) !== undefined;
147
+ if (row === undefined) {
148
+ return { sessionId, exists, live: 'idle', recent: [], inflightTools: [] };
149
+ }
150
+ const window = tailWindow(row, TRACE_WINDOW);
151
+ // 在飞工具 = 尾窗内 tool/call 无后续同 toolCallId 的 tool/result
152
+ // (call → result 配对收敛;result 按到达序覆盖——后见胜出)
153
+ const settled = new Set();
154
+ const calls = new Map(); // toolCallId → name
155
+ for (const event of window) {
156
+ const data = event.data;
157
+ const callId = data && typeof data === 'object' && typeof data.toolCallId === 'string' ? data.toolCallId : undefined;
158
+ if (event.type === 'tool/result') {
159
+ if (callId !== undefined)
160
+ settled.add(callId);
161
+ }
162
+ else if (event.type === 'tool/call') {
163
+ const name = data && typeof data === 'object' && typeof data.name === 'string' ? data.name : (callId ?? 'unknown');
164
+ if (callId !== undefined)
165
+ calls.set(callId, name);
166
+ }
167
+ }
168
+ const inflightTools = [...calls.entries()].filter(([callId]) => !settled.has(callId)).map(([, name]) => name);
169
+ return {
170
+ sessionId,
171
+ exists,
172
+ live: deriveLiveState(tailEvent(row)),
173
+ recent: window.map(toTailItem),
174
+ inflightTools,
175
+ };
176
+ },
177
+ selfStatus(sessionId) {
178
+ const { row, live } = derivedState(sessionId);
179
+ const info = inActive(sessionId);
180
+ return {
181
+ sessionId,
182
+ origin: row?.origin ?? info?.origin ?? 'conversation',
183
+ parentId: row?.parentId,
184
+ workspaceRoot: workspaceRoot(),
185
+ live,
186
+ model: row === undefined ? undefined : lastModel(sessionId),
187
+ };
188
+ },
189
+ isSameTree(a, b) {
190
+ if (a === b)
191
+ return true; // self 特例(同 id 天然含)
192
+ const rootA = rootOf(a);
193
+ const rootB = rootOf(b);
194
+ return rootA !== undefined && rootA === rootB;
195
+ },
196
+ };
197
+ /** 根求解(parent_id 链上溯——帽深 64;行缺席/链断/超帽 = undefined) */
198
+ function rootOf(sessionId) {
199
+ let current = sessionId;
200
+ for (let depth = 0; depth < LINEAGE_MAX_DEPTH; depth += 1) {
201
+ const row = deps.sessions.getSessionRow(current);
202
+ if (row === undefined)
203
+ return undefined; // 链中段缺席——不可判
204
+ if (row.parentId === undefined)
205
+ return row.id; // 无父 = 根
206
+ current = row.parentId;
207
+ }
208
+ return undefined; // 超帽——不可判(fail-closed 跨树)
209
+ }
210
+ }
211
+ /** 文本截断(含省略号——单行有界) */
212
+ function clip(text) {
213
+ const normalized = text.replace(/\s+/g, ' ').trim();
214
+ return normalized.length > SUMMARY_MAX_CHARS ? `${normalized.slice(0, SUMMARY_MAX_CHARS)}…` : normalized;
215
+ }
216
+ /** JSON 兜底摘要(坏形不炸——截断呈现) */
217
+ function safeJson(data) {
218
+ try {
219
+ const text = JSON.stringify(data) ?? String(data);
220
+ return text;
221
+ }
222
+ catch {
223
+ return '(unserializable)';
224
+ }
225
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * obs — obs_query 只读工具件(03 §10.8;fetch 工具同款薄包装先例)。
3
+ *
4
+ * 参数面 typebox schema(07 schema 层宿主件直用同律)+ 回执文本表格
5
+ * (usage 行主计费桶与 cache 分列——模型可见)。effect 'read':聚合查询
6
+ * 是模型读观测面动作,零写权。装载归批 12 装配面(createObsQueryTool
7
+ * 工厂——与 createFetchTool 同款接线位)。
8
+ */
9
+ import { BaseError } from '../contracts/index.js';
10
+ import { Type } from 'typebox';
11
+ /** 桶起点格式化(ISO UTC 整点/整日——存储客观、呈现换算的第一面) */
12
+ function formatBucket(bucket) {
13
+ return new Date(bucket).toISOString().replace('.000Z', 'Z');
14
+ }
15
+ /** 用量行文本(列对齐——主计费桶与 cache 桶分列 + 命中率派生列〔RP5〕) */
16
+ function usageLine(row) {
17
+ const main = row.input + row.output;
18
+ return [
19
+ formatBucket(row.bucket),
20
+ `calls=${row.calls}`,
21
+ `main(in+out)=${main}`,
22
+ `cache_read=${row.cacheRead}`,
23
+ `cache_write=${row.cacheWrite}`,
24
+ `cache_write_1h=${row.cacheWrite1h}`,
25
+ `reasoning=${row.reasoning}`,
26
+ // 命中率呈现:null = 桶内无 token 流(诚实缺席);有值取两位百分比
27
+ `hit_rate=${row.hitRate === null ? 'n/a' : `${(row.hitRate * 100).toFixed(2)}%`}`,
28
+ ].join(' ');
29
+ }
30
+ /** 事件计数行文本 */
31
+ function eventsLine(row) {
32
+ return `${formatBucket(row.bucket)} ${row.eventType} count=${row.count}`;
33
+ }
34
+ /**
35
+ * obs_query 工具定义工厂(装载批由装配根经插件注册面挂入工具注册表)。
36
+ * @param service obs 服务句柄(与告警/摄取同一实例)
37
+ */
38
+ export function createObsQueryTool(service) {
39
+ return {
40
+ name: 'obs_query',
41
+ description: '查询观测聚合(durable 事件流的小时/日桶统计)。metric=events 返回各事件类型' +
42
+ '计数(eventType 支持尾通配 <族前缀>/*——如 compaction/* 族计数一次可达;' +
43
+ '非任意 glob);metric=usage 返回 LLM token 用量聚合(input/output 主计费桶与 cache 桶' +
44
+ '分列,token 原始值——不折算货币;hit_rate = 缓存命中率派生列 cacheRead/' +
45
+ '(input+cacheRead+cacheWrite) 桶内聚合比值,n/a = 桶内无 token 流)。桶时刻 ' +
46
+ 'UTC 对齐。窗口 from/to 为 epoch 毫秒(含边界);行上限缺省 100、硬帽 1000,' +
47
+ '按时间正序返回。',
48
+ parameters: Type.Object({
49
+ granularity: Type.Union([Type.Literal('hour'), Type.Literal('day')], {
50
+ description: '粒度:hour = 小时桶;day = 日桶(仅含已闭合日)',
51
+ }),
52
+ metric: Type.Optional(Type.Union([Type.Literal('events'), Type.Literal('usage')], {
53
+ description: '指标:events = 事件计数(缺省);usage = LLM 用量聚合',
54
+ })),
55
+ from: Type.Optional(Type.Number({ description: '窗口下界(epoch 毫秒,含)' })),
56
+ to: Type.Optional(Type.Number({ description: '窗口上界(epoch 毫秒,含)' })),
57
+ eventType: Type.Optional(Type.String({
58
+ // 尾通配一形(03 §10.8 u-1 RP5——u-4 落码):无 '*' 精确匹配,或恰
59
+ // 一个尾 '/*';其余 glob 形(中缀 '*'、空前缀 '/*'、'**'、裸 '*')
60
+ // schema 段前置拒——04 §7 段 1 既有码 TOOL_INVALID_ARGS(零新码)
61
+ pattern: '^[^*]+(/\\*)?$',
62
+ description: '事件类型过滤(仅 metric=events 有效):精确匹配或尾通配 <族前缀>/*(如 compaction/* 族计数一次可达;非任意 glob)',
63
+ })),
64
+ limit: Type.Optional(Type.Number({ description: '行上限(缺省 100、硬帽 1000)' })),
65
+ }, { additionalProperties: false }),
66
+ effect: 'read',
67
+ execute: async (args) => {
68
+ try {
69
+ const rows = service.query({
70
+ granularity: args.granularity === 'day' ? 'day' : 'hour',
71
+ metric: args.metric === 'usage' ? 'usage' : 'events',
72
+ ...(typeof args.from === 'number' ? { from: args.from } : {}),
73
+ ...(typeof args.to === 'number' ? { to: args.to } : {}),
74
+ ...(typeof args.eventType === 'string' ? { eventType: args.eventType } : {}),
75
+ ...(typeof args.limit === 'number' ? { limit: args.limit } : {}),
76
+ });
77
+ if (rows.length === 0) {
78
+ return { content: [{ type: 'text', text: '(窗口内无聚合行)' }] };
79
+ }
80
+ const header = args.metric === 'usage'
81
+ ? 'bucket calls main(in+out) cache_read cache_write cache_write_1h reasoning hit_rate'
82
+ : 'bucket event_type count';
83
+ const lines = rows.map((row) => ('eventType' in row ? eventsLine(row) : usageLine(row)));
84
+ return { content: [{ type: 'text', text: [header, ...lines].join('\n') }] };
85
+ }
86
+ catch (error) {
87
+ // 一切失败编码为 isError 数据面(03 §2.3)——BaseError 携码前置披露
88
+ const code = error instanceof BaseError ? error.code : undefined;
89
+ const message = error instanceof Error ? error.message : String(error);
90
+ return {
91
+ content: [{ type: 'text', text: code ? `[${code}] ${message}` : message }],
92
+ isError: true,
93
+ };
94
+ }
95
+ },
96
+ };
97
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,63 @@
1
+ import { BaseError, getEventTypeMeta } from '../contracts/index.js';
2
+ /**
3
+ * audit_events 建表迁移项(05 §9)。占号 v8——号随注册序顺延:scheduler v2 /
4
+ * goal v3 / memory v4-6 / credentials v7 之后。声明 export-only:宿主装配根
5
+ * HOST_MIGRATION_TAIL 机械聚合(05 §6.4——声明来自件、执行在宿主)。
6
+ */
7
+ export const AUDIT_MIGRATION = {
8
+ version: 8,
9
+ name: 'audit-events',
10
+ sql: 'CREATE TABLE audit_events (id INTEGER PRIMARY KEY, time INTEGER NOT NULL, type TEXT NOT NULL, data TEXT NOT NULL) STRICT',
11
+ };
12
+ /** listRecent 缺省帽(诊断/呈现面一屏量) */
13
+ const LIST_RECENT_DEFAULT = 100;
14
+ /**
15
+ * 构造审计流面。词汇闸以 contracts 事件注册表为判据(模块加载即含全部
16
+ * 核心词——计数随批增长以注册表为单源,此处不复述数字防漂移;插件扩展
17
+ * 词经 registerEventType 亦即时可达)。
18
+ * @param db 已开库句柄(audit_events 表须已由迁移链建就——表不在则首笔
19
+ * INSERT 即 SQLite 报错,fail-loud 不静默)
20
+ * @param clock 挂钟注入(测试假钟;缺省 Date.now)
21
+ */
22
+ export function createAuditFace(db, clock = Date.now) {
23
+ // 三语句构造期预编译(better-sqlite3 惯例——句柄复用免逐笔 parse)
24
+ const insertStmt = db.prepare('INSERT INTO audit_events (time, type, data) VALUES (?, ?, ?)');
25
+ const lastByTypeStmt = db.prepare('SELECT id, time, type, data FROM audit_events WHERE type = ? ORDER BY id DESC LIMIT 1');
26
+ const recentStmt = db.prepare('SELECT id, time, type, data FROM audit_events ORDER BY id DESC LIMIT ?');
27
+ /** 单行解析:data JSON 串 → 对象形;坏形 fail-loud(宁崩不误读) */
28
+ const parseRow = (row) => {
29
+ let parsed;
30
+ try {
31
+ parsed = JSON.parse(row.data);
32
+ }
33
+ catch {
34
+ throw new BaseError('PERSIST_DATA_CORRUPT', `审计流行 #${row.id}(${row.type})data 非法 JSON——库文件损坏,拒误读`);
35
+ }
36
+ // JSON 合法但非对象形(串/数组/数字)同为坏形——data 词形恒为对象
37
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
38
+ throw new BaseError('PERSIST_DATA_CORRUPT', `审计流行 #${row.id}(${row.type})data 非对象形——拒误读`);
39
+ }
40
+ return { id: row.id, time: row.time, type: row.type, data: parsed };
41
+ };
42
+ return {
43
+ append(type, data) {
44
+ // 词汇闸两档(写入面前置——先于任何库操作):
45
+ const meta = getEventTypeMeta(type);
46
+ if (meta === undefined) {
47
+ throw new Error(`审计流 append 词汇闸红:事件类型 ${type} 未在词汇注册表——先注册再落账(fail-loud)`);
48
+ }
49
+ if (meta.category !== 'log-only') {
50
+ throw new Error(`审计流 append 词汇闸红:事件类型 ${type} 类别为 ${meta.category}——审计流只收 log-only 词,surface/snapshot/structure 词归会话流管辖`);
51
+ }
52
+ insertStmt.run(clock(), type, JSON.stringify(data));
53
+ },
54
+ lastOf(type) {
55
+ const row = lastByTypeStmt.get(type);
56
+ return row === undefined ? undefined : parseRow(row);
57
+ },
58
+ listRecent(limit = LIST_RECENT_DEFAULT) {
59
+ const rows = recentStmt.all(limit);
60
+ return rows.map(parseRow);
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * persist — 派生库开库面(aux 面;03 §10.8 件身份条款 + 05 §6.4 表族入主库
3
+ * 迁移链例外清单〔memory/goal/scheduler〕不含 obs 的执法位)。
4
+ *
5
+ * 面向「可禁用 core: 件的派生存储」:调用方(obs 等)经本公开面拿
6
+ * better-sqlite3 Database 实例,自建表、自管版本(PRAGMA user_version 归
7
+ * 调用方——与主库 user_version 门禁分立不串档)。本函数只做物理卫生三拍:
8
+ * 父目录代建 + WAL 编舞(busy_timeout/WAL/synchronous=FULL——与主库开库
9
+ * 同一拍单源复用 store.prepareWal)+ 库文件 0600 自检修复。
10
+ *
11
+ * better-sqlite3 只准出现在 persist(02 §4.2 native 隔离律)——本面是
12
+ * 该律对派生库场景的延伸出口:obs 件经公开面拿实例、自身不 import
13
+ * better-sqlite3。
14
+ */
15
+ import Database from 'better-sqlite3';
16
+ import { mkdirSync } from 'node:fs';
17
+ import { dirname } from 'node:path';
18
+ import { repairFileMode } from './paths.js';
19
+ import { prepareWal } from './store.js';
20
+ /**
21
+ * 打开派生库(物理卫生三拍;无版本门禁——schema 主权归调用方)。
22
+ * @param dbPath 库文件路径(':memory:' 内存形态同样支持——诊断/测试位)
23
+ * @returns 就绪的 Database(调用方负责建表与 close)
24
+ */
25
+ export function openAuxDatabase(dbPath, options = {}) {
26
+ const warn = options.warn ?? ((message) => console.error(message));
27
+ const inMemory = dbPath === ':memory:';
28
+ if (!inMemory) {
29
+ // 库父目录须在场(better-sqlite3 不代建目录;openStore 同款)
30
+ mkdirSync(dirname(dbPath), { recursive: true });
31
+ }
32
+ const db = new Database(dbPath);
33
+ prepareWal(db, warn);
34
+ if (!inMemory) {
35
+ // 库文件 0600 自检修复(派生库同持操作者数据——权限面与主库一致)
36
+ repairFileMode(dbPath, '派生库文件', warn);
37
+ }
38
+ return db;
39
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * persist 域错误码注册(05 篇 §6 物理存储——物理层码族 PERSIST_ 首批)。
3
+ *
4
+ * 前缀族明列见 contracts/errors.ts ERROR_CODE_PREFIXES(02 §5.3 #1;2026-09-05
5
+ * persist 落码批补列)。本文件由模块公开面 index.ts 引入(注册纪律:写入点
6
+ * 文件必须实际 import 本文件,注册才发生——与 session/codes.ts 同款)。
7
+ */
8
+ import { registerErrorCodes } from '../contracts/index.js';
9
+ registerErrorCodes([
10
+ {
11
+ code: 'PERSIST_SCHEMA_TOO_NEW',
12
+ module: 'persist',
13
+ description: '版本门禁红:库内 user_version 高于宿主迁移链 head(降级运行拒开,05 §6.4)',
14
+ },
15
+ {
16
+ code: 'PERSIST_SCHEMA_UNRECOGNIZED',
17
+ module: 'persist',
18
+ description: '库文件不可识别:非空库但 user_version=0(外来 SQLite 文件/损坏残卷,宁拒绝不误读)',
19
+ },
20
+ {
21
+ code: 'PERSIST_DATA_CORRUPT',
22
+ module: 'persist',
23
+ description: '库内数据损坏:JSON 列解析失败等外因损坏(断电半写/手编库),fail-loud 拒误读',
24
+ },
25
+ {
26
+ code: 'PERSIST_WRITE_EXHAUSTED',
27
+ module: 'persist',
28
+ description: 'write-behind 批级重试耗尽(首版 3 次)——进程 fail-loud(写不进库的会话继续跑 = 谎报持久化,05 §6.3)',
29
+ },
30
+ {
31
+ code: 'PERSIST_SECRET_UNREADABLE',
32
+ module: 'persist',
33
+ description: '凭证密文不可解读:密钥丢失/不匹配(AES-256-GCM 解密失败)——重录凭证即恢复',
34
+ },
35
+ ]);
@@ -0,0 +1,30 @@
1
+ /**
2
+ * persist 模块公开面(L2 物理存储——02 篇模块清单席 11;机制真源 05 篇 §6/§9)。
3
+ *
4
+ * 本批落码:SQLite 单后端 WAL + v1 基线 schema(七对象)+ user_version 迁移
5
+ * 门禁(TOO_NEW 拒开/缺口备份迁移/冷启动单事务)+ write-behind 批落链(微任务
6
+ * 节流/批帽 64/指数退避/毒丸隔离·durable 标记/flush 屏障)+ Persistence 门面
7
+ * (SessionLog 接线/种子会话同步落库)+ 凭证密文盒(AES-256-GCM)+ 主库
8
+ * 归属三级梯子 + store_state LRU 治理 + session_fts 对账三档。
9
+ * 消费面(host 装配根/ctx.sessions 只读服务/core: 插件 DAO)随后续批接线。
10
+ */
11
+ import './codes.js';
12
+ export { APPLICATION_ID, SCHEMA_VERSION, CANONICAL_DDL } from './schema.js';
13
+ export { normalizeMigrations } from './migrations.js';
14
+ export { DATA_DIR_ENV, DB_PATH_ENV, DEFAULT_DATA_DIR_BASENAME, DEFAULT_DB_BASENAME, MEMORY_DB_PATH, resolveDataDir, resolveDatabasePath, resolveDatabasePathIn, ensureDataDir, } from './paths.js';
15
+ export { SECRET_KEY_BASENAME, loadOrCreateSecretKey, encryptSecret, decryptSecret, ephemeralSecretKey, } from './secret-box.js';
16
+ export { openStore, Store } from './store.js';
17
+ // 进程级 durable 审计流(05 §9 audit_events——U3 落码批 U3-2):迁移项声明
18
+ // export-only(宿主装配根 HOST_MIGRATION_TAIL 机械聚合——同 credentials v7
19
+ // 形);写入面经库句柄构造,单写者 = 宿主固定件(boot 幂等 diff/高危面记账)
20
+ export { AUDIT_MIGRATION, createAuditFace } from './audit.js';
21
+ // 装载史 durable 载体(05 §9 load_generations——装载史批 h-2):迁移项声明
22
+ // export-only(宿主装配根 HOST_MIGRATION_TAIL 机械聚合——audit v8 同形);
23
+ // 写点 = 宿主装配根(boot 完成点 + /reload reapply 尾,h-3 接线),CLI 卸载
24
+ // 腿只读消费(uninstall 双回执有源化,h-4 接线)
25
+ export { LOAD_GENERATIONS_MIGRATION, createLoadHistoryFace } from './load-history.js';
26
+ // 派生库开库面(批 18b——03 §10.8 obs 自管库文件的执法位:物理卫生三拍
27
+ // 单源复用,schema 主权归调用方)
28
+ export { openAuxDatabase } from './aux.js';
29
+ export { WriteBehind } from './write-behind.js';
30
+ export { Persistence } from './persistence.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * load_generations 建表迁移项(05 §9)。占号 v10——号随注册序顺延:scheduler
3
+ * v2 / goal v3 / memory v4-6·9 / credentials v7 / audit v8 之后。声明
4
+ * export-only:宿主装配根 HOST_MIGRATION_TAIL 机械聚合(05 §6.4——声明来自
5
+ * 件、执行在宿主;audit_events v8 同形)。
6
+ */
7
+ export const LOAD_GENERATIONS_MIGRATION = {
8
+ version: 10,
9
+ name: 'load-generations',
10
+ sql: 'CREATE TABLE load_generations (id INTEGER PRIMARY KEY, started_at INTEGER NOT NULL, ended_at INTEGER, activated TEXT NOT NULL, skipped TEXT NOT NULL, failed TEXT NOT NULL) STRICT',
11
+ };
12
+ /**
13
+ * 构造装载史面。
14
+ * @param db 已开库句柄(load_generations 表须已由迁移链建就——表不在则首笔
15
+ * 即 SQLite 报错,fail-loud 不静默;sessions 表为 v1 基线恒在)
16
+ * @param clock 挂钟注入(测试假钟;缺省 Date.now——started_at 落笔与查询侧
17
+ * 开放窗 COALESCE 归一共用此钟)
18
+ */
19
+ export function createLoadHistoryFace(db, clock = Date.now) {
20
+ // 三语句构造期预编译(better-sqlite3 惯例——句柄复用免逐笔 parse)
21
+ const closeOpenStmt = db.prepare('UPDATE load_generations SET ended_at = ? WHERE ended_at IS NULL');
22
+ const insertStmt = db.prepare('INSERT INTO load_generations (started_at, ended_at, activated, skipped, failed) VALUES (?, NULL, ?, ?, ?)');
23
+ // 时间窗 join 推算(05 §9 定形语义):世代激活窗 [started_at,
24
+ // COALESCE(ended_at, :now)] ∩ 会话存活窗 [created_at, updated_at] 非空即
25
+ // 共现(闭区间——端点相触即计);成员判据恒 activated——json_each 展开
26
+ // 三分区列比对 id(SQLite JSON1 内建,非外部依赖)。
27
+ const countStmt = db.prepare(`
28
+ SELECT COUNT(DISTINCT s.id) AS cnt
29
+ FROM sessions s
30
+ JOIN load_generations g
31
+ ON s.created_at <= COALESCE(g.ended_at, ?)
32
+ AND s.updated_at >= g.started_at
33
+ WHERE EXISTS (SELECT 1 FROM json_each(g.activated) m WHERE m.value ->> '$.id' = ?)
34
+ `);
35
+ // 换代双写单事务:回填与插新同成同败——半途崩不产生双开放窗或丢前代收口
36
+ const rotateTx = db.transaction((snapshot, now) => {
37
+ closeOpenStmt.run(now);
38
+ const info = insertStmt.run(now, JSON.stringify(snapshot.activated), JSON.stringify(snapshot.skipped), JSON.stringify(snapshot.failed));
39
+ return Number(info.lastInsertRowid);
40
+ });
41
+ return {
42
+ recordLoadGeneration(snapshot) {
43
+ return rotateTx(snapshot, clock());
44
+ },
45
+ querySessionsWithPlugin(pluginId) {
46
+ const row = countStmt.get(clock(), pluginId);
47
+ return row.cnt;
48
+ },
49
+ };
50
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * persist — 统一迁移框架(05 篇 §6.4)。
3
+ *
4
+ * 迁移函数注册表在 persist(宿主固定件),插件不注册迁移;core: 插件表族
5
+ * (memory 记忆表 / goal 表 / scheduler jobs 表)的建表/升级声明由 host 装配根
6
+ * **机械聚合**传入 openStore——迁移链仍是单链,声明来自插件、执行在宿主。
7
+ * 本批只有框架 + v1 基线;首个业务迁移项随对应插件落码批进场。
8
+ */
9
+ /**
10
+ * 校验并排序迁移链(开库前调用一次——装配错误在任何库文件被碰之前抛出)。
11
+ * 规则:全部 version > 基线版本、严格递增无重复、sql 非空。
12
+ * @param migrations 聚合的迁移项(顺序无关,按 version 排序后返回)
13
+ * @param baseVersion 基线 SCHEMA_VERSION(迁移链起点)
14
+ * @returns 按 version 升序的迁移链
15
+ */
16
+ export function normalizeMigrations(migrations, baseVersion) {
17
+ const seen = new Set();
18
+ const out = [];
19
+ for (const m of migrations) {
20
+ if (!Number.isInteger(m.version) || m.version <= baseVersion) {
21
+ throw new Error(`迁移项 ${m.name || '(未具名)'}:version=${m.version} 必须是大于基线 ${baseVersion} 的整数`);
22
+ }
23
+ if (!m.name || typeof m.name !== 'string') {
24
+ throw new Error(`迁移项 version=${m.version}:name 缺失`);
25
+ }
26
+ if (typeof m.sql !== 'string' || m.sql.trim() === '') {
27
+ throw new Error(`迁移项 ${m.name}(v${m.version}):sql 为空`);
28
+ }
29
+ if (seen.has(m.version)) {
30
+ throw new Error(`迁移项 version=${m.version} 重复(${m.name})——每个版本至多一项迁移`);
31
+ }
32
+ seen.add(m.version);
33
+ out.push(m);
34
+ }
35
+ out.sort((a, b) => a.version - b.version);
36
+ return out;
37
+ }