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,138 @@
1
+ /**
2
+ * 技能注册表(06 §11.3 护栏②注册表层 + 03 skills_change 事件面)。
3
+ *
4
+ * - provider 注册序即优先序(06 §11.3);同名 first-wins 由扫描序表达,冲突带
5
+ * collision 诊断(winner/loser 双路径);
6
+ * - 快照数帽 100:超帽裁尾 + capacity 诊断列被裁计数(provider 注册序即裁序
7
+ * ——最低优先层先出局);
8
+ * - refresh() 全量重扫(/reload 正口与 skill_manage 写后自动刷新共用一径);
9
+ * - onChange 监听面 = 03 skills_change 事件的装配桥(载荷 = 现行 provider id
10
+ * 清单,消费 = 渐进披露清单重物化);
11
+ * - 并发 refresh 串行化(承诺链——后到刷新在先到落定后重扫,末笔即最新快照;
12
+ * 与 exec 登记簿持久化链同律)。
13
+ */
14
+ import { BaseError } from '../contracts/index.js';
15
+ import { SKILL_SNAPSHOT_CAP } from './types.js';
16
+ /**
17
+ * 创建技能注册表。
18
+ *
19
+ * @param options.cap 快照数帽(缺省 100)
20
+ */
21
+ export function createSkillsRegistry(options = {}) {
22
+ const cap = options.cap ?? SKILL_SNAPSHOT_CAP;
23
+ const providers = [];
24
+ const providerById = new Map();
25
+ let snapshot = [];
26
+ let lastDiagnostics = [];
27
+ let lastReport = { providers: 0, total: 0, droppedByCap: 0, collisions: 0 };
28
+ const listeners = new Set();
29
+ let refreshChain = Promise.resolve(lastReport);
30
+ const runRefresh = async () => {
31
+ const merged = new Map(); // name → skill(first-wins)
32
+ const diagnostics = [];
33
+ let collisions = 0;
34
+ // pass 域去重集:本轮 refresh 新铸(层间同文件 realpath 去重锚)——
35
+ // 不跨 refresh 持久(曾因持久共享集致二次 refresh 起整册静默清空)
36
+ const passSeen = new Set();
37
+ for (const provider of providers) {
38
+ let scan;
39
+ try {
40
+ scan = await provider.scan(passSeen);
41
+ }
42
+ catch (error) {
43
+ // 单 provider 扫描炸不拖垮其余层(坏层诊断降级——其余层技能照常服务)
44
+ diagnostics.push({
45
+ type: 'warning',
46
+ message: `provider ${provider.id} 扫描失败:${error instanceof Error ? error.message : String(error)}`,
47
+ });
48
+ continue;
49
+ }
50
+ for (const diagnostic of scan.diagnostics)
51
+ diagnostics.push(diagnostic);
52
+ for (const skill of scan.skills) {
53
+ const existing = merged.get(skill.name);
54
+ if (existing !== undefined) {
55
+ collisions += 1;
56
+ diagnostics.push({
57
+ type: 'collision',
58
+ message: `技能同名冲突:${skill.name}(${existing.providerId} 层 ${existing.filePath} 压过 ${skill.providerId} 层 ${skill.filePath})`,
59
+ path: skill.filePath,
60
+ });
61
+ continue; // first-wins:扫描序(即 provider 注册序)在先者胜
62
+ }
63
+ merged.set(skill.name, skill);
64
+ }
65
+ }
66
+ // 快照帽:按优先序裁尾(最低优先层先出局)+ capacity 诊断(作者侧反馈)
67
+ let droppedByCap = 0;
68
+ if (merged.size > cap) {
69
+ droppedByCap = merged.size - cap;
70
+ const kept = [...merged.entries()].slice(0, cap);
71
+ diagnostics.push({
72
+ type: 'capacity',
73
+ message: `技能快照超帽 ${cap}——按优先序裁尾 ${droppedByCap} 件(低优先层先出局;同名覆盖或精简低优先层)`,
74
+ });
75
+ snapshot = kept.map(([, skill]) => skill);
76
+ }
77
+ else {
78
+ snapshot = [...merged.values()];
79
+ }
80
+ lastDiagnostics = diagnostics;
81
+ lastReport = {
82
+ providers: providers.length,
83
+ total: snapshot.length,
84
+ droppedByCap,
85
+ collisions,
86
+ };
87
+ for (const listener of listeners)
88
+ listener(lastReport);
89
+ return lastReport;
90
+ };
91
+ return {
92
+ registerProvider(provider) {
93
+ if (providerById.has(provider.id)) {
94
+ throw new BaseError('SKILLS_PROVIDER_CONFLICT', `[SKILLS_PROVIDER_CONFLICT] 技能 provider 撞名:${provider.id} 已注册——provider id 即层身份(skills_change 载荷成员),装配序须唯一`);
95
+ }
96
+ providerById.set(provider.id, provider);
97
+ providers.push(provider); // 注册序即优先序——后注册者优先级低
98
+ },
99
+ unregisterProvider(id) {
100
+ const provider = providerById.get(id);
101
+ if (provider === undefined)
102
+ return false;
103
+ providerById.delete(id);
104
+ providers.splice(providers.indexOf(provider), 1);
105
+ return true;
106
+ },
107
+ providerIds() {
108
+ return providers.map((p) => p.id);
109
+ },
110
+ getProvider(id) {
111
+ return providerById.get(id);
112
+ },
113
+ scanRoots() {
114
+ const roots = [];
115
+ for (const provider of providers)
116
+ roots.push(...provider.roots);
117
+ return roots;
118
+ },
119
+ refresh() {
120
+ // 串行承诺链:并发 refresh 按到达序逐笔重扫,末笔即最新快照
121
+ refreshChain = refreshChain.then(runRefresh, runRefresh);
122
+ return refreshChain;
123
+ },
124
+ get(name) {
125
+ return snapshot.find((skill) => skill.name === name);
126
+ },
127
+ list() {
128
+ return snapshot;
129
+ },
130
+ diagnostics() {
131
+ return lastDiagnostics;
132
+ },
133
+ onChange(listener) {
134
+ listeners.add(listener);
135
+ return () => listeners.delete(listener);
136
+ },
137
+ };
138
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * 渐进披露渲染层(06 §11.3 护栏③ + §11.5 激活包装)。
3
+ *
4
+ * 清单块(护栏③):name+description+location「一技能一块」;字节帽 64KiB
5
+ * 整件裁尾(半件截断会让模型读到残缺描述做错误选择——整件进出);达限截断
6
+ * 必须带块内注释就地披露(一切截断必须带作者侧反馈——禁静默)。
7
+ *
8
+ * 激活包装(§11.5(b)):/skill:name 显式激活把全文包装为具名块注入(不走
9
+ * FS);包装形镜像 pi(name/location 属性 + 引用相对路径解析锚句 + 正文 +
10
+ * 可选 args 尾段);parseSkillInvocation 是包装的镜像解析(TUI 命令面展开
11
+ * 与会话回放侧共用——具名块是往返可逆的)。
12
+ */
13
+ import { SKILL_MANIFEST_BYTE_CAP } from './types.js';
14
+ /** XML 转义(属性与文本共用——& 先行防双转义) */
15
+ function escapeXml(str) {
16
+ return str
17
+ .replace(/&/g, '&')
18
+ .replace(/</g, '&lt;')
19
+ .replace(/>/g, '&gt;')
20
+ .replace(/"/g, '&quot;')
21
+ .replace(/'/g, '&apos;');
22
+ }
23
+ /** 描述折行归一(清单行单行化——换行/制表/连续空格折成一空格) */
24
+ function normalizeDescription(description) {
25
+ return description.replace(/\s+/g, ' ').trim();
26
+ }
27
+ /**
28
+ * 渲染渐进披露清单块(一技能一块:name/description/location)。
29
+ *
30
+ * - disable-model-invocation 件滤除(模型自动路径不见——显式激活面仍可达);
31
+ * - 字节帽整件裁尾:逐件累加,装得下一件才列一件;装不下即停 + 块内注释
32
+ * 披露未列件数(模型与作者两侧都看得见被裁的事实);
33
+ * - 描述折行归一空格(frontmatter 折行书写不破清单行结构)。
34
+ */
35
+ export function renderAvailableSkills(skills, options = {}) {
36
+ const byteCap = options.byteCap ?? SKILL_MANIFEST_BYTE_CAP;
37
+ const visible = skills.filter((skill) => !skill.disableModelInvocation);
38
+ if (visible.length === 0) {
39
+ return { text: '', omitted: 0, truncated: false };
40
+ }
41
+ const header = [
42
+ '以下技能为特定任务提供专门指令。',
43
+ '当任务与某技能的描述匹配时,用 load_skill 工具按名装载该技能全文或节(section 节寻址),或用 read 工具按 location 直读全文。',
44
+ '技能正文引用相对路径时,以技能目录(SKILL.md 父目录)为根解析为绝对路径后再使用。',
45
+ '',
46
+ '<available_skills>',
47
+ ];
48
+ const footer = '</available_skills>';
49
+ const headerBytes = Buffer.byteLength(header.join('\n') + '\n', 'utf8');
50
+ const blocks = visible.map((skill) => [
51
+ ' <skill>',
52
+ ` <name>${escapeXml(skill.name)}</name>`,
53
+ ` <description>${escapeXml(normalizeDescription(skill.description))}</description>`,
54
+ ` <location>${escapeXml(skill.filePath)}</location>`,
55
+ ' </skill>',
56
+ ].join('\n'));
57
+ // 整件累加:固定头脚 + 已列块装得下下一件才列(半件不截)
58
+ let used = headerBytes + Buffer.byteLength(footer, 'utf8');
59
+ let listed = 0;
60
+ for (const block of blocks) {
61
+ const cost = Buffer.byteLength(block, 'utf8') + 1; // +1 = 块间换行
62
+ if (used + cost > byteCap)
63
+ break;
64
+ used += cost;
65
+ listed += 1;
66
+ }
67
+ const omitted = visible.length - listed;
68
+ if (listed === 0) {
69
+ // 头脚即超帽的极端形态:空清单 + 披露注释仍是完整反馈面
70
+ const disclosure = `<!-- 清单超 ${byteCap} 字节帽——${visible.length} 件全部未列(精简低优先层或拆分技能) -->`;
71
+ const text = [...header, disclosure, footer].join('\n');
72
+ return { text, omitted, truncated: true };
73
+ }
74
+ if (omitted === 0) {
75
+ return { text: [...header, ...blocks.slice(0, listed), footer].join('\n'), omitted: 0, truncated: false };
76
+ }
77
+ const disclosure = `<!-- 清单超 ${byteCap} 字节帽——另有 ${omitted} 件未列(read 对应 location 或精简低优先层) -->`;
78
+ return {
79
+ text: [...header, ...blocks.slice(0, listed), disclosure, footer].join('\n'),
80
+ omitted,
81
+ truncated: true,
82
+ };
83
+ }
84
+ /**
85
+ * 具名技能块单源(§11.5 具名块三消费面共用:显式激活包装 / load_skill 工具
86
+ * 回执 / skills 键 spawn 注入——06 §11.6)。
87
+ *
88
+ * content 由调用方给定(全文 / 命中节重建 / mode 过滤形)——回执与激活包装
89
+ * 同形同源(formatSkillInvocation = 本件 + args 尾段)。引导句钉相对路径
90
+ * 解析锚(baseDir)——模型 follow-up 引用 references/ 等相对路径时的解析
91
+ * 约定(06 §11.3 细化②薄主文件范式的配套面)。
92
+ */
93
+ export function formatSkillBlock(skill, content) {
94
+ const head = `<skill name="${escapeXml(skill.name)}" location="${escapeXml(skill.filePath)}">`;
95
+ const body = `引用相对路径以 ${skill.baseDir} 为根解析。\n\n${content}`;
96
+ return `${head}\n${body}\n</skill>`;
97
+ }
98
+ /**
99
+ * 显式激活包装(§11.5(b)):全文包装为具名 user message 注入(不走 FS)。
100
+ */
101
+ export function formatSkillInvocation(skill, args) {
102
+ const block = formatSkillBlock(skill, skill.content);
103
+ const tail = args !== undefined && args.trim() !== '' ? `\n\n${args}` : '';
104
+ return `${block}${tail}`;
105
+ }
106
+ /**
107
+ * 激活块镜像解析(formatSkillInvocation 的逆;形镜像 pi parseSkillBlock)。
108
+ * 非激活块文本返回 null(判据形不匹配即非——不猜)。
109
+ */
110
+ export function parseSkillInvocation(text) {
111
+ const match = text.match(/^<skill name="([^"]+)" location="([^"]+)">\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);
112
+ if (match === null)
113
+ return null;
114
+ const anchor = '引用相对路径以 ';
115
+ const content = match[3] ?? '';
116
+ if (!content.startsWith(anchor))
117
+ return null; // 包装头句是契约的一部分
118
+ return {
119
+ name: match[1] ?? '',
120
+ location: match[2] ?? '',
121
+ content,
122
+ ...(match[4] !== undefined && match[4].trim() !== '' ? { args: match[4] } : {}),
123
+ };
124
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * 渐进披露第三层细化三件(06 §11.3——2026-09-06 技术调研消化批增补):
3
+ *
4
+ * ① 节级寻址——SKILL.md 按标题建节骨架(split_sections 形),深读可按祖先路径
5
+ * 寻址节('Mode 3 > Workflow' 式——只读命中节不读全文);歧义多匹配报错
6
+ * 不猜(猜错节的注入比不注入更坏——错误指导比无指导危害大);
7
+ * ② 薄主文件——作者侧范式(主文件只留触发+路由、深度进 references/),机制面
8
+ * 仅此件:引用路径按 baseDir 解析的约定(read 路径消费,无新通道);
9
+ * ③ 行级过滤——命中注入前按运行参数行级过滤(ponytail filterSkillBodyForMode
10
+ * 形:强度表行按模式名匹配、带引号防普通规则行误杀——一份本体服务多档)。
11
+ *
12
+ * 三层的成本纪律同 06 §11.3 总则:节骨架与路由行是常驻清单成本、深读仍按需
13
+ * ——渐进披露 O(1) 不破(细化的是第三层「read 全文」的粒度,不动清单层)。
14
+ */
15
+ /** ATX 标题行(CommonMark 形:≤3 前导空格 + 1-6 个 # + 空白 + 文本;尾 # 串修剪) */
16
+ const HEADING_RE = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/;
17
+ /** 围栏代码块开栏行(``` 或 ~~~ ≥3 个;缩进 ≤3;fence 标记后的 info 串不参与判定) */
18
+ const FENCE_OPEN_RE = /^ {0,3}(`{3,}|~{3,})/;
19
+ /** 闭栏行(CommonMark:闭栏只许 fence 字符 + 空白——```js 带 info 串不闭栏) */
20
+ const FENCE_CLOSE_RE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
21
+ /**
22
+ * 按标题切节(split_sections 形)。
23
+ *
24
+ * 围栏代码块内的 `#` 行不是标题(fence 追踪——同字符开关,``` 开 ``` 关);
25
+ * 节正文到下一「同级或更高级」标题前收口;首个标题之前的引导文本不产节
26
+ * (无标题路径可寻址——引导文本随全文 read 路径在场,不丢内容)。
27
+ * 同父同名兄弟标题会产出重复 path——寻址歧义由 findSkillSection 报不猜。
28
+ */
29
+ export function splitSkillSections(body) {
30
+ const lines = body.split('\n');
31
+ // 尾随换行的空串尾巴是切分伪影('a\n'.split → ['a',''])——剥掉防末节 endLine 虚增
32
+ if (lines.length > 0 && (lines[lines.length - 1] ?? '') === '')
33
+ lines.pop();
34
+ const sections = [];
35
+ // 祖先栈:[{level, title}]——遇到 ≤ 栈顶级别的标题先弹栈再入栈
36
+ const stack = [];
37
+ let openFence = null; // 在栏 fence 字符('```' / '~~~')
38
+ let current = null;
39
+ let startBodyIndex = -1; // 当前节正文首行(含)
40
+ const closeSection = (endLineExclusive) => {
41
+ if (current === null)
42
+ return;
43
+ // endLineExclusive 是 0 起半开区间右端——换 1 起含端恰为「末行行号」;
44
+ // 空节(标题紧邻下一标题/文末)时退化为 startLine(标题行自身)
45
+ const endLine = Math.max(endLineExclusive, current.startLine);
46
+ sections.push({
47
+ path: current.path,
48
+ level: current.level,
49
+ title: current.title,
50
+ body: lines.slice(startBodyIndex, endLineExclusive).join('\n'),
51
+ startLine: current.startLine,
52
+ endLine,
53
+ });
54
+ current = null;
55
+ };
56
+ for (let i = 0; i < lines.length; i++) {
57
+ const line = lines[i] ?? '';
58
+ if (openFence !== null) {
59
+ const closeMatch = FENCE_CLOSE_RE.exec(line);
60
+ // 闭栏须同字符(``` 开 ``` 关——``` 块内的 ~~~ 行是内容不是栏)
61
+ if (closeMatch !== null && (closeMatch[1] ?? '').startsWith(openFence)) {
62
+ openFence = null;
63
+ }
64
+ continue; // 栏内行不参与标题切分(正文由行区间还原)
65
+ }
66
+ const fenceMatch = FENCE_OPEN_RE.exec(line);
67
+ if (fenceMatch !== null) {
68
+ openFence = (fenceMatch[1] ?? '').slice(0, 3); // 同字符判定取前三位(``` 与 ```` 互闭)
69
+ continue;
70
+ }
71
+ const headingMatch = HEADING_RE.exec(line);
72
+ if (headingMatch === null)
73
+ continue;
74
+ const level = (headingMatch[1] ?? '').length;
75
+ const title = headingMatch[2] ?? '';
76
+ closeSection(i); // 上一节在标题行前收口
77
+ while (stack.length > 0 && (stack[stack.length - 1]?.level ?? 0) >= level)
78
+ stack.pop();
79
+ stack.push({ level, title });
80
+ current = {
81
+ path: stack.map((s) => s.title).join(' > '),
82
+ level,
83
+ title,
84
+ startLine: i + 1, // 1 起行号
85
+ };
86
+ startBodyIndex = i + 1; // 正文自标题行之后(0 起)
87
+ }
88
+ closeSection(lines.length);
89
+ return sections;
90
+ }
91
+ /**
92
+ * 按祖先路径寻址节('Mode 3 > Workflow' 式)。
93
+ *
94
+ * 歧义多匹配报错不猜(06 §11.3 细化①——错误指导比无指导危害大);未命中时
95
+ * candidates = 全部可寻址 path(人面呈现「可用节」——指路而非裸拒)。
96
+ */
97
+ export function findSkillSection(body, path) {
98
+ const sections = splitSkillSections(body);
99
+ const hits = sections.filter((s) => s.path === path);
100
+ if (hits.length === 1) {
101
+ const section = hits[0];
102
+ if (section !== undefined)
103
+ return { ok: true, section };
104
+ }
105
+ if (hits.length > 1) {
106
+ return { ok: false, reason: 'ambiguous', candidates: hits.map((s) => `${s.path}(L${s.startLine})`) };
107
+ }
108
+ return { ok: false, reason: 'not-found', candidates: sections.map((s) => s.path) };
109
+ }
110
+ /** 强度表行模式标签(`| **label** | …`——行级过滤与模式词表推导共用判据) */
111
+ const MODE_TABLE_LABEL_RE = /^\|\s*\*\*(.+?)\*\*\s*\|/;
112
+ /** 带引号工作示例行模式标签(`- label: "…"`——引号是判据的一部分) */
113
+ const MODE_EXAMPLE_LABEL_RE = /^-\s*([^:]+):\s*"/;
114
+ /**
115
+ * 行级过滤(ponytail filterSkillBodyForMode 形泛化——06 §11.3 细化③)。
116
+ *
117
+ * 只有两种行是模式专属、参与裁剪,其余行一律原样保留(普通规则行绝不误杀):
118
+ * - 强度表行:`| **label** | …`——label 命中模式词汇时,仅当前模式行保留;
119
+ * - 带引号的工作示例行:`- label: "…"`——引号是判据的一部分(`- Full: …`
120
+ * 无引号形是普通规则行,略读误杀即静默改行为——ponytail 实测教训)。
121
+ * 输入须为正文(frontmatter 已剥——装载态 content 即此形)。
122
+ */
123
+ export function filterSkillBody(body, options) {
124
+ const normalize = (label) => label.trim().toLowerCase();
125
+ const modeSet = new Set(options.modes.map(normalize));
126
+ const active = normalize(options.active);
127
+ return body
128
+ .split('\n')
129
+ .filter((line) => {
130
+ const tableMatch = MODE_TABLE_LABEL_RE.exec(line);
131
+ if (tableMatch !== null) {
132
+ const label = normalize(tableMatch[1] ?? '');
133
+ if (modeSet.has(label))
134
+ return label === active;
135
+ }
136
+ const exampleMatch = MODE_EXAMPLE_LABEL_RE.exec(line);
137
+ if (exampleMatch !== null) {
138
+ const label = normalize(exampleMatch[1] ?? '');
139
+ if (modeSet.has(label))
140
+ return label === active;
141
+ }
142
+ return true;
143
+ })
144
+ .join('\n');
145
+ }
146
+ /**
147
+ * 模式词表双形交集推导(06 §11.5 load_skill mode 判据)。
148
+ *
149
+ * 词表 = 强度表行标签 ∩ 带引号示例行标签(双形同现才认模式名——单形如
150
+ * `| **Version** |` 只是普通表行,入词表会把非模式正文误裁);推导域 =
151
+ * 待过滤正文自身(作者在正文里写了什么模式,模型就只能选什么——无第三真源)。
152
+ * trim+小写归一与 filterSkillBody 比对同源。
153
+ */
154
+ export function deriveModeVocabulary(body) {
155
+ const normalize = (label) => label.trim().toLowerCase();
156
+ const table = new Set();
157
+ const example = new Set();
158
+ for (const line of body.split('\n')) {
159
+ const tableMatch = MODE_TABLE_LABEL_RE.exec(line);
160
+ if (tableMatch !== null) {
161
+ table.add(normalize(tableMatch[1] ?? ''));
162
+ continue;
163
+ }
164
+ const exampleMatch = MODE_EXAMPLE_LABEL_RE.exec(line);
165
+ if (exampleMatch !== null) {
166
+ example.add(normalize(exampleMatch[1] ?? ''));
167
+ }
168
+ }
169
+ // 交集(table 迭代序 = 正文出现序——词表呈现稳定)
170
+ return [...table].filter((label) => example.has(label));
171
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * skills 域类型与常量(06 篇 §11 全线——双层结构/渐进披露三层护栏/发现六位序列)。
3
+ *
4
+ * 词汇面(02 §4.1 席 13):definition/provider 词汇族——Skill 是纯数据契约,
5
+ * SkillsProvider 是发现层实现方;注册表按 provider 注册序(即优先序)合并。
6
+ */
7
+ /** 技能名长度帽(agentskills.io 标准;06 §11.2) */
8
+ export const SKILL_NAME_MAX = 64;
9
+ /** 描述长度帽——装载层超限截断装载(全文仍可经 read 路径读原文件;06 §11.3 护栏①) */
10
+ export const SKILL_DESCRIPTION_MAX = 1024;
11
+ /** 注册表快照数帽——超帽裁尾 + capacity 诊断列被裁计数(06 §11.3 护栏②) */
12
+ export const SKILL_SNAPSHOT_CAP = 100;
13
+ /** 渐进披露清单块字节帽——达限截断 + 块内注释就地披露(06 §11.3 护栏③) */
14
+ export const SKILL_MANIFEST_BYTE_CAP = 64 * 1024;
15
+ /** provenance.memories 上限(06 §9.1 第 2 项——source_refs 50 同族) */
16
+ export const SKILL_PROVENANCE_MEMORIES_MAX = 50;
17
+ /**
18
+ * 单条 skills 引用清单帽(agents frontmatter `skills` 键 / SubagentRequest.skills
19
+ * 两面同判——06 §11.6 第五键;spawn 永久注入位防正文膨胀的形状护栏之一)
20
+ */
21
+ export const SKILL_REFS_MAX = 8;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * subagent 域错误码注册(04 §10——SUBAGENT_/JOB_ 前缀族;02 §5.3 批 15c
3
+ * 明列五码,SUBAGENT_DEPTH_EXCEEDED/JOB_KIND_UNKNOWN 承 04 §10 定名)。
4
+ *
5
+ * 码语义分层:SUBAGENT_DEPTH_EXCEEDED 管委派深度帽(防自嵌套爆栈);
6
+ * SUBAGENT_PRECHECK_FAILED 管 spawn 前预检闸(fail-ask 不降级瞎跑);
7
+ * SUBAGENT_PROVIDER_UNKNOWN 管静态绑定路由;JOB_KIND_UNKNOWN 管 kind
8
+ * 词汇登记(词汇注册表纪律);JOB_LIMIT_REACHED 管 Job 并行帽(形随
9
+ * issue 件落码批定值);SUBAGENT_NAME_INVALID 管程序化注册槽 name 裸词
10
+ * 词法(D 批——03 §2.7 行 262「本批新码」)。本文件由模块公开面 index.ts
11
+ * 引入(注册纪律:import 发生才注册)。
12
+ */
13
+ import { registerErrorCodes } from '../contracts/index.js';
14
+ registerErrorCodes([
15
+ {
16
+ code: 'SUBAGENT_DEPTH_EXCEEDED',
17
+ module: 'subagent',
18
+ description: '委派深度超帽 3 拒——防自嵌套爆栈(04 §10 委派边界③)',
19
+ },
20
+ {
21
+ code: 'SUBAGENT_PRECHECK_FAILED',
22
+ module: 'subagent',
23
+ description: 'spawn 前能力/权限预检闸拒——委派 def 前置要求(requiresTools)工具缺席即拒起跑并回执缺口清单,fail-ask 不降级瞎跑(04 §10 静默退化防御②;与 tools 白名单正交——白名单缺了照样跑、前置要求缺了不起跑)',
24
+ },
25
+ {
26
+ code: 'SUBAGENT_PROVIDER_UNKNOWN',
27
+ module: 'subagent',
28
+ description: '委派路由命中未注册 provider 拒——委派面静态绑定(通用 agent 工具缺省路由 in-process)',
29
+ },
30
+ {
31
+ code: 'SUBAGENT_PROVIDER_EXISTS',
32
+ module: 'subagent',
33
+ description: 'named provider 撞名拒——服务面注册(声明式子代理每文件一 named provider,扫描序 first-wins 由发现层表达、注册面撞名响亮拒)',
34
+ },
35
+ {
36
+ code: 'JOB_KIND_UNKNOWN',
37
+ module: 'subagent',
38
+ description: 'registerKind 未登记种类拒——防随手字符串当 kind 用(词汇注册表纪律同 04 §6)',
39
+ },
40
+ {
41
+ code: 'JOB_LIMIT_REACHED',
42
+ module: 'subagent',
43
+ description: 'Job 并行帽拒——同刻在飞 Job 数达按 kind 分帽上限(缺省无帽;缺省值随 issue 件落码批定——04 §10 issue 立题批钉位)',
44
+ },
45
+ {
46
+ code: 'SUBAGENT_NAME_INVALID',
47
+ module: 'subagent',
48
+ description: '程序化注册槽 name 词法违例拒——非裸词(06 §11.6 声明式 name 同形、词法承 §11.2「小写连字符与数字」——词法真源 06:386);执法序同 AGENT_ROLE 注记:撞名闸前置格式闸(03 §2.7 行 262;2026-09-07 冷读闸 blocker 修复批拍板案 a 改形——原「非域名两段式」弃)',
49
+ },
50
+ ]);
@@ -0,0 +1,55 @@
1
+ import { BaseError } from '../contracts/index.js';
2
+ import { createDeclarativeAgentTool } from './tool.js';
3
+ import { DEFAULT_SUBAGENT_PROVIDER, IN_PROCESS_CAPABILITIES } from './types.js';
4
+ /**
5
+ * def → named provider(late-binding def-defaults 包装):路由到本 def 的
6
+ * 委派请求缺省字段由此合流(模型侧经静态工具携带、程序侧直呼 service.run
7
+ * 两条道同折);真身始终 'in-process' 基 provider——独立装配全套的
8
+ * 黑盒契约不因声明式形态而二轨。def 形两腿共享(声明式 SubagentDef /
9
+ * 程序化 ProgrammaticSubagentDef——filePath 位非本桥消费,Omit 宽形;
10
+ * D 批 D-2 程序化腿复用同桥)。
11
+ */
12
+ export function defBoundProvider(def, service) {
13
+ return {
14
+ capabilities: IN_PROCESS_CAPABILITIES,
15
+ async run(request) {
16
+ const base = service.getProvider(DEFAULT_SUBAGENT_PROVIDER);
17
+ if (base === undefined) {
18
+ throw new BaseError('SUBAGENT_PROVIDER_UNKNOWN', `声明式子代理「${def.name}」的基工厂 'in-process' 未注册——host 装配批接线位(06 §11.6 桥条款)`);
19
+ }
20
+ // def 缺省合流(请求已有值胜出——静态工具道 service 已合流过一次,
21
+ // 幂等;程序直呼 provider 道由此兜底)
22
+ const merged = {
23
+ ...request,
24
+ ...(request.model === undefined && def.model !== undefined ? { model: def.model } : {}),
25
+ ...(request.systemPrompt === undefined ? { systemPrompt: def.systemPrompt } : {}),
26
+ ...(request.tools === undefined && def.tools !== undefined ? { tools: [...def.tools] } : {}),
27
+ ...(request.skills === undefined && def.skills !== undefined ? { skills: [...def.skills] } : {}),
28
+ name: request.name ?? def.name,
29
+ };
30
+ return base.run(merged);
31
+ },
32
+ };
33
+ }
34
+ /**
35
+ * 批量物化声明式子代理:每 def 一 named provider + 一静态工具。
36
+ * 撞名(named provider 或既有工具名)响亮拒——坏 def 已在解析层跳过,
37
+ * 这里的拒是装配态冲突(fail-loud;调用方可折 warn 降级——发现层
38
+ * first-wins 已表达同层同名,跨层冲突走注册面)。
39
+ */
40
+ export function materializeDeclarativeSubagents(defs, service, deps, opts) {
41
+ const names = [];
42
+ const tools = [];
43
+ const disposeProviders = [];
44
+ for (const def of defs) {
45
+ disposeProviders.push(service.registerProvider(def.name, defBoundProvider(def, service), { owner: opts?.owner }));
46
+ names.push(def.name);
47
+ tools.push(createDeclarativeAgentTool(def, { ...deps, service }));
48
+ }
49
+ // 撤位逆注册序(镜像 tools 撤位律);索引逆扫免 reverse 变异(重入安全)
50
+ const dispose = () => {
51
+ for (let i = disposeProviders.length - 1; i >= 0; i--)
52
+ disposeProviders[i]?.();
53
+ };
54
+ return { names, tools, dispose };
55
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * subagent 件公开面(04 §10——ctx.jobs 注册表 + SubagentProvider 委派机器)。
3
+ *
4
+ * 消费方:host 装配根(in-process 真工厂接线 + provideJobsService)、
5
+ * exec/issue 同表消费件(registerKind 'process'/'issue')、组合根(通知面
6
+ * 与结算钩子桥接)。in-process 真工厂本体归 host 装配批——本件提供机器
7
+ * 与 late-binding 接缝(15a/15b 先例:装载态挂账、批内机器先行)。
8
+ * 程序化注册面(registerProgrammatic——03 §2.2 第十二动词受局面,D 批
9
+ * D-2):注册表机器与执法序在本件,ctx 动词包装在 host/plugin-context。
10
+ */
11
+ import './codes.js';
12
+ export { isTerminalStatus, JOB_RETENTION_CAP, createJobRegistry } from './registry.js';
13
+ export { AGENT_TOOL_NAME, DEFAULT_SUBAGENT_PROVIDER, EXCLUDED_FROM_DERIVED_SURFACE, IN_PROCESS_CAPABILITIES, } from './types.js';
14
+ export { deriveToolSurface, findPrecheckGaps, intersectToolWhitelist } from './surface.js';
15
+ export { subagentSettledContent } from './notify.js';
16
+ export { createSubagentService, DEFAULT_SUBAGENT_FANOUT_LIMIT, ENV_MAX_CONCURRENT_SUBAGENTS, resolveSubagentFanoutLimit, } from './service.js';
17
+ export { createAgentTool, createDeclarativeAgentTool } from './tool.js';
18
+ export { defBoundProvider, materializeDeclarativeSubagents } from './declarative.js';
19
+ export { JOBS_SERVICE_NAME, provideJobsService } from './provide.js';
@@ -0,0 +1,51 @@
1
+ /** 通知内输出预览帽(人读通知不刷屏——全文在子会话日志可回看) */
2
+ const OUTPUT_PREVIEW_MAX = 512;
3
+ /** 输出预览截断(保头——通知是索引非全文载体) */
4
+ function preview(text) {
5
+ if (text.length <= OUTPUT_PREVIEW_MAX)
6
+ return text;
7
+ return `${text.slice(0, OUTPUT_PREVIEW_MAX)}…(已截断——全文见子会话日志)`;
8
+ }
9
+ /** 终态人读词(stopReason → 通知文案行) */
10
+ function stopWord(stopReason) {
11
+ if (stopReason === 'stop')
12
+ return '已完成';
13
+ if (stopReason === 'aborted')
14
+ return '已中止';
15
+ return '失败';
16
+ }
17
+ /**
18
+ * 结算通知的子会话指针行(RP3——收果拉取面轻量案:通知携指针非全文)。
19
+ * 取数自宿主机器账(in-process 工厂铸造位 structured)——第三方 provider
20
+ * 自有形无此字段则指针行诚实缺席(防御 guard:只认 in-process v1 形的
21
+ * string childSessionId + number messageCount,缺任一即视无指针)。
22
+ */
23
+ function childSessionPointerLine(result) {
24
+ const account = result.structured;
25
+ if (account === null || typeof account !== 'object')
26
+ return undefined;
27
+ const { childSessionId, messageCount } = account;
28
+ if (typeof childSessionId !== 'string' || childSessionId === '')
29
+ return undefined;
30
+ if (typeof messageCount !== 'number' || !Number.isInteger(messageCount) || messageCount < 0) {
31
+ return undefined;
32
+ }
33
+ return `子会话 ${childSessionId}(${messageCount} 条消息)`;
34
+ }
35
+ /**
36
+ * 结算通知 content(subagent-settled 注入体的唯一文案真源)。
37
+ */
38
+ export function subagentSettledContent(input) {
39
+ const { jobName, result } = input;
40
+ const lines = [`子代理「${jobName}」${stopWord(result.stopReason)}。`];
41
+ const pointer = childSessionPointerLine(result);
42
+ if (pointer !== undefined)
43
+ lines.push(pointer);
44
+ if (result.output !== '')
45
+ lines.push(`输出:${preview(result.output)}`);
46
+ // 降级上报如实呈现(禁伪装——上报表是知情面三律之一)
47
+ if (result.diagnostic !== undefined && result.diagnostic !== '') {
48
+ lines.push(`诊断:${result.diagnostic}`);
49
+ }
50
+ return lines.join('\n');
51
+ }
@@ -0,0 +1,7 @@
1
+ /** 服务词汇名(ctx.jobs——02 §4.1 #15 行 2026-09-07 批 15c 改名收口) */
2
+ export const JOBS_SERVICE_NAME = 'jobs';
3
+ /** provide 注册表到 scope(host 装配根调用——先于一切消费件起跑) */
4
+ export function provideJobsService(scope, registry) {
5
+ scope.provide(JOBS_SERVICE_NAME, registry);
6
+ return registry;
7
+ }