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,38 @@
1
+ /**
2
+ * 指数退避 + 等比半幅抖动(04 §3.3 条 4 定式)。
3
+ * @param base 基值 ms(RetryPolicyConfig.baseDelayMs)
4
+ * @param attempt 第几次重试(1 起——指数位 n-1)
5
+ * @param random 随机源(缺省 Math.random;测试注确定性源)
6
+ * @returns 实延迟 ms——恒 ∈ [base·2^(n-1)·0.5, base·2^(n-1)),下界恒 > 0
7
+ */
8
+ export function jitteredBackoff(base, attempt, random = Math.random) {
9
+ const scale = base * 2 ** (attempt - 1);
10
+ return scale * (0.5 + random() * 0.5);
11
+ }
12
+ /** 按策略计算某次重试的实延迟(抖动内联——runTurns 重试腿的取值口) */
13
+ export function retryDelay(policy, attempt, random = Math.random) {
14
+ return jitteredBackoff(policy.baseDelayMs, attempt, random);
15
+ }
16
+ /**
17
+ * 可中止睡眠(退避挂 abort signal 的执法位)。
18
+ * @param ms 时长 @param signal 中止信号(已中止/中途触发都即时收场)
19
+ * @returns true = 睡满;false = 被中止(打断即取消重试)
20
+ */
21
+ export function abortableSleep(ms, signal) {
22
+ return new Promise((resolve) => {
23
+ if (signal?.aborted) {
24
+ resolve(false);
25
+ return;
26
+ }
27
+ const onAbort = () => {
28
+ clearTimeout(timer);
29
+ signal.removeEventListener('abort', onAbort);
30
+ resolve(false);
31
+ };
32
+ const timer = setTimeout(() => {
33
+ signal?.removeEventListener('abort', onAbort);
34
+ resolve(true);
35
+ }, ms);
36
+ signal?.addEventListener('abort', onAbort, { once: true });
37
+ });
38
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * conversation 域操控面错误码注册(02 §5.3 SESSION_ 族操控四码——2026-09-08
3
+ * e-1 规范先行批明列;03 §2.2 第十一面三动词执法承载;本批 e-4 落码兑现
4
+ * 「词先锚定、落码随 e-4」)。
5
+ *
6
+ * 四码分层:SESSION_TARGET_NOT_FOUND 是三动词共同幽灵守卫(目标会话 id 无
7
+ * 对应行——GOAL_NOT_FOUND 同构;词面避撞批 13b SDK 面 SESSION_NOT_FOUND
8
+ * 〔module 段 session〕,e-1 冷读闸 blocker 处置案 a);SESSION_INACTIVE 管
9
+ * interrupt 打断无对象(响亮拒不静默 no-op);SESSION_TURN_STALE 管
10
+ * expectedTurnId 乐观并发位翻页拒(codex expected_turn_id 形);SESSION_ROUND_LIMIT
11
+ * 管 a2a 回合护栏帽拒(缺省 5、人面输入重置链深)。第五码 SESSION_CONTROL_DENIED
12
+ * 管操控门检拒(sessions.control-cross 全域同门——操控轴无树内豁免;e-4
13
+ * 落码批 02 §5.3 同笔扩册,SESSION_OBSERVE_DENIED 同构先例)。
14
+ *
15
+ * module 段 conversation——SESSION_ 前缀系跨功能域共用前缀(session 模块
16
+ * 既有码族/批 13b SDK 面裸码〔module 段 session〕/obs 观测两码〔module 段
17
+ * obs〕与本族四码分域并存——02 §5.3 跨功能域共用前缀注)。
18
+ * 本文件由模块公开面 index.ts 引入(注册纪律:import 发生才注册)。
19
+ */
20
+ import { registerErrorCodes } from '../contracts/index.js';
21
+ registerErrorCodes([
22
+ {
23
+ code: 'SESSION_TARGET_NOT_FOUND',
24
+ module: 'conversation',
25
+ description: '操控动词目标会话 id 无对应行——幽灵守卫(GOAL_NOT_FOUND 同构;send/interrupt/withdraw 三动词共同前置,03 §2.2 第十一面。词面避撞:批 13b SDK 面 SESSION_NOT_FOUND 在册〔module 段 session——prompt 显式 sessionId 无对应会话〕,两码语义分立故带 TARGET 段)',
26
+ },
27
+ {
28
+ code: 'SESSION_INACTIVE',
29
+ module: 'conversation',
30
+ description: 'interrupt 作用目标无在飞 run 拒——打断无对象,响亮拒不静默 no-op(03 §2.2 第十一面 interrupt)',
31
+ },
32
+ {
33
+ code: 'SESSION_TURN_STALE',
34
+ module: 'conversation',
35
+ description: '乐观并发位 expectedTurnId 给定而目标 turn 已翻页拒(「说完话世界已变」显式化——静默错投比响亮拒更险;codex expected_turn_id 形,03 §2.2 第十一面 send)',
36
+ },
37
+ {
38
+ code: 'SESSION_ROUND_LIMIT',
39
+ module: 'conversation',
40
+ description: 'a2a 回合护栏帽拒——跨会话 send 链深超帽(缺省 5、人面输入重置链深,03 §2.2 第十一面回合护栏)',
41
+ },
42
+ {
43
+ code: 'SESSION_CONTROL_DENIED',
44
+ module: 'conversation',
45
+ description: '操控门未开门拒——高危面 sessions.control-cross(03 §4.6 v1 首批第六枚)门检执法位拒:操控轴无树内豁免、全域同门(03 §2.2 第十一面门制句),send/interrupt/withdraw 三动词共同前置(幽灵守卫之后);SESSION_OBSERVE_DENIED 同构门检拒码先例(2026-09-08 e-4 落码批 02 §5.3 同笔扩册)',
46
+ },
47
+ ]);
@@ -0,0 +1,102 @@
1
+ /**
2
+ * conversation — 跨会话操控工具族(03 §2.2 第十一面模型道——e-4 落码):
3
+ * 宿主内建固定三件 `session_send` / `session_interrupt` / `session_withdraw`
4
+ * (`session_` 前缀保留字〔03 §2.7〕的宿主侧落位,与 obs 观测四件同段)。
5
+ *
6
+ * **双面同源**(题 7 拍板):本族 = sessions-control 受理器(control.ts
7
+ * createSessionsControl——动词语义/执法序单源)的**薄包装**——工具 execute
8
+ * 只做 caller 归因闭包({kind:'session', sessionId}——per-session 装配位
9
+ * 注入,模型无法伪造送话身份)+ 回执 JSON 呈报;幽灵守卫/门检/链深帽/
10
+ * TURN_STALE 全在受理器(拒码 BaseError 经 guard 折 `[CODE] message`
11
+ * isError——与 obs 工具族同形)。
12
+ *
13
+ * 恒挂载律(03 §2.2 第十一面同款):模型可见清单恒在,不随门开合动态挂载
14
+ * ——门检在受理器内执法(拒 = SESSION_CONTROL_DENIED 自描述文案)。
15
+ */
16
+ import { BaseError } from '../contracts/index.js';
17
+ import { Type } from 'typebox';
18
+ /** execute 统一包装:受理器 BaseError 折 `[CODE] message` isError(obs guard 同形) */
19
+ function guard(body) {
20
+ return body().catch((error) => {
21
+ if (error instanceof BaseError) {
22
+ return {
23
+ content: [{ type: 'text', text: `[${error.code}] ${error.message}` }],
24
+ isError: true,
25
+ };
26
+ }
27
+ throw error;
28
+ });
29
+ }
30
+ /** 回执 JSON 呈报(类型化回执词结构化转写——不虚构不改写) */
31
+ function receiptText(receipt) {
32
+ return { content: [{ type: 'text', text: JSON.stringify(receipt) }] };
33
+ }
34
+ /** createControlTools 工厂(装载位/测试唯一入口)——三件定义数组 */
35
+ export function createControlTools(deps) {
36
+ const { callerSessionId, control } = deps;
37
+ return [
38
+ {
39
+ name: 'session_send',
40
+ description: '向另一会话发送消息(跨会话 a2a 协作)。回执三态:delivered(已投递起跑/停摆落账)、' +
41
+ 'queued(目标忙已入列——可经 session_withdraw 撤回)、dropped(拒收)。' +
42
+ '需高危面 sessions.control-cross 开门(全域同门——同树目标同样要开门)。' +
43
+ 'a2a 链深帽缺省 5(连续代理互搏拒 SESSION_ROUND_LIMIT——目标会话收到人面输入即重置);' +
44
+ 'expectedTurnId 可选乐观并发位(= 目标最近 turn/start 事件 seq——不匹配拒 SESSION_TURN_STALE);' +
45
+ 'dedupeKey 可选幂等位(重试场景携稳定键——同键重复发送返原回执不重复注入)。',
46
+ parameters: Type.Object({
47
+ sessionId: Type.String({ description: '目标会话 id' }),
48
+ text: Type.String({ description: '注入文本(非空)' }),
49
+ expectedTurnId: Type.Optional(Type.Number({ description: '乐观并发位:目标 durable 日志最近 turn/start 事件 seq(可选)' })),
50
+ dedupeKey: Type.Optional(Type.String({ description: '幂等键(可选——同键重复 send 返原回执不重复注入;须调用方自铸唯一)' })),
51
+ }, { additionalProperties: false }),
52
+ effect: 'write',
53
+ execute: async (args) => guard(async () => {
54
+ const receipt = await control.send({
55
+ caller: { kind: 'session', sessionId: callerSessionId },
56
+ targetSessionId: args.sessionId,
57
+ text: args.text,
58
+ ...(args.expectedTurnId !== undefined ? { expectedTurnId: args.expectedTurnId } : {}),
59
+ ...(args.dedupeKey !== undefined ? { dedupeKey: args.dedupeKey } : {}),
60
+ });
61
+ return receiptText(receipt);
62
+ }),
63
+ },
64
+ {
65
+ name: 'session_interrupt',
66
+ description: '打断目标会话的在飞 run(协作中止——目标 turn 以 interrupted 收口)。' +
67
+ '目标无在飞 run 拒 SESSION_INACTIVE(响亮拒不静默 no-op)。' +
68
+ '回执含 stillQueued(打断后在队操控件 id 清单——可逐件 session_withdraw 撤回)' +
69
+ '与 queuedCount(在队总数——在队件保留在队、下次续跑作种子,打断不清队)。' +
70
+ '需高危面 sessions.control-cross 开门(全域同门)。',
71
+ parameters: Type.Object({ sessionId: Type.String({ description: '目标会话 id' }) }, { additionalProperties: false }),
72
+ effect: 'write',
73
+ execute: async (args) => guard(async () => {
74
+ const receipt = await control.interrupt({
75
+ caller: { kind: 'session', sessionId: callerSessionId },
76
+ targetSessionId: args.sessionId,
77
+ });
78
+ return receiptText(receipt);
79
+ }),
80
+ },
81
+ {
82
+ name: 'session_withdraw',
83
+ description: '撤回本会话先前经 session_send 排入目标队列的消息(在队撤回对称闭环)。' +
84
+ 'messageId 取 session_send 回执。回执两态:withdrawn(在队已移除)、' +
85
+ 'delivered(已出队/从未在队——诚实呈报不虚构撤回成功)。' +
86
+ '需高危面 sessions.control-cross 开门(全域同门)。',
87
+ parameters: Type.Object({
88
+ sessionId: Type.String({ description: '目标会话 id' }),
89
+ messageId: Type.String({ description: 'send 回执所铸消息 id(msg-<n> 形)' }),
90
+ }, { additionalProperties: false }),
91
+ effect: 'write',
92
+ execute: async (args) => guard(async () => {
93
+ const receipt = await control.withdraw({
94
+ caller: { kind: 'session', sessionId: callerSessionId },
95
+ targetSessionId: args.sessionId,
96
+ messageId: args.messageId,
97
+ });
98
+ return receiptText(receipt);
99
+ }),
100
+ },
101
+ ];
102
+ }
@@ -0,0 +1,210 @@
1
+ /**
2
+ * conversation — 跨会话操控面(03 §2.2 第十一面 `sessions-control`——e-1
3
+ * 规范先行批立面、2026-09-08 e-4 落码批兑现、2026-09-09 e-5 收口批定形)。
4
+ *
5
+ * **双面同源**(题 7 拍板):模型走宿主内建固定工具族(`session_send` /
6
+ * `session_interrupt` / `session_withdraw`——恒挂载不随门开合动态挂载),
7
+ * 插件代码走 `ctx.get("sessions-control")` 服务面;**两面同一实现源**——
8
+ * 工具 = 服务面薄包装,动词语义单源本文件(受理器 + 契约)。
9
+ *
10
+ * 三动词(共同前置:幽灵守卫 `SESSION_TARGET_NOT_FOUND` → 门检
11
+ * `SESSION_CONTROL_DENIED`〔操控轴无树内豁免、全域同门〕→ …动词各自位):
12
+ * - `send`:跨会话输入注入——发送方不选通道(04 §4 三通道路由驱动侧
13
+ * 单源:idle→followUp 起跑 / busy→steer 合批 / 停摆→inject 带入),
14
+ * 回执 = 入列回执(被丢/被拒项呈报不静默);`expectedTurnId?` 乐观
15
+ * 并发位(翻页拒 `SESSION_TURN_STALE`);a2a 链深超帽拒
16
+ * `SESSION_ROUND_LIMIT`;`dedupeKey?` 幂等位(e-5 定案——同键重复
17
+ * send 返原回执不重复注入,键域按调用方身份分域)。
18
+ * - `interrupt`:打断目标在飞 run(turn 终态 interrupted);无在飞拒
19
+ * `SESSION_INACTIVE`(响亮拒不静默 no-op)。回执含 still_queued 后果
20
+ * 清单(e-5 定形:在队操控件 id 清单 + 在队总数计数——打断只打断当前
21
+ * run,在队 steer 件保留在队、下次 followUp 作种子续跑)。
22
+ * - `withdraw`:在队撤回对称闭环(题 9 缺省案 e-5 呈拍确认维持:已出队
23
+ * 'delivered' no-op 诚实回执,不虚构撤回成功)。
24
+ *
25
+ * **provenance 盖章单源**(05 §3.1):注入的 user/message `source` 由本
26
+ * 受理面按调用方身份(ControlCaller)铸——模型工具道 `session:<送话会话id>`、
27
+ * 插件服务道 `plugin:<插件id>`(既有前缀复用);**调用方传入面无 source
28
+ * 参数位**——伪造结构性不存在。
29
+ */
30
+ import { BaseError } from '../contracts/index.js';
31
+ import { adjudicateCapabilityDoor } from '../contracts/api.js';
32
+ /** 操控轴高危面名(03 §4.6 v1 首批第六枚——操控全域无树内豁免,两轴分立) */
33
+ export const CONTROL_CROSS_CAPABILITY = 'sessions.control-cross';
34
+ /** 服务面名(scope.provide 键——插件经 `ctx.get("sessions-control")` 消费) */
35
+ export const SESSIONS_CONTROL_SERVICE = 'sessions-control';
36
+ /** a2a 回合护栏帽缺省(03 §2.2 第十一面题 8 呈拍定案:缺省 5 对齐 openclaw;装配可覆盖) */
37
+ export const A2A_ROUND_LIMIT_DEFAULT = 5;
38
+ /**
39
+ * send 幂等近期窗帽(e-5 定案——与队列帽同数 100:同键重复 send 返原回执
40
+ * 的查重窗容量;超帽逐最旧〔Map 插入序〕。内存位不承诺跨进程——与队列/
41
+ * a2a 深度位同语义)。
42
+ */
43
+ export const SEND_DEDUPE_WINDOW_CAPACITY = 100;
44
+ /**
45
+ * 受理面铸 source(provenance 盖章——05 §3.1;唯一合法铸位:driver.submit
46
+ * 对 `session:` 前缀 fail-loud 拒,结构性杜绝绕受理面伪造)。
47
+ */
48
+ export function controlSourceOf(caller) {
49
+ return caller.kind === 'session' ? `session:${caller.sessionId}` : `plugin:${caller.pluginId}`;
50
+ }
51
+ /**
52
+ * 操控受理器工厂(双面同源的单源实现位——03 §2.2 第十一面 e-4 落码):
53
+ * 宿主内建工具族(模型道)与 `ctx.get("sessions-control")`(插件道)都调
54
+ * 本件返回的 SessionsControlFace,动词语义/执法序单源在此。
55
+ *
56
+ * 受理序(send):幽灵守卫 → 操控门检(全域)→ a2a 链深帽 →
57
+ * expectedTurnId 对拍 → 投递(auto-open:目标未 open 即 resume 打开——
58
+ * 发送方不应被要求先手动打开目标)→ capability/used 审计;
59
+ * interrupt/withdraw:幽灵守卫 → 门检 → 动词位(interrupt 不 auto-open——
60
+ * 打断休眠会话无在飞可打;withdraw 不 auto-open——队列是内存态,休眠无队)。
61
+ */
62
+ export function createSessionsControl(deps) {
63
+ const roundLimit = deps.roundLimit ?? A2A_ROUND_LIMIT_DEFAULT;
64
+ /** 撤回关联键铸造位(栈级自增——受理器栈内唯一,跨会话可对账) */
65
+ let messageSeq = 0;
66
+ /**
67
+ * send 幂等近期窗(e-5 定案):分域键 = `<caller 前缀>|<dedupeKey>` →
68
+ * 原回执。仅成功回执入窗(拒路径不入——重试须重新走受理序);命中前置
69
+ * 返原回执(零新注入零新审计——回执重放非新受理)。超帽逐最旧(Map
70
+ * 插入序)。内存位:崩溃重启即丢(与队列/a2a 深度位同语义——幂等承诺
71
+ * 不跨进程)。
72
+ */
73
+ const dedupeWindow = new Map();
74
+ /** 幽灵守卫(三动词共同前置①——目标 id 无对应行,进程内 ∪ durable) */
75
+ const guardTarget = (verb, targetSessionId) => {
76
+ if (!deps.manager.exists(targetSessionId)) {
77
+ throw new BaseError('SESSION_TARGET_NOT_FOUND', `操控动词 ${verb} 的目标会话 ${targetSessionId} 不存在(进程内与 durable 均无对应行)`);
78
+ }
79
+ };
80
+ /**
81
+ * 操控门检(三动词共同前置②——全域同门无树内豁免,03 §2.2 第十一面门制句)。
82
+ * 门检输入 = caller 感知合成(开门制扩展批——deps.getOpensFor(caller):
83
+ * 插件道 doors 段 ∪ 该插件行 opens、模型道 doors 段单独)。
84
+ */
85
+ const enforceDoor = (verb, targetSessionId, caller) => {
86
+ const verdict = adjudicateCapabilityDoor(deps.getOpensFor(caller), CONTROL_CROSS_CAPABILITY);
87
+ if (!verdict.ok) {
88
+ throw new BaseError('SESSION_CONTROL_DENIED', `${verdict.message}(动词 ${verb},目标会话 ${targetSessionId}——操控轴无树内豁免)`);
89
+ }
90
+ };
91
+ /** capability/used 逐次审计(门开后行使即记——拒路径未获许可不记) */
92
+ const audit = (verb, input, caller) => {
93
+ deps.onCapabilityUsed?.({
94
+ capability: CONTROL_CROSS_CAPABILITY,
95
+ verb,
96
+ targetSessionId: input.targetSessionId,
97
+ ...(caller.kind === 'session' ? { callerSessionId: caller.sessionId } : { pluginId: caller.pluginId }),
98
+ });
99
+ };
100
+ /**
101
+ * send 幂等窗入位(仅成功回执——e-5 定案):分域键铸造 + 超帽逐最旧。
102
+ * 命中查询在 send 入口前置(见 send 体)。
103
+ */
104
+ const rememberSend = (dedupeKey, caller, receipt) => {
105
+ const key = `${controlSourceOf(caller)}|${dedupeKey}`;
106
+ if (dedupeWindow.size >= SEND_DEDUPE_WINDOW_CAPACITY) {
107
+ // 逐最旧(Map 迭代序 = 插入序——首个键即最旧)
108
+ dedupeWindow.delete(dedupeWindow.keys().next().value);
109
+ }
110
+ dedupeWindow.set(key, receipt);
111
+ };
112
+ return {
113
+ async send(input) {
114
+ // 空文本拒收(契约位——空文本是调用方 bug,非业务拒不造码)
115
+ if (input.text.trim() === '') {
116
+ throw new Error('send 拒收空文本(调用方 bug——注入文本非空是调用方契约)');
117
+ }
118
+ // —— 幂等命中前置(e-5 定案):同键重复 send 返原回执不重复注入。
119
+ // 前置于幽灵/门检——首次受理已成功,重试就该幂等返原果(重试时世界
120
+ // 变化不该让已成功的逻辑发送吃新拒码);命中 = 回执重放非新受理,
121
+ // 零新注入零新审计
122
+ if (input.dedupeKey !== undefined) {
123
+ const prior = dedupeWindow.get(`${controlSourceOf(input.caller)}|${input.dedupeKey}`);
124
+ if (prior !== undefined)
125
+ return prior;
126
+ }
127
+ guardTarget('send', input.targetSessionId);
128
+ enforceDoor('send', input.targetSessionId, input.caller);
129
+ // —— a2a 链深帽(03 §2.2 第十一面回合护栏):送话方深度+1 超帽拒。
130
+ // 插件服务道起跳链深 1(插件直唤即第一跳);送话会话未 open 按 0——
131
+ // 与「崩溃重启归零」同语义(内存位不承诺跨进程精确保留,护栏目的已达)
132
+ const callerDepth = input.caller.kind === 'session' ? (deps.manager.driverOf(input.caller.sessionId)?.a2aDepth ?? 0) : 0;
133
+ const newDepth = callerDepth + 1;
134
+ if (newDepth > roundLimit) {
135
+ throw new BaseError('SESSION_ROUND_LIMIT', `跨会话 send 链深 ${newDepth} 超回合护栏帽 ${roundLimit}(a2a 互搏环——人面输入重置链深)`);
136
+ }
137
+ // —— auto-open 投递腿:目标未 open 即 resume 打开(幂等 open——单焦点
138
+ // 不造第二附着);expectedTurnId 对拍用活体 durable 日志尾扫(单源)
139
+ const driver = deps.manager.driverOf(input.targetSessionId) ?? deps.manager.open(input.targetSessionId).driver;
140
+ if (input.expectedTurnId !== undefined && input.expectedTurnId !== lastTurnStartSeq(driver)) {
141
+ throw new BaseError('SESSION_TURN_STALE', `目标会话 ${input.targetSessionId} 的 turn 已翻页(期望最近 turn/start seq=${input.expectedTurnId},实际 ${String(lastTurnStartSeq(driver))}——说完话世界已变)`);
142
+ }
143
+ const messageId = `msg-${(messageSeq += 1)}`;
144
+ const message = {
145
+ role: 'user',
146
+ content: input.text,
147
+ timestamp: Date.now(),
148
+ source: controlSourceOf(input.caller),
149
+ };
150
+ const receipt = driver.deliverControl(message, messageId, newDepth);
151
+ // 幂等窗入位(仅成功回执——三态都是「受理完成」面;拒路径已在上方 throw 不至此)
152
+ if (input.dedupeKey !== undefined)
153
+ rememberSend(input.dedupeKey, input.caller, receipt);
154
+ audit('send', input, input.caller);
155
+ return receipt;
156
+ },
157
+ async interrupt(input) {
158
+ guardTarget('interrupt', input.targetSessionId);
159
+ enforceDoor('interrupt', input.targetSessionId, input.caller);
160
+ // 打断无对象响亮拒不静默 no-op(03 §2.2 第十一面 interrupt)——目标未
161
+ // open 必无在飞 run(休眠会话无 run),缺席 driver 同判
162
+ const driver = deps.manager.driverOf(input.targetSessionId);
163
+ if (driver === undefined || !driver.running) {
164
+ throw new BaseError('SESSION_INACTIVE', `目标会话 ${input.targetSessionId} 无在飞 run 可打断(休眠或已停摆)`);
165
+ }
166
+ driver.abort();
167
+ // —— still_queued 后果清单(e-5 定形):打断只打断当前 run,在队件
168
+ // 保留在队(aborted 不续跑——下次 followUp 作种子续跑)。打断后取快照
169
+ // 呈报去向:操控件 id 清单(可 withdraw)+ 在队总数(含普通件计数)
170
+ const queued = driver.queuedItems();
171
+ audit('interrupt', input, input.caller);
172
+ return {
173
+ status: 'interrupted',
174
+ targetSessionId: input.targetSessionId,
175
+ stillQueued: queued.flatMap((item) => (item.id !== undefined ? [item.id] : [])),
176
+ queuedCount: queued.length,
177
+ };
178
+ },
179
+ async withdraw(input) {
180
+ guardTarget('withdraw', input.targetSessionId);
181
+ enforceDoor('withdraw', input.targetSessionId, input.caller);
182
+ // 队列是内存态:目标未 open / 已停摆清队 → 不在队恒真('delivered'
183
+ // 诚实呈报——不虚构撤回成功)
184
+ const driver = deps.manager.driverOf(input.targetSessionId);
185
+ const withdrawn = driver?.withdrawQueued(input.messageId) ?? false;
186
+ audit('withdraw', input, input.caller);
187
+ return { status: withdrawn ? 'withdrawn' : 'delivered', messageId: input.messageId };
188
+ },
189
+ };
190
+ }
191
+ /**
192
+ * 逐插件绑定(plugin-boot fork 落位调用):caller = {kind:'plugin', pluginId}
193
+ * 闭包铸造——插件传入面无 caller 位(传入即被覆写,伪造结构性不存在)。
194
+ */
195
+ export function bindControlForPlugin(pluginId, face) {
196
+ return {
197
+ send: (input) => face.send({ ...input, caller: { kind: 'plugin', pluginId } }),
198
+ interrupt: (input) => face.interrupt({ ...input, caller: { kind: 'plugin', pluginId } }),
199
+ withdraw: (input) => face.withdraw({ ...input, caller: { kind: 'plugin', pluginId } }),
200
+ };
201
+ }
202
+ /** 目标 durable 日志最近 turn/start 事件 seq(expectedTurnId 对拍单源——尾扫;无 turn 返 undefined) */
203
+ function lastTurnStartSeq(driver) {
204
+ const events = driver.session.events();
205
+ for (let i = events.length - 1; i >= 0; i -= 1) {
206
+ if (events[i].type === 'turn/start')
207
+ return events[i].seq;
208
+ }
209
+ return undefined;
210
+ }