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.
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/.api-emit.stamp +1 -0
- package/dist/.build-meta.json +6 -0
- package/dist/agent/events.js +1 -0
- package/dist/agent/index.js +2 -0
- package/dist/agent/loop.js +139 -0
- package/dist/agent/queue.js +119 -0
- package/dist/agent/stream.js +62 -0
- package/dist/agent/tools-batch.js +221 -0
- package/dist/agent/types.js +8 -0
- package/dist/api/berry-agent-llm.d.ts +18 -0
- package/dist/api/berry-agent.d.ts +9 -0
- package/dist/api/surface.json +1632 -0
- package/dist/api/tsconfig.paths.json +12 -0
- package/dist/api/typebox-compile.d.ts +2 -0
- package/dist/api/typebox-value.d.ts +2 -0
- package/dist/api/typebox.d.ts +2 -0
- package/dist/browser/cdp.js +256 -0
- package/dist/browser/codes.js +31 -0
- package/dist/browser/discover.js +107 -0
- package/dist/browser/engine.js +169 -0
- package/dist/browser/index.js +17 -0
- package/dist/browser/install.js +342 -0
- package/dist/browser/page.js +321 -0
- package/dist/browser/service.js +158 -0
- package/dist/browser/tools.js +198 -0
- package/dist/browser/types.js +81 -0
- package/dist/channels/ask-queue.js +82 -0
- package/dist/channels/commands.js +129 -0
- package/dist/channels/engine/cell.js +143 -0
- package/dist/channels/engine/diff.js +143 -0
- package/dist/channels/engine/engine.js +416 -0
- package/dist/channels/engine/index.js +8 -0
- package/dist/channels/engine/input-keys.js +86 -0
- package/dist/channels/engine/input.js +596 -0
- package/dist/channels/engine/memory-io.js +71 -0
- package/dist/channels/engine/process-io.js +38 -0
- package/dist/channels/engine/types.js +17 -0
- package/dist/channels/engine/width.js +122 -0
- package/dist/channels/index.js +15 -0
- package/dist/channels/registry.js +56 -0
- package/dist/channels/sdk/admit.js +29 -0
- package/dist/channels/sdk/backend.js +102 -0
- package/dist/channels/sdk/cursor.js +56 -0
- package/dist/channels/sdk/index.js +20 -0
- package/dist/channels/sdk/jsonl.js +208 -0
- package/dist/channels/sdk/protocol.js +24 -0
- package/dist/channels/sdk/schema.js +91 -0
- package/dist/channels/sdk/wire-core.js +446 -0
- package/dist/channels/service.js +169 -0
- package/dist/channels/tui/autocomplete/autocomplete.js +42 -0
- package/dist/channels/tui/autocomplete/file-mentions.js +106 -0
- package/dist/channels/tui/autocomplete/popup.js +148 -0
- package/dist/channels/tui/autocomplete/provider.js +7 -0
- package/dist/channels/tui/autocomplete/token.js +68 -0
- package/dist/channels/tui/backend/ansi-rows.js +211 -0
- package/dist/channels/tui/backend/main-screen.js +207 -0
- package/dist/channels/tui/backend/osc.js +91 -0
- package/dist/channels/tui/backend/transcript.js +230 -0
- package/dist/channels/tui/backend/tui-backend.js +915 -0
- package/dist/channels/tui/editor/editor-model.js +543 -0
- package/dist/channels/tui/editor/editor-view.js +140 -0
- package/dist/channels/tui/editor/editor.js +275 -0
- package/dist/channels/tui/editor/undo-stack.js +35 -0
- package/dist/channels/tui/editor/visual-lines.js +126 -0
- package/dist/channels/tui/editor/word-nav.js +106 -0
- package/dist/channels/tui/history/history-viewer.js +403 -0
- package/dist/channels/tui/index.js +3 -0
- package/dist/channels/tui/layout.js +167 -0
- package/dist/channels/tui/markdown/blocks.js +111 -0
- package/dist/channels/tui/markdown/inline.js +75 -0
- package/dist/channels/tui/markdown/markdown.js +182 -0
- package/dist/channels/tui/memory/memory-viewer.js +540 -0
- package/dist/channels/tui/overlay/alt-screen.js +105 -0
- package/dist/channels/tui/overlay/overlay.js +57 -0
- package/dist/channels/tui/overlay/select-confirm.js +156 -0
- package/dist/channels/tui/panels/todo-panel.js +54 -0
- package/dist/channels/tui/panels/tool-progress-panel.js +93 -0
- package/dist/channels/tui/scroll/scroll-view.js +254 -0
- package/dist/channels/tui/status/status-line.js +76 -0
- package/dist/channels/tui/text.js +38 -0
- package/dist/channels/tui/theme.js +41 -0
- package/dist/channels/types.js +11 -0
- package/dist/channels/ui-core.js +209 -0
- package/dist/checkpoint/capture.js +45 -0
- package/dist/checkpoint/codes.js +33 -0
- package/dist/checkpoint/command.js +82 -0
- package/dist/checkpoint/gate.js +48 -0
- package/dist/checkpoint/index.js +20 -0
- package/dist/checkpoint/restore.js +172 -0
- package/dist/checkpoint/store.js +247 -0
- package/dist/checkpoint/types.js +4 -0
- package/dist/checkpoint/walk.js +146 -0
- package/dist/compaction/ccr-tools.js +112 -0
- package/dist/compaction/ccr.js +84 -0
- package/dist/compaction/codes.js +21 -0
- package/dist/compaction/index.js +18 -0
- package/dist/compaction/policy.js +200 -0
- package/dist/compaction/service.js +422 -0
- package/dist/compaction/slots.js +121 -0
- package/dist/compaction/types.js +10 -0
- package/dist/context/codes.js +40 -0
- package/dist/context/events.js +160 -0
- package/dist/context/index.js +13 -0
- package/dist/context/logger.js +136 -0
- package/dist/context/scope.js +182 -0
- package/dist/context/workspace.js +104 -0
- package/dist/contracts/agent-events.d.ts +73 -0
- package/dist/contracts/agent-events.js +16 -0
- package/dist/contracts/api.d.ts +249 -0
- package/dist/contracts/api.js +299 -0
- package/dist/contracts/approval.d.ts +37 -0
- package/dist/contracts/approval.js +9 -0
- package/dist/contracts/env-ref.d.ts +41 -0
- package/dist/contracts/env-ref.js +32 -0
- package/dist/contracts/errors.d.ts +56 -0
- package/dist/contracts/errors.js +266 -0
- package/dist/contracts/events.d.ts +70 -0
- package/dist/contracts/events.js +329 -0
- package/dist/contracts/index.d.ts +32 -0
- package/dist/contracts/index.js +26 -0
- package/dist/contracts/llm.d.ts +275 -0
- package/dist/contracts/llm.js +12 -0
- package/dist/contracts/messages.d.ts +63 -0
- package/dist/contracts/messages.js +65 -0
- package/dist/contracts/redact.d.ts +46 -0
- package/dist/contracts/redact.js +223 -0
- package/dist/contracts/tools.d.ts +197 -0
- package/dist/contracts/tools.js +33 -0
- package/dist/contracts/types.d.ts +302 -0
- package/dist/contracts/types.js +50 -0
- package/dist/contracts/ui.d.ts +122 -0
- package/dist/contracts/ui.js +15 -0
- package/dist/conversation/agent-service.js +61 -0
- package/dist/conversation/approval-wiring.js +82 -0
- package/dist/conversation/backoff.js +38 -0
- package/dist/conversation/codes.js +47 -0
- package/dist/conversation/control-tools.js +102 -0
- package/dist/conversation/control.js +210 -0
- package/dist/conversation/driver.js +922 -0
- package/dist/conversation/index.js +12 -0
- package/dist/conversation/model-visible.js +169 -0
- package/dist/conversation/open-tools.js +126 -0
- package/dist/conversation/reseed.js +132 -0
- package/dist/conversation/sessions.js +215 -0
- package/dist/conversation/todo.js +203 -0
- package/dist/conversation/types.js +35 -0
- package/dist/conversation/wiring.js +204 -0
- package/dist/credentials/codes.js +59 -0
- package/dist/credentials/commands.js +186 -0
- package/dist/credentials/env-ref.js +34 -0
- package/dist/credentials/index.js +28 -0
- package/dist/credentials/migration.js +18 -0
- package/dist/credentials/oauth.js +266 -0
- package/dist/credentials/refresh.js +152 -0
- package/dist/credentials/secrets.js +118 -0
- package/dist/credentials/types.js +30 -0
- package/dist/exec/bash.js +279 -0
- package/dist/exec/codes.js +37 -0
- package/dist/exec/env.js +99 -0
- package/dist/exec/environment.js +39 -0
- package/dist/exec/git-guard.js +623 -0
- package/dist/exec/index.js +24 -0
- package/dist/exec/registry.js +120 -0
- package/dist/exec/spawn.js +281 -0
- package/dist/exec/tail.js +77 -0
- package/dist/exec/types.js +5 -0
- package/dist/goal/codes.js +33 -0
- package/dist/goal/command.js +175 -0
- package/dist/goal/fold.js +121 -0
- package/dist/goal/gates.js +122 -0
- package/dist/goal/index.js +22 -0
- package/dist/goal/migration.js +48 -0
- package/dist/goal/service.js +501 -0
- package/dist/goal/todo-tool.js +174 -0
- package/dist/goal/types.js +1 -0
- package/dist/goal/update-tool.js +46 -0
- package/dist/host/approval-cmd.js +276 -0
- package/dist/host/assembly.js +1146 -0
- package/dist/host/boot-failures.js +104 -0
- package/dist/host/budget-advisory.js +74 -0
- package/dist/host/budget-broadcast.js +50 -0
- package/dist/host/builtins.js +54 -0
- package/dist/host/cli.js +544 -0
- package/dist/host/codes.js +120 -0
- package/dist/host/config-schema.js +195 -0
- package/dist/host/conversation-stack.js +654 -0
- package/dist/host/core-plugins.js +1685 -0
- package/dist/host/credentials-cmd.js +42 -0
- package/dist/host/disclosure.js +30 -0
- package/dist/host/dispatch.js +105 -0
- package/dist/host/doors-cmd.js +154 -0
- package/dist/host/dump-config.js +44 -0
- package/dist/host/hook-dispatch-guard.js +27 -0
- package/dist/host/import-gate.js +153 -0
- package/dist/host/index.js +54 -0
- package/dist/host/issue-session.js +241 -0
- package/dist/host/loader.js +372 -0
- package/dist/host/main.js +161 -0
- package/dist/host/manifest.js +381 -0
- package/dist/host/mcp-entry.js +53 -0
- package/dist/host/plugin-boot.js +952 -0
- package/dist/host/plugin-context.js +672 -0
- package/dist/host/plugin-install.js +480 -0
- package/dist/host/plugin-reload.js +124 -0
- package/dist/host/plugin-store.js +462 -0
- package/dist/host/plugin-tools.js +359 -0
- package/dist/host/plugin-uninstall.js +257 -0
- package/dist/host/plugins-cmd.js +302 -0
- package/dist/host/plugins-command.js +130 -0
- package/dist/host/plugins-config.js +195 -0
- package/dist/host/prompt-sections.js +133 -0
- package/dist/host/run-entry.js +544 -0
- package/dist/host/runtime.js +209 -0
- package/dist/host/scheduler-tick.js +294 -0
- package/dist/host/serve-daemon.js +501 -0
- package/dist/host/serve-entry.js +279 -0
- package/dist/host/session-anchor.js +41 -0
- package/dist/host/sessions-cmd.js +219 -0
- package/dist/host/sessions-face.js +47 -0
- package/dist/host/settings-store.js +112 -0
- package/dist/host/signals.js +69 -0
- package/dist/host/single-instance.js +105 -0
- package/dist/host/subagent-factory.js +291 -0
- package/dist/host/testkit/harness.js +283 -0
- package/dist/host/testkit/index.js +14 -0
- package/dist/host/testkit/matrix.js +302 -0
- package/dist/host/tool-policy-store.js +177 -0
- package/dist/host/triggers.js +212 -0
- package/dist/host/tui-entry.js +216 -0
- package/dist/host/webui-bridge.js +165 -0
- package/dist/issue/codes.js +38 -0
- package/dist/issue/filter.js +146 -0
- package/dist/issue/github.js +116 -0
- package/dist/issue/index.js +17 -0
- package/dist/issue/mount.js +70 -0
- package/dist/issue/poll.js +58 -0
- package/dist/issue/service.js +341 -0
- package/dist/issue/tools.js +70 -0
- package/dist/issue/types.js +34 -0
- package/dist/issue/webhook.js +126 -0
- package/dist/llm/codes.js +77 -0
- package/dist/llm/complete.js +196 -0
- package/dist/llm/events.js +15 -0
- package/dist/llm/index.js +24 -0
- package/dist/llm/inflight.js +65 -0
- package/dist/llm/model-id.js +58 -0
- package/dist/llm/provider-face.d.ts +32 -0
- package/dist/llm/provider-face.js +34 -0
- package/dist/llm/recovery.js +146 -0
- package/dist/llm/runtime.js +46 -0
- package/dist/llm/stream-fn.js +147 -0
- package/dist/lsp/codes.js +31 -0
- package/dist/lsp/connection.js +147 -0
- package/dist/lsp/frame.js +110 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/inject.js +58 -0
- package/dist/lsp/instance.js +264 -0
- package/dist/lsp/service.js +522 -0
- package/dist/lsp/tools.js +53 -0
- package/dist/lsp/types.js +201 -0
- package/dist/mcp/bridge.js +222 -0
- package/dist/mcp/codes.js +21 -0
- package/dist/mcp/index.js +19 -0
- package/dist/mcp/jsonrpc.js +230 -0
- package/dist/mcp/service.js +125 -0
- package/dist/mcp/tools.js +145 -0
- package/dist/mcp/types.js +108 -0
- package/dist/memory/cite.js +131 -0
- package/dist/memory/codes.js +50 -0
- package/dist/memory/command.js +71 -0
- package/dist/memory/consolidate.js +219 -0
- package/dist/memory/cycle.js +109 -0
- package/dist/memory/dao.js +878 -0
- package/dist/memory/diff.js +238 -0
- package/dist/memory/extract.js +181 -0
- package/dist/memory/fts.js +58 -0
- package/dist/memory/index.js +42 -0
- package/dist/memory/inject.js +354 -0
- package/dist/memory/merge.js +178 -0
- package/dist/memory/migration.js +132 -0
- package/dist/memory/pollution.js +59 -0
- package/dist/memory/port.js +385 -0
- package/dist/memory/review.js +201 -0
- package/dist/memory/scan.js +89 -0
- package/dist/memory/tools.js +508 -0
- package/dist/memory/types.js +140 -0
- package/dist/obs/codes.js +38 -0
- package/dist/obs/db.js +115 -0
- package/dist/obs/index.js +20 -0
- package/dist/obs/rollup.js +61 -0
- package/dist/obs/service.js +363 -0
- package/dist/obs/session-tools.js +282 -0
- package/dist/obs/session-view.js +225 -0
- package/dist/obs/tool.js +97 -0
- package/dist/obs/types.js +1 -0
- package/dist/persist/audit.js +63 -0
- package/dist/persist/aux.js +39 -0
- package/dist/persist/codes.js +35 -0
- package/dist/persist/index.js +30 -0
- package/dist/persist/load-history.js +50 -0
- package/dist/persist/migrations.js +37 -0
- package/dist/persist/paths.js +108 -0
- package/dist/persist/persistence.js +229 -0
- package/dist/persist/schema.js +99 -0
- package/dist/persist/secret-box.js +95 -0
- package/dist/persist/store.js +706 -0
- package/dist/persist/write-behind.js +238 -0
- package/dist/safety/approval.js +171 -0
- package/dist/safety/bwrap.js +123 -0
- package/dist/safety/codes.js +57 -0
- package/dist/safety/danger.js +505 -0
- package/dist/safety/gate.js +226 -0
- package/dist/safety/index.js +24 -0
- package/dist/safety/presets.js +73 -0
- package/dist/safety/roots.js +139 -0
- package/dist/safety/sandbox.js +214 -0
- package/dist/safety/seatbelt.js +88 -0
- package/dist/safety/sensitive.js +34 -0
- package/dist/safety/tool-policy.js +203 -0
- package/dist/safety/types.js +13 -0
- package/dist/scheduler/codes.js +54 -0
- package/dist/scheduler/cron-backend.js +135 -0
- package/dist/scheduler/engine.js +280 -0
- package/dist/scheduler/gates.js +60 -0
- package/dist/scheduler/index.js +25 -0
- package/dist/scheduler/migration.js +22 -0
- package/dist/scheduler/runner.js +171 -0
- package/dist/scheduler/schedule.js +193 -0
- package/dist/scheduler/service.js +283 -0
- package/dist/scheduler/tick.js +139 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/sdk/http.js +865 -0
- package/dist/sdk/index.js +22 -0
- package/dist/sdk/mcp.js +289 -0
- package/dist/sdk/plugin-route-registry.js +126 -0
- package/dist/sdk/plugin-routes.js +149 -0
- package/dist/sdk/security.js +116 -0
- package/dist/sdk/types.js +32 -0
- package/dist/session/budget.js +159 -0
- package/dist/session/codes.js +40 -0
- package/dist/session/derive.js +180 -0
- package/dist/session/event-data.js +1 -0
- package/dist/session/fork.js +72 -0
- package/dist/session/import-gates.js +148 -0
- package/dist/session/index.js +18 -0
- package/dist/session/recover.js +103 -0
- package/dist/session/session.js +279 -0
- package/dist/session/snapshot.js +107 -0
- package/dist/skills/agents.js +242 -0
- package/dist/skills/codes.js +51 -0
- package/dist/skills/discovery.js +266 -0
- package/dist/skills/frontmatter.js +215 -0
- package/dist/skills/index.js +28 -0
- package/dist/skills/load.js +118 -0
- package/dist/skills/manage.js +201 -0
- package/dist/skills/registry.js +138 -0
- package/dist/skills/render.js +124 -0
- package/dist/skills/sections.js +171 -0
- package/dist/skills/types.js +21 -0
- package/dist/subagent/codes.js +50 -0
- package/dist/subagent/declarative.js +55 -0
- package/dist/subagent/index.js +19 -0
- package/dist/subagent/notify.js +51 -0
- package/dist/subagent/provide.js +7 -0
- package/dist/subagent/registry.js +149 -0
- package/dist/subagent/service.js +338 -0
- package/dist/subagent/surface.js +44 -0
- package/dist/subagent/tool.js +153 -0
- package/dist/subagent/types.js +25 -0
- package/dist/tools/apply-patch.js +164 -0
- package/dist/tools/codes.js +109 -0
- package/dist/tools/fs.js +501 -0
- package/dist/tools/index.js +17 -0
- package/dist/tools/observed.js +99 -0
- package/dist/tools/pipeline.js +176 -0
- package/dist/tools/protected-read.js +54 -0
- package/dist/tools/registry.js +250 -0
- package/dist/tools/search.js +451 -0
- package/dist/tools/worktree.js +327 -0
- package/dist/web/codes.js +37 -0
- package/dist/web/gate.js +43 -0
- package/dist/web/hygiene.js +195 -0
- package/dist/web/index.js +16 -0
- package/dist/web/service.js +207 -0
- package/dist/web/ssrf-guard.js +44 -0
- package/dist/web/tool.js +76 -0
- package/dist/web/types.js +10 -0
- package/dist/webui/assets/index-B-t2O8gh.js +22 -0
- package/dist/webui/assets/index-DOlgSZ9Q.css +2 -0
- package/dist/webui/index.html +14 -0
- package/dist/webui/index.js +14 -0
- package/dist/webui/server.js +549 -0
- package/dist/webui/types.js +38 -0
- package/examples/README.md +26 -0
- package/examples/minimal-code-plugin/entry.js +43 -0
- package/examples/minimal-code-plugin/package.json +12 -0
- package/examples/pure-skill-pack/package.json +14 -0
- package/examples/pure-skill-pack/skills/markdown-table/SKILL.md +32 -0
- package/package.json +86 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/plugin-context — 插件上下文装配件(03 §2.1/§2.2/§2.4/§3.1/§3.4/§8.5;批 12f-2a)。
|
|
3
|
+
*
|
|
4
|
+
* 一件三面:
|
|
5
|
+
* - **ctx 十路注册动词 + 三动词 + provide**(§2.2/§3.1):tools.register(拒绝式)/ channels.
|
|
6
|
+
* registerCommand(后写胜出)/ channels.registerUiBackend(拒绝式·门检前置
|
|
7
|
+
* ——第十三动词,U3 批 U3-4:channels.ui-backend 开门制先于撞名律,分域
|
|
8
|
+
* 执法在 ChannelsService)/ llm.registerProvider(后写胜出 upsert)/ events.
|
|
9
|
+
* registerSessionEventType(拒绝式·不可逆)/ agent.registerMessageRole(拒绝式)/
|
|
10
|
+
* agent.registerSubagentProvider(拒绝式——第十二动词,D 批 D-2:执法序与
|
|
11
|
+
* owner 分域在 SubagentService)/ prompts.
|
|
12
|
+
* registerSection(拒绝式——本件 prompt-sections)/ triggers.register(拒绝式
|
|
13
|
+
* ·门检三闸——本件 triggers,C 批 C-2)/ ctx.on(fail-closed)/ ctx.emit +
|
|
14
|
+
* ctx.get·tryGet·effect + ctx.provide(§2.2 表行——委派共享根作用域,Kahn 解锁动词)
|
|
15
|
+
* ——逐动词冲突律真源 03 §2.7(本件只执法窗口与频率,撞名执法归各真源注册表)。
|
|
16
|
+
* - **装载窗口律**(§2.1):注册动词只在 apply 执行期间合法;窗口外抛
|
|
17
|
+
* `PLUGIN_WINDOW_CLOSED`。唯一例外 = 宿主回调上下文内(钩子 handler/工具执行期
|
|
18
|
+
* ——§2.4 主表注记「钩子内注册面变更同注册即生效」的钩子豁免条款),判定形 =
|
|
19
|
+
* 回调窗重入计数(enterHostCallback 开合)。get/tryGet/host 免窗——读面不设限。
|
|
20
|
+
* - **频率护栏 + 钩子消费点 5s 时钟**(§3.4 遗漏审计批三项挂账之余二):单插件
|
|
21
|
+
* 注册/事件动作滑动窗 1000ms 内 >1000 次即拒(`PLUGIN_RATE_LIMITED`——计数位 =
|
|
22
|
+
* 注册动词 + ctx.on + ctx.emit + ctx.effect);钩子 handler 包 5s 竞速钟(超时上报
|
|
23
|
+
* onHookTimeout——notify 腿超时放行收口不悬挂、waterfall 腿超时按管线失败传播)。
|
|
24
|
+
*
|
|
25
|
+
* 装配关系:装载器(loader.ts)createContext 注入位消费本件——apply 前 create、
|
|
26
|
+
* apply 收口后 closeWindow;钩子词汇 registerEventNames 预注册归装配批(12f-2b),
|
|
27
|
+
* 本件只按主表路由不注册词。
|
|
28
|
+
*
|
|
29
|
+
* 挂账注记(消费面未齐暂缓,随各自消费腿定形):registerRenderer(07 §4.1
|
|
30
|
+
* 渲染签名未钉——随 TUI/SPA 消费腿);ctx.sessions/ctx.host 动词包装
|
|
31
|
+
* (§4.4/§4.5——随 core:memory 消费腿);tools 管道 waterfall 派发位(pipeline.ts
|
|
32
|
+
* 归 tools 域——宿主发射位接线随装配批)。ctx.ui 七原语已随交互动词族批
|
|
33
|
+
* ix-2 兑销(消费腿条款 07 §4.3——会话锚定档位表/钩子窗禁律/护栏增位)。
|
|
34
|
+
*/
|
|
35
|
+
import { BaseError, registerEventType, registerMessageRole } from '../contracts/index.js';
|
|
36
|
+
// internal 桶机制符号深导(门检裁决核——03 §4.6;开门是宿主裁决面非插件 API)
|
|
37
|
+
import { adjudicateCapabilityDoor } from '../contracts/api.js';
|
|
38
|
+
// 接管缝归因铸造两律(U4-3——compaction 公开面机制件;host→compaction 边在册)
|
|
39
|
+
import { forgeBeforeCompactIdentity, markBeforeCompactRewrite } from '../compaction/index.js';
|
|
40
|
+
import { AGENT_TOOL_PREFIX } from '../subagent/types.js';
|
|
41
|
+
import { SESSION_LIFECYCLE_EVENT } from '../conversation/index.js';
|
|
42
|
+
import { readSessionAnchor, withoutSessionAnchor } from './session-anchor.js';
|
|
43
|
+
/**
|
|
44
|
+
* 钩子主表词汇镜像(03 §2.4:七层 35 + 生命周期与刷新组 6 = 41 词)。
|
|
45
|
+
* 词清单增删 = 规范主表先改再同步本表(词汇真源单点在规范;本表是 fail-closed
|
|
46
|
+
* 执法面 PLUGIN_HOOK_UNKNOWN 的判据源)。
|
|
47
|
+
*/
|
|
48
|
+
export const PLUGIN_HOOK_VOCABULARY = [
|
|
49
|
+
// session 层(7)
|
|
50
|
+
{ name: 'session/event', mode: 'emit' },
|
|
51
|
+
{ name: 'session_start', mode: 'emit' },
|
|
52
|
+
{ name: 'session_before_fork', mode: 'waterfall' },
|
|
53
|
+
{ name: 'session_before_compact', mode: 'waterfall' },
|
|
54
|
+
{ name: 'session_compact', mode: 'emit' },
|
|
55
|
+
{ name: 'session_flush', mode: 'serial' },
|
|
56
|
+
{ name: 'session_shutdown', mode: 'parallel' },
|
|
57
|
+
// agent 层(6)
|
|
58
|
+
{ name: 'agent_before_start', mode: 'waterfall' },
|
|
59
|
+
{ name: 'agent_start', mode: 'emit' },
|
|
60
|
+
{ name: 'agent_pre_step', mode: 'waterfall' },
|
|
61
|
+
{ name: 'agent_request_error', mode: 'waterfall' },
|
|
62
|
+
{ name: 'agent_end', mode: 'emit' },
|
|
63
|
+
{ name: 'agent_idle', mode: 'emit' },
|
|
64
|
+
// turn 层(3)
|
|
65
|
+
{ name: 'turn_start', mode: 'emit' },
|
|
66
|
+
{ name: 'turn_stopping', mode: 'serial' },
|
|
67
|
+
{ name: 'turn_end', mode: 'emit' },
|
|
68
|
+
// message 层(5)
|
|
69
|
+
{ name: 'message_start', mode: 'emit' },
|
|
70
|
+
{ name: 'message_update', mode: 'emit' },
|
|
71
|
+
{ name: 'message_end', mode: 'waterfall' },
|
|
72
|
+
{ name: 'context_transform', mode: 'waterfall' },
|
|
73
|
+
{ name: 'user_input', mode: 'waterfall' },
|
|
74
|
+
// tool 层(7)
|
|
75
|
+
{ name: 'tools_pre_execute', mode: 'waterfall' },
|
|
76
|
+
{ name: 'tools_execute', mode: 'waterfall' },
|
|
77
|
+
{ name: 'tools_post_execute', mode: 'waterfall' },
|
|
78
|
+
{ name: 'tool_execution_start', mode: 'emit' },
|
|
79
|
+
{ name: 'tool_execution_update', mode: 'emit' },
|
|
80
|
+
{ name: 'tool_execution_end', mode: 'emit' },
|
|
81
|
+
{ name: 'tools_change', mode: 'emit' },
|
|
82
|
+
// provider 层(4)
|
|
83
|
+
{ name: 'provider_before_request', mode: 'waterfall' },
|
|
84
|
+
{ name: 'provider_before_headers', mode: 'waterfall' },
|
|
85
|
+
{ name: 'provider_after_response', mode: 'emit' },
|
|
86
|
+
{ name: 'model_select', mode: 'waterfall' },
|
|
87
|
+
// 插件层(3)
|
|
88
|
+
{ name: 'resources_discover', mode: 'serial' },
|
|
89
|
+
{ name: 'project_trust', mode: 'waterfall' },
|
|
90
|
+
{ name: 'job_settled', mode: 'emit' },
|
|
91
|
+
// 生命周期与刷新组(6——plugin/activated 三词主表同排一行,此处拆三条目)
|
|
92
|
+
{ name: 'plugin/activated', mode: 'emit' },
|
|
93
|
+
{ name: 'plugin/failed', mode: 'emit' },
|
|
94
|
+
{ name: 'plugin/skipped', mode: 'emit' },
|
|
95
|
+
{ name: 'composition/reloaded', mode: 'emit' },
|
|
96
|
+
{ name: 'prompts_change', mode: 'emit' },
|
|
97
|
+
{ name: 'skills_change', mode: 'emit' },
|
|
98
|
+
];
|
|
99
|
+
/** 钩子词汇查 mode(未知词返回 undefined——ctx.on fail-closed 判据) */
|
|
100
|
+
const HOOK_MODES = new Map(PLUGIN_HOOK_VOCABULARY.map((h) => [h.name, h.mode]));
|
|
101
|
+
/** 缺省钩子超时上报(stderr 直写——装配根接 logger 前的先有鸡缺省) */
|
|
102
|
+
const defaultHookTimeoutReport = (pluginId, hookName, err) => {
|
|
103
|
+
console.error(`[host] 插件 ${pluginId} 钩子 ${hookName} 超时/异常`, err);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* 构造插件上下文(装载器 createContext 注入位——每插件一枚)。
|
|
107
|
+
* 窗口初态 = 开(构造即 apply 前——装载序紧邻);closeWindow 后走回调窗律。
|
|
108
|
+
*/
|
|
109
|
+
export function createPluginContext(options) {
|
|
110
|
+
const { pluginId, scope, dispatch, hostFace } = options;
|
|
111
|
+
const rateWindowMs = options.rateLimit?.windowMs ?? 1_000;
|
|
112
|
+
const rateMax = options.rateLimit?.max ?? 1_000;
|
|
113
|
+
const hookTimeoutMs = options.hookTimeoutMs ?? 5_000;
|
|
114
|
+
const onHookTimeout = options.onHookTimeout ?? defaultHookTimeoutReport;
|
|
115
|
+
// 装载窗口态(§2.1):初开——apply 期注册合法;closeWindow 后仅回调窗内合法
|
|
116
|
+
let windowOpen = true;
|
|
117
|
+
// 宿主回调窗重入计数(钩子 handler/工具执行期——嵌套回调支持)
|
|
118
|
+
let hostCallbackDepth = 0;
|
|
119
|
+
// 频率护栏滑动窗(§3.4:单插件注册/事件动作 1000 次/1000ms——数组剪枝)
|
|
120
|
+
const recentActions = [];
|
|
121
|
+
// 开门授予集物化(03 §4.6 批 U2——只进 handle 门检面,不进 ctx 插件面)
|
|
122
|
+
const grantedOpens = new Set(options.opens ?? []);
|
|
123
|
+
/**
|
|
124
|
+
* 高危面门检(§4.6 fail-loud):裁决核拒绝即抛门关码(同码分流——
|
|
125
|
+
* door-closed/not-a-door 两档共用 PLUGIN_CAPABILITY_DOOR_CLOSED,message
|
|
126
|
+
* 底稿 = verdict.message 原文)。
|
|
127
|
+
*/
|
|
128
|
+
const assertDoor = (capability) => {
|
|
129
|
+
const verdict = adjudicateCapabilityDoor(grantedOpens, capability);
|
|
130
|
+
if (!verdict.ok) {
|
|
131
|
+
throw new BaseError('PLUGIN_CAPABILITY_DOOR_CLOSED', `${verdict.message}(插件 ${pluginId})`);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* observe-cross 专属分立判定位(开门制扩展批 2026-09-09——03 §4.6 双源
|
|
136
|
+
* 并集律):行 opens(grantedOpens 物化)∥ doors 段活体**并判**——两源任一
|
|
137
|
+
* 含该门即过。**不并入 grantedOpens 全局面**:grantedOpens 系四枚插件道面
|
|
138
|
+
* (ui-backend 换装/路由受理/secrets/操控绑定)共用的装载期单集,doors 段
|
|
139
|
+
* 是运行期活体源——只在此判定位合成局部集喂裁决核(message 单源
|
|
140
|
+
* adjudicateCapabilityDoor),波及面结构性为零。
|
|
141
|
+
*/
|
|
142
|
+
const assertObserveCrossDoor = () => {
|
|
143
|
+
const doorsLive = options.crossDoors?.() ?? new Set();
|
|
144
|
+
const opened = grantedOpens.has('sessions.observe-cross') || doorsLive.has('sessions.observe-cross')
|
|
145
|
+
? new Set(['sessions.observe-cross'])
|
|
146
|
+
: grantedOpens; // 两源皆不含——喂原集,裁决核判 door-closed(message 单源拒词)
|
|
147
|
+
const verdict = adjudicateCapabilityDoor(opened, 'sessions.observe-cross');
|
|
148
|
+
if (!verdict.ok) {
|
|
149
|
+
throw new BaseError('PLUGIN_CAPABILITY_DOOR_CLOSED', `${verdict.message}(插件 ${pluginId})`);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/** 记一次动作:窗内已满即拒(fail-loud 先于受理——超限受理连动作都不发生) */
|
|
153
|
+
const countAction = () => {
|
|
154
|
+
const now = Date.now();
|
|
155
|
+
// 剪枝:滑出窗口的旧动作丢弃(数组头即最旧——单调递增,剪到首个窗内即止)
|
|
156
|
+
while (recentActions.length > 0 && now - recentActions[0] > rateWindowMs)
|
|
157
|
+
recentActions.shift();
|
|
158
|
+
if (recentActions.length >= rateMax) {
|
|
159
|
+
throw new BaseError('PLUGIN_RATE_LIMITED', `插件 ${pluginId} 注册/事件动作超频率护栏(滑动窗 ${rateWindowMs}ms 内已达 ${rateMax} 次)——失控登记防线(03 §3.4)`);
|
|
160
|
+
}
|
|
161
|
+
recentActions.push(now);
|
|
162
|
+
};
|
|
163
|
+
/** 注册动词窗口闸(§2.1——apply 窗或回调窗内合法;get/tryGet/host 免窗不经过本闸) */
|
|
164
|
+
const assertWindow = (verb) => {
|
|
165
|
+
if (windowOpen || hostCallbackDepth > 0)
|
|
166
|
+
return;
|
|
167
|
+
throw new BaseError('PLUGIN_WINDOW_CLOSED', `注册动词 ${verb} 在装载窗口外被拒(插件 ${pluginId}——03 §2.1:注册动词只在 apply 执行期间合法,唯一例外为宿主回调上下文内〔钩子 handler/工具执行期〕)`);
|
|
168
|
+
};
|
|
169
|
+
/** 受局面缺位执法(装配缺陷响亮——与 agentToolsFor 同判据 CONTEXT_SERVICE_MISSING) */
|
|
170
|
+
const required = (face, name, verb) => {
|
|
171
|
+
if (face === undefined) {
|
|
172
|
+
throw new BaseError('CONTEXT_SERVICE_MISSING', `注册动词 ${verb} 的受局面 ${name} 缺席(插件 ${pluginId}——装配根未接线该注册表;装配缺陷 fail-loud)`);
|
|
173
|
+
}
|
|
174
|
+
return face;
|
|
175
|
+
};
|
|
176
|
+
// ---- ctx.ui 消费腿(ix-2——07 §4.3 消费腿条款)----
|
|
177
|
+
/** 降档 warn 出口(缺省 console.warn——装配根接 logger.warn) */
|
|
178
|
+
const warnUi = (message) => {
|
|
179
|
+
(options.uiWarn ?? ((m) => console.warn(`[host] ${m}`)))(message);
|
|
180
|
+
};
|
|
181
|
+
/** 钩子窗禁律(判序窗判前置锚判——窗内即使显式 sessionId 亦拒:撞的是派发序非锚定面) */
|
|
182
|
+
const assertNotInHookDispatch = (verb) => {
|
|
183
|
+
if (options.hookDispatchGuard?.inHookDispatch?.() ?? false) {
|
|
184
|
+
throw new BaseError('UI_ASK_WINDOW_INVALID', `ctx.ui ${verb} 在钩子派发窗内被拒(插件 ${pluginId}——07 §4.3 钩子窗条:阻塞挂起与钩子消费钟/waterfall 短路序相撞;窗内即使显式 sessionId 亦拒)`);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* 阻塞三件锚解析(档位 2):显式 opts.sessionId 优先 → ambient 命令锚
|
|
189
|
+
* (ALS 语境继承——命令 handler 及其异步尾链零自觉锚定)回落;缺席拒
|
|
190
|
+
* UI_ASK_UNANCHORED(fail-loud——「问了没人答」不得伪装「用户答了否」);
|
|
191
|
+
* 锚时效:不在通道核在册集拒 UI_ASK_SESSION_CLOSED(陈年锚晚到不悬死)。
|
|
192
|
+
*/
|
|
193
|
+
const resolveAskAnchor = (verb, explicit) => {
|
|
194
|
+
const ui = required(options.channelsUi, 'channels-ui', `ctx.ui.${verb}`);
|
|
195
|
+
const sessionId = explicit ?? readSessionAnchor();
|
|
196
|
+
if (sessionId === undefined) {
|
|
197
|
+
throw new BaseError('UI_ASK_UNANCHORED', `ctx.ui ${verb} 无会话锚(插件 ${pluginId}——07 §4.3 档位 2:装载期/无锚后台语境 opts.sessionId 缺席且无 ambient 命令锚;命令 handler 内自动锚、尾链继承)`);
|
|
198
|
+
}
|
|
199
|
+
if (!ui.hasSession(sessionId)) {
|
|
200
|
+
throw new BaseError('UI_ASK_SESSION_CLOSED', `ctx.ui ${verb} 会话锚已收口(插件 ${pluginId},会话 ${sessionId}——未注册或已注销:命令尾链陈年锚晚到不悬死,与在队收口三则对称分立)`);
|
|
201
|
+
}
|
|
202
|
+
return { ui, sessionId };
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* 单向原语锚解析(档位 3):缺席/不在册 = no-op warn 一行(降档不炸装载/
|
|
206
|
+
* 不炸派发——问不到人可以不问,状态行更新炸装载属过罚失当);受局面
|
|
207
|
+
* 缺席同律降档(阻塞三件的 required 拒与此分档)。
|
|
208
|
+
*/
|
|
209
|
+
const resolveStatusAnchor = (verb, explicit) => {
|
|
210
|
+
const ui = options.channelsUi;
|
|
211
|
+
if (ui === undefined) {
|
|
212
|
+
warnUi(`ctx.ui ${verb} 受局面缺席(插件 ${pluginId})——no-op(装配根未接通道核窄面)`);
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
const sessionId = explicit ?? readSessionAnchor();
|
|
216
|
+
if (sessionId === undefined || !ui.hasSession(sessionId)) {
|
|
217
|
+
warnUi(`ctx.ui ${verb} 无有效会话锚(插件 ${pluginId}${sessionId === undefined ? '' : `,会话 ${sessionId} 已收口`})——no-op(07 §4.3 档位 3:单向原语降档不炸装载)`);
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
return { ui, sessionId };
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* 回调窗包裹:handler 体前后开合(同步/异步腿都收口——finally 恢复)。
|
|
224
|
+
* 本包裹只用于钩子派发两腿(waterfall/notify)——回调窗与钩子派发段窗
|
|
225
|
+
* (guard 深度计数,03 §3.4 执法)同开同合;工具执行体走 loader 侧
|
|
226
|
+
* enterHostCallback 外包,不经本面(钩子段禁模型、工具段不禁的分界位)。
|
|
227
|
+
*/
|
|
228
|
+
const withCallbackWindow = async (fn) => {
|
|
229
|
+
hostCallbackDepth++;
|
|
230
|
+
options.hookDispatchGuard?.enter(); // 钩子派发段开窗(await 跨度覆盖 handler 全执行段)
|
|
231
|
+
try {
|
|
232
|
+
// 钩子派发段语境遮蔽(ix-2——07 §4.3 档位 2 两处遮蔽之一):ambient
|
|
233
|
+
// 命令锚不穿透进钩子 handler(钩子语境结构性无自动锚——单向原语 no-op
|
|
234
|
+
// warn、阻塞三件另由窗判拒)。ALS.exit 语境跟随 async 执行:handler
|
|
235
|
+
// 全执行段(含 await 续体)均在遮蔽内;fire-and-forget 尾链(派发收口
|
|
236
|
+
// 后自起)在遮蔽外——ambient 锚自然恢复(「锚判随后自理」的边界即此)。
|
|
237
|
+
return await withoutSessionAnchor(fn);
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
options.hookDispatchGuard?.exit(); // 派发收口即闭窗(fire-and-forget 尾链窗外合法)
|
|
241
|
+
hostCallbackDepth--;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
/** 超时错误构造(时钟语义非域码语义——不占错误码册位;标记位供 notify 腿区分吞并档) */
|
|
245
|
+
const hookTimeoutError = (hookName) => Object.assign(new Error(`插件 ${pluginId} 钩子 ${hookName} 消费超时(${hookTimeoutMs}ms——03 §3.4 时钟)`), {
|
|
246
|
+
hookTimedOut: true,
|
|
247
|
+
});
|
|
248
|
+
/** 值保真竞速钟(§3.4 钩子消费点 5s——超时 reject 携标记位;notify 腿据此吞并收口、waterfall 腿按管线失败传播) */
|
|
249
|
+
const raceTimeout = (p, hookName) => {
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
const timer = setTimeout(() => reject(hookTimeoutError(hookName)), hookTimeoutMs);
|
|
252
|
+
Promise.resolve(p).then((value) => {
|
|
253
|
+
clearTimeout(timer);
|
|
254
|
+
resolve(value);
|
|
255
|
+
}, (err) => {
|
|
256
|
+
clearTimeout(timer);
|
|
257
|
+
reject(err);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* waterfall 腿分段钟(§3.4 射程精确化——2026-09-13 真模型五轮定罪件):
|
|
263
|
+
* 钟只测插件自有执行段——首段(进 handler 到首调 next)与回程段(next
|
|
264
|
+
* resolve 后到返回)各 hookTimeoutMs;next 委派的下游链(宿主守门行/
|
|
265
|
+
* 其余监听器——含审批 ask 悬置等待)是宿主管线时间,不归因本插件钟:
|
|
266
|
+
* 首调即停钟、resolve 后重起。全程无 next(短路形)= 单段全程钟(与
|
|
267
|
+
* notify 腿同射程)。曾用全程钟致交互审批结构性不可用(checkpoint 观察
|
|
268
|
+
* 钩子 await next 包住 safety 审批等待,>5s 必 fail-closed block)。
|
|
269
|
+
*/
|
|
270
|
+
const createWaterfallClock = (hookName) => {
|
|
271
|
+
let timer;
|
|
272
|
+
let settled = false; // 派发已收口(成功/失败/超时)——此后停/起皆 no-op
|
|
273
|
+
let paused = false; // 粘性暂停位:next 已委派、下游链在飞——arm 不生效(resume 清位)
|
|
274
|
+
let rejectOuter;
|
|
275
|
+
const arm = () => {
|
|
276
|
+
if (settled || paused || rejectOuter === undefined || timer !== undefined)
|
|
277
|
+
return;
|
|
278
|
+
timer = setTimeout(() => {
|
|
279
|
+
timer = undefined;
|
|
280
|
+
settled = true; // 超时即收口——迟到结果弃置(与 raceTimeout 同语义)
|
|
281
|
+
rejectOuter?.(hookTimeoutError(hookName));
|
|
282
|
+
}, hookTimeoutMs);
|
|
283
|
+
};
|
|
284
|
+
const disarm = () => {
|
|
285
|
+
if (timer !== undefined)
|
|
286
|
+
clearTimeout(timer);
|
|
287
|
+
timer = undefined;
|
|
288
|
+
};
|
|
289
|
+
return {
|
|
290
|
+
/**
|
|
291
|
+
* 段边界:next 委派时粘性停钟(下游链不归因插件钟——§3.4 射程精确化)。
|
|
292
|
+
* 粘性 = handler 同步前缀即调 next 的形下 pause 先于 race/arm 执行,
|
|
293
|
+
* 非粘性位会被随后的 arm 抵消(首段钟白起——下游等待仍被计时)。
|
|
294
|
+
*/
|
|
295
|
+
pause: () => {
|
|
296
|
+
disarm();
|
|
297
|
+
paused = true;
|
|
298
|
+
},
|
|
299
|
+
/** 段边界:next 返回后重起钟(回程段自有执行再计时) */
|
|
300
|
+
resume: () => {
|
|
301
|
+
paused = false;
|
|
302
|
+
arm();
|
|
303
|
+
},
|
|
304
|
+
/** 竞速入口(与 raceTimeout 同收口语义:settle 后迟到值弃置) */
|
|
305
|
+
race(p) {
|
|
306
|
+
return new Promise((resolve, reject) => {
|
|
307
|
+
rejectOuter = reject;
|
|
308
|
+
arm();
|
|
309
|
+
Promise.resolve(p).then((value) => {
|
|
310
|
+
disarm();
|
|
311
|
+
settled = true;
|
|
312
|
+
resolve(value);
|
|
313
|
+
}, (err) => {
|
|
314
|
+
disarm();
|
|
315
|
+
settled = true;
|
|
316
|
+
reject(err);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
/** 钩子消费点超时判定(标记位判据——raceTimeout 超时腿专属) */
|
|
323
|
+
const isHookTimeout = (err) => err instanceof Error && err.hookTimedOut === true;
|
|
324
|
+
const ctx = {
|
|
325
|
+
get(name) {
|
|
326
|
+
try {
|
|
327
|
+
return scope.get(name);
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
// 缺席报错附现行服务目录(§3.1「服务目录运行时可枚举」——点名错误当场可诊)
|
|
331
|
+
if (err instanceof BaseError && err.code === 'CONTEXT_SERVICE_MISSING') {
|
|
332
|
+
throw new BaseError('CONTEXT_SERVICE_MISSING', `服务 ${name} 缺席(插件 ${pluginId};现行服务目录:${scope.serviceNames().join('、') || '(空)'}——拼错名/装载序缺口,可选消费用 tryGet)`);
|
|
333
|
+
}
|
|
334
|
+
throw err;
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
tryGet(name) {
|
|
338
|
+
return scope.tryGet(name);
|
|
339
|
+
},
|
|
340
|
+
provide(name, service) {
|
|
341
|
+
// 注册动词族:吃窗口闸 + 计频率数;真实落库委派共享根(撞名/stale 归 Scope 执法)
|
|
342
|
+
assertWindow('ctx.provide');
|
|
343
|
+
countAction();
|
|
344
|
+
required(options.provide, 'provide', 'ctx.provide')(name, service);
|
|
345
|
+
},
|
|
346
|
+
effect(register) {
|
|
347
|
+
countAction();
|
|
348
|
+
scope.effect(register);
|
|
349
|
+
},
|
|
350
|
+
on(hookName, handler) {
|
|
351
|
+
// ctx.on 属注册动词族(§2.2 主表末行)——吃窗口闸;ctx.effect 属 §3.1 三动词
|
|
352
|
+
// (消费面)不入闸,只计频率数(其自有护栏 = Scope stale/容量 + 本件计数)
|
|
353
|
+
assertWindow('ctx.on');
|
|
354
|
+
countAction();
|
|
355
|
+
const mode = HOOK_MODES.get(hookName);
|
|
356
|
+
if (mode === undefined) {
|
|
357
|
+
// fail-closed:未知钩子不静默吞(03 §2.4/§2.7)
|
|
358
|
+
throw new BaseError('PLUGIN_HOOK_UNKNOWN', `钩子 ${hookName} 不在主表(插件 ${pluginId} 订阅被拒——词汇真源 03 §2.4,41 词清单见 PLUGIN_HOOK_VOCABULARY)`);
|
|
359
|
+
}
|
|
360
|
+
if (mode === 'waterfall') {
|
|
361
|
+
// waterfall 腿:真 next 委托链(不调即短路——管线语义);超时按管线失败
|
|
362
|
+
// 传播(宿主发射位决定处置),另经 onHookTimeout 上报观测面。
|
|
363
|
+
// U4-3 归因铸造位(03 §2.4 session_before_compact 行/05 §2.1):值链
|
|
364
|
+
// 改写者记名(mark——末位改写者归因,非接管缝值零接触)+ takeover
|
|
365
|
+
// .pluginId 铸造覆写(forge——插件自填被覆写,冒名结构性不存在)。
|
|
366
|
+
// 两律只对携带归因箱的值生效(宿主派发包装层种子),通用 waterfall
|
|
367
|
+
// 钩子结构性不受影响。
|
|
368
|
+
const wrapped = ((value, next) => {
|
|
369
|
+
let delegated = false; // 委托标记:产物归因在入口侧完成,出口侧不重复记名(下游改写归下游)
|
|
370
|
+
// 归因两律的单监听者应用位(U4-3):改写值(引用变)→ 记名;接管位
|
|
371
|
+
// 引用换 → 铸造覆写(原引用透传不覆写——防下游误夺上游接管归因)
|
|
372
|
+
const attributed = (produced) => {
|
|
373
|
+
if (produced !== value)
|
|
374
|
+
markBeforeCompactRewrite(produced, pluginId);
|
|
375
|
+
return forgeBeforeCompactIdentity(produced, value, pluginId);
|
|
376
|
+
};
|
|
377
|
+
// 分段钟(§3.4 射程精确化):包装 next——首调停钟(下游链是宿主
|
|
378
|
+
// 管线时间不归因本插件钟),resolve 后重起(回程段自有执行再计时)
|
|
379
|
+
const clock = createWaterfallClock(hookName);
|
|
380
|
+
const gatedNext = (nextValue) => {
|
|
381
|
+
delegated = true;
|
|
382
|
+
clock.pause();
|
|
383
|
+
return Promise.resolve(next(attributed(nextValue))).finally(() => clock.resume());
|
|
384
|
+
};
|
|
385
|
+
return withCallbackWindow(() => clock.race(Promise.resolve(handler(value, gatedNext)).then((result) => {
|
|
386
|
+
if (delegated)
|
|
387
|
+
return result;
|
|
388
|
+
// 短路形:未委托即返回——返回值即管线终值,同律记名铸造
|
|
389
|
+
return attributed(result);
|
|
390
|
+
})));
|
|
391
|
+
});
|
|
392
|
+
// 受理成功尾逐笔落账(T9 案一批 t-2——05 §1.1 hook/registered):词汇
|
|
393
|
+
// 闸/窗闸已过、dispatch.onWaterfall 返回即受理成功;「谁在挂瀑布」的
|
|
394
|
+
// durable 归因(context_transform 全隐身注册就此有账)。拒路径
|
|
395
|
+
// (上方 throw)零审计——被拒的注册不是行为
|
|
396
|
+
options.auditSink?.append('hook/registered', { pluginId, hook: hookName });
|
|
397
|
+
return dispatch.onWaterfall(hookName, wrapped);
|
|
398
|
+
}
|
|
399
|
+
// emit/serial/parallel 共用通知型监听面(dispatch.on)——next 为直通占位
|
|
400
|
+
// (notify 腿无值语义,调用无害);超时腿上报后吞并收口不悬挂(emit 隔离
|
|
401
|
+
// 语义:单腿超时不波及他腿与调用方;迟到结果弃置);真异常照抛——dispatch
|
|
402
|
+
// 隔离腿上报(onListenerError)照常,与超时档分道
|
|
403
|
+
const passthroughNext = (v) => Promise.resolve(v);
|
|
404
|
+
const wrapped = (data) => withCallbackWindow(() => raceTimeout(Promise.resolve(handler(data, passthroughNext)), hookName).catch((err) => {
|
|
405
|
+
if (!isHookTimeout(err))
|
|
406
|
+
throw err;
|
|
407
|
+
onHookTimeout(pluginId, hookName, err);
|
|
408
|
+
}));
|
|
409
|
+
// 受理成功尾逐笔落账(T9 案一批 t-2——同 waterfall 腿):notify 族
|
|
410
|
+
// (emit/serial/parallel)的受理账与瀑布腿同词同形
|
|
411
|
+
options.auditSink?.append('hook/registered', { pluginId, hook: hookName });
|
|
412
|
+
return dispatch.on(hookName, wrapped);
|
|
413
|
+
},
|
|
414
|
+
emit(name, data) {
|
|
415
|
+
countAction();
|
|
416
|
+
// 域名律:活体事件词须以本插件域起头(03 §2.2 尾注「自定义事件名走域名前缀」——
|
|
417
|
+
// 兄弟插件域词 emit 即冒名广播,结构性不可达)
|
|
418
|
+
const prefix = `${pluginId}/`;
|
|
419
|
+
if (!name.startsWith(prefix)) {
|
|
420
|
+
throw new BaseError('EVENT_NOT_REGISTERED', `事件词 ${name} 未以本插件域起头(须 ${prefix}…——域名前缀纪律,03 §2.2;emit 只广播自域词)`);
|
|
421
|
+
}
|
|
422
|
+
// 自域词自动注册一次(活体事件注册面无词汇门禁——不落日志,03 §2.2 尾注;
|
|
423
|
+
// 兄弟插件同域词不可能(域前缀互斥),重复注册仅发生在本插件重复 emit 首词)
|
|
424
|
+
if (!dispatch.isRegistered(name))
|
|
425
|
+
dispatch.registerEventNames([name]);
|
|
426
|
+
return dispatch.emit(name, data);
|
|
427
|
+
},
|
|
428
|
+
tools: {
|
|
429
|
+
register(def, opts) {
|
|
430
|
+
assertWindow('ctx.tools.register');
|
|
431
|
+
countAction();
|
|
432
|
+
const registry = required(options.tools, 'tools', 'ctx.tools.register');
|
|
433
|
+
// agent_ 前缀保留字闸(03 §2.7 行 254——拒绝式同码):named provider
|
|
434
|
+
// 派生工具名专属段(04 §10 程序化注册槽),插件先占 agent_xxx 位即
|
|
435
|
+
// 反锁后续 named provider 注册(拒绝服务窗)——保留字在注册面执法、
|
|
436
|
+
// 执法位写死注册侧(本动词;机器侧放行 host 物化腿的派生工具注册——
|
|
437
|
+
// 豁免位 = core:subagent 件:声明式/程序化子代理 agent_<name> 静态
|
|
438
|
+
// 工具经本动词物化,批 19c-1 注释条款兑现)
|
|
439
|
+
if (def.name.startsWith(AGENT_TOOL_PREFIX) && pluginId !== 'core:subagent') {
|
|
440
|
+
throw new BaseError('TOOL_NAME_CONFLICT', `工具名「${def.name}」携 ${AGENT_TOOL_PREFIX} 前缀——保留字段(named provider 派生工具名专属段,03 §2.7/04 §10),请改名注册`);
|
|
441
|
+
}
|
|
442
|
+
// 工具名账包壳(装载史批 h-3——05 §9 世代行 tools 列真值源):注册
|
|
443
|
+
// 成功才入账(前置闸全过 + 真源 register 返回即成功);disposer 包装
|
|
444
|
+
// 出账(代内撤注不留残影——世代行 tools = 收口时点在册集)
|
|
445
|
+
// owner 归因铸造(03 §2.3 尾注——T9 案一批 R1):无条件覆写为注册者
|
|
446
|
+
// pluginId(fork 闭包单源)——插件自报 owner 值恒不达注册表(冒名
|
|
447
|
+
// 结构性不存在;覆写无条件非「缺省补齐」);agent_ 派生族经本动词
|
|
448
|
+
// 注册(core:subagent 域物化)自然铸得 'core:subagent',无需另立位
|
|
449
|
+
const inner = registry.register({ ...def, owner: pluginId }, opts);
|
|
450
|
+
options.toolLedger?.add(pluginId, def.name);
|
|
451
|
+
return () => {
|
|
452
|
+
inner();
|
|
453
|
+
options.toolLedger?.remove(pluginId, def.name);
|
|
454
|
+
};
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
channels: {
|
|
458
|
+
registerCommand(name, handler, description) {
|
|
459
|
+
assertWindow('ctx.channels.registerCommand');
|
|
460
|
+
countAction();
|
|
461
|
+
return required(options.commands, 'commands', 'ctx.channels.registerCommand').register(name, handler, description);
|
|
462
|
+
},
|
|
463
|
+
registerUiBackend(backend) {
|
|
464
|
+
assertWindow('ctx.channels.registerUiBackend');
|
|
465
|
+
countAction();
|
|
466
|
+
// 门检前置(03 §2.7 第十三动词执法序):channels.ui-backend 高危面
|
|
467
|
+
// 开门制先于撞名律——未开门插件连 ChannelsService 都不可达,探测不到
|
|
468
|
+
// 宿主/插件两域名单(§4.6 默认关语义)
|
|
469
|
+
assertDoor('channels.ui-backend');
|
|
470
|
+
const disposer = required(options.uiBackends, 'uiBackends', 'ctx.channels.registerUiBackend').registerPluginBackend(backend);
|
|
471
|
+
// capability/used 落账(05 §1.1):门检通过 + 受理成功后落(拒笔不
|
|
472
|
+
// 记使用——没发生的使用不是使用);sink 缺席 = 诊断形不落账不阻拦
|
|
473
|
+
options.auditSink?.append('capability/used', { pluginId, capability: 'channels.ui-backend' });
|
|
474
|
+
return disposer;
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
llm: {
|
|
478
|
+
registerProvider(provider) {
|
|
479
|
+
assertWindow('ctx.llm.registerProvider');
|
|
480
|
+
countAction();
|
|
481
|
+
return required(options.llm, 'llm', 'ctx.llm.registerProvider').registerProvider(provider);
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
events: {
|
|
485
|
+
registerSessionEventType(meta) {
|
|
486
|
+
assertWindow('ctx.events.registerSessionEventType');
|
|
487
|
+
countAction();
|
|
488
|
+
// 不可逆动词:词汇注册进程生命周期(dispatch 同生命周期——open-tools 批已裁);
|
|
489
|
+
// 返回 void 无 disposer 是§2.2「每个注册动词返回 disposer 或记入 ctx.effect」的明文例外
|
|
490
|
+
registerEventType(meta);
|
|
491
|
+
},
|
|
492
|
+
subscribeSessionLifecycle(handler, opts) {
|
|
493
|
+
assertWindow('ctx.events.subscribeSessionLifecycle');
|
|
494
|
+
countAction();
|
|
495
|
+
// 局部名避开外层 scope(Scope 根实例)——观测作用域词面
|
|
496
|
+
const observeScope = opts?.scope ?? 'all'; // 插件无「本会话」——缺省档唯一无锚形 all
|
|
497
|
+
// 作用域坏形 fail-loud(04 §6 e-2 定形):非三值词面 / self·tree 档锚
|
|
498
|
+
// 缺席——静默升 all 档等价绕门、静默降 self 档等价丢事件,两向都拒
|
|
499
|
+
if (observeScope !== 'self' && observeScope !== 'tree' && observeScope !== 'all') {
|
|
500
|
+
throw new BaseError('SESSION_OBSERVE_SCOPE_INVALID', `订阅作用域「${String(observeScope)}」非三值词面 self|tree|all(插件 ${pluginId}——04 §6 作用域参数闭集)`);
|
|
501
|
+
}
|
|
502
|
+
const anchorId = observeScope === 'all' ? undefined : opts?.sessionId;
|
|
503
|
+
if (observeScope !== 'all' && (anchorId === undefined || anchorId === '')) {
|
|
504
|
+
throw new BaseError('SESSION_OBSERVE_SCOPE_INVALID', `订阅作用域 ${observeScope} 需 sessionId 锚(插件 ${pluginId}——self 档 = 锚会话自身、tree 档 = 锚会话血缘树;缺锚不可过滤)`);
|
|
505
|
+
}
|
|
506
|
+
// all 档门检 + 审计(sessions.observe-cross 高危面——拉取/订阅一枚统摄;
|
|
507
|
+
// 审计一次于受理成功〔registerUiBackend 同形〕非逐事件——推送非使用动作)。
|
|
508
|
+
// 门检走 observe-cross 专属分立判定位(开门制扩展批——行 opens ∥ doors
|
|
509
|
+
// 段并判,见 assertObserveCrossDoor 注)
|
|
510
|
+
if (observeScope === 'all') {
|
|
511
|
+
assertObserveCrossDoor();
|
|
512
|
+
options.auditSink?.append('capability/used', {
|
|
513
|
+
pluginId,
|
|
514
|
+
capability: 'sessions.observe-cross',
|
|
515
|
+
verb: 'subscribeSessionLifecycle',
|
|
516
|
+
scope: 'all',
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
// tree 档过滤消费血缘判定面(装配根 SessionView.isSameTree 单源)
|
|
520
|
+
const lineage = observeScope === 'tree'
|
|
521
|
+
? required(options.sessionLineage, 'sessionLineage', 'ctx.events.subscribeSessionLifecycle')
|
|
522
|
+
: undefined;
|
|
523
|
+
const unsubscribe = dispatch.on(SESSION_LIFECYCLE_EVENT, (event) => {
|
|
524
|
+
const lifecycle = event;
|
|
525
|
+
if (observeScope === 'all') {
|
|
526
|
+
handler(lifecycle);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
// self 档:同 id 判定;tree 档:同 id 特例天然含于 isSameTree(链上溯)。
|
|
530
|
+
// anchorId 守卫 = 类型收窄位(受理时已验非空——此处恒真,不重复执法)
|
|
531
|
+
if (lifecycle.sessionId === anchorId) {
|
|
532
|
+
handler(lifecycle);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (observeScope === 'tree' &&
|
|
536
|
+
anchorId !== undefined &&
|
|
537
|
+
lineage !== undefined && // 类型收窄位(tree 档受理时 lineage 必经 required——恒真)
|
|
538
|
+
lineage.isSameTree(lifecycle.sessionId, anchorId)) {
|
|
539
|
+
handler(lifecycle);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
// 订阅回卷律(04 §6——unload 撤订):LIFO effect 挂插件作用域(effect
|
|
543
|
+
// 形参 = 注册函数返退订闭包;重复退订幂等——splice indexOf 二跑 no-op)
|
|
544
|
+
scope.effect(() => unsubscribe);
|
|
545
|
+
return unsubscribe;
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
agent: {
|
|
549
|
+
registerMessageRole(role, definition) {
|
|
550
|
+
assertWindow('ctx.agent.registerMessageRole');
|
|
551
|
+
countAction();
|
|
552
|
+
return registerMessageRole(role, definition);
|
|
553
|
+
},
|
|
554
|
+
registerSubagentProvider(def) {
|
|
555
|
+
assertWindow('ctx.agent.registerSubagentProvider');
|
|
556
|
+
countAction();
|
|
557
|
+
// 第十二动词(03 §2.2 行 109——注册者 id 即 owner 分域键,本插件
|
|
558
|
+
// 身份由 ctx 闭包携带不假手插件自报——防冒名);消费腿 = 注册即
|
|
559
|
+
// 派生(04 §10 程序化注册槽):service 位落册后物化 agent_<name>
|
|
560
|
+
// 静态工具(物化面缺席 = 诊断形只落 service 位);物化拒(撞名等)
|
|
561
|
+
// 整体拒——service 位回滚,半注册结构性不存在
|
|
562
|
+
const disposeProvider = required(options.subagents, 'subagents', 'ctx.agent.registerSubagentProvider').registerProgrammatic(pluginId, def);
|
|
563
|
+
let disposeTool;
|
|
564
|
+
if (options.subagentToolMaterializer !== undefined) {
|
|
565
|
+
try {
|
|
566
|
+
disposeTool = options.subagentToolMaterializer(def);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
disposeProvider(); // 回滚 service 位(注册整体拒)
|
|
570
|
+
throw err;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return () => {
|
|
574
|
+
disposeProvider();
|
|
575
|
+
disposeTool?.(); // 工具注册位与 service 位同生共死
|
|
576
|
+
};
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
prompts: {
|
|
580
|
+
registerSection(slot, builder, registerOptions) {
|
|
581
|
+
assertWindow('ctx.prompts.registerSection');
|
|
582
|
+
countAction();
|
|
583
|
+
return required(options.promptSections, 'promptSections', 'ctx.prompts.registerSection').register(slot, pluginId, builder, registerOptions);
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
triggers: {
|
|
587
|
+
register(def) {
|
|
588
|
+
assertWindow('ctx.triggers.register');
|
|
589
|
+
countAction();
|
|
590
|
+
return required(options.triggers, 'triggers', 'ctx.triggers.register').register(pluginId, def);
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
ui: {
|
|
594
|
+
// 档位 1:notify 无会话位恒可(核层恒扇出)——只过护栏与受局面
|
|
595
|
+
notify(message, opts) {
|
|
596
|
+
countAction();
|
|
597
|
+
required(options.channelsUi, 'channels-ui', 'ctx.ui.notify').notify(message, opts);
|
|
598
|
+
},
|
|
599
|
+
// 档位 2:阻塞三件——判序护栏 → 窗判前置 → 锚解析(显式优先/ALS 回落
|
|
600
|
+
// /缺席拒/不在册拒);opts 剥 sessionId 后透传核层形(核层 sessionId 是首参)
|
|
601
|
+
confirm(message, opts) {
|
|
602
|
+
countAction();
|
|
603
|
+
assertNotInHookDispatch('confirm');
|
|
604
|
+
const { ui, sessionId } = resolveAskAnchor('confirm', opts?.sessionId);
|
|
605
|
+
const { signal } = opts ?? {};
|
|
606
|
+
return ui.confirm(sessionId, message, signal !== undefined ? { signal } : undefined);
|
|
607
|
+
},
|
|
608
|
+
select(message, choices, opts) {
|
|
609
|
+
countAction();
|
|
610
|
+
assertNotInHookDispatch('select');
|
|
611
|
+
const { ui, sessionId } = resolveAskAnchor('select', opts?.sessionId);
|
|
612
|
+
const { signal } = opts ?? {};
|
|
613
|
+
return ui.select(sessionId, message, choices, signal !== undefined ? { signal } : undefined);
|
|
614
|
+
},
|
|
615
|
+
input(message, opts) {
|
|
616
|
+
countAction();
|
|
617
|
+
assertNotInHookDispatch('input');
|
|
618
|
+
const { ui, sessionId } = resolveAskAnchor('input', opts?.sessionId);
|
|
619
|
+
const { signal, placeholder } = opts ?? {};
|
|
620
|
+
const askOpts = signal !== undefined || placeholder !== undefined
|
|
621
|
+
? { ...(signal !== undefined ? { signal } : {}), ...(placeholder !== undefined ? { placeholder } : {}) }
|
|
622
|
+
: undefined;
|
|
623
|
+
return ui.input(sessionId, message, askOpts);
|
|
624
|
+
},
|
|
625
|
+
// 档位 3:单向原语——无有效锚 no-op warn(不炸装载/不炸派发)
|
|
626
|
+
setStatus(status, opts) {
|
|
627
|
+
countAction();
|
|
628
|
+
const resolved = resolveStatusAnchor('setStatus', opts?.sessionId);
|
|
629
|
+
if (resolved !== undefined)
|
|
630
|
+
resolved.ui.setStatus(resolved.sessionId, status);
|
|
631
|
+
},
|
|
632
|
+
setWidget(node, opts) {
|
|
633
|
+
countAction();
|
|
634
|
+
const resolved = resolveStatusAnchor('setWidget', opts?.sessionId);
|
|
635
|
+
if (resolved !== undefined)
|
|
636
|
+
resolved.ui.setWidget(resolved.sessionId, node);
|
|
637
|
+
},
|
|
638
|
+
// 档位 1:观众探针——只读免护栏计数(03 §3.4 免计清单)
|
|
639
|
+
hasAudience() {
|
|
640
|
+
return required(options.channelsUi, 'channels-ui', 'ctx.ui.hasAudience').hasAudience();
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
host: { ...hostFace, pluginId },
|
|
644
|
+
};
|
|
645
|
+
return {
|
|
646
|
+
ctx,
|
|
647
|
+
closeWindow() {
|
|
648
|
+
windowOpen = false; // 幂等(重复关窗无副作用)
|
|
649
|
+
},
|
|
650
|
+
enterHostCallback() {
|
|
651
|
+
hostCallbackDepth++;
|
|
652
|
+
let restored = false;
|
|
653
|
+
return () => {
|
|
654
|
+
// 恢复闭包幂等(异常路径双调防御——深度不减穿零)
|
|
655
|
+
if (restored)
|
|
656
|
+
return;
|
|
657
|
+
restored = true;
|
|
658
|
+
hostCallbackDepth--;
|
|
659
|
+
};
|
|
660
|
+
},
|
|
661
|
+
grantedOpens,
|
|
662
|
+
// 回调窗判定只读面(深度 > 0 = 窗内——嵌套取「在窗内」语义)
|
|
663
|
+
get inHostCallback() {
|
|
664
|
+
return hostCallbackDepth > 0;
|
|
665
|
+
},
|
|
666
|
+
// 装载窗判定只读面(closeWindow 前 = apply 期 true)
|
|
667
|
+
get inLoadWindow() {
|
|
668
|
+
return windowOpen;
|
|
669
|
+
},
|
|
670
|
+
assertDoor,
|
|
671
|
+
};
|
|
672
|
+
}
|