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,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 事件分派四模式(04 §6「作用域与事件四模式」后半——分派契约)。
|
|
3
|
+
*
|
|
4
|
+
* 四模式(模式是事件契约的一部分):
|
|
5
|
+
* - emit:广播——全部监听器各跑各,异常互相隔离(单腿炸不波及他腿与调用方);
|
|
6
|
+
* - waterfall:串行管线——监听器必须调 next() 委托、不调即短路(管线后段
|
|
7
|
+
* 不再执行;守门行语义依赖它);异常沿链传播(管线失败);
|
|
8
|
+
* - parallel:全体并发,Promise.all 收口(异常传播);
|
|
9
|
+
* - serial:排队串行(不短路,与 waterfall 的区别 = 无委托链);异常隔离
|
|
10
|
+
* 不停后段(上报后继续)。
|
|
11
|
+
*
|
|
12
|
+
* 词汇纪律:自定义事件名走域名前缀(件/插件域);未注册词 emit/on 均
|
|
13
|
+
* fail-loud(EVENT_NOT_REGISTERED)、撞名 EVENT_DUPLICATE 拒静默覆盖。
|
|
14
|
+
*/
|
|
15
|
+
import { BaseError } from '../contracts/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* 事件分派器:词汇注册表 + 四模式派发。一个 ctx 一枚(装载运行时的通信契约
|
|
18
|
+
* 单源——双实现 = 通信割裂)。
|
|
19
|
+
*/
|
|
20
|
+
export class EventDispatch {
|
|
21
|
+
/** 词汇注册表(name → 已注册标记;单源执法面) */
|
|
22
|
+
registered = new Set();
|
|
23
|
+
/** 监听器表(name → 注册序监听器列表;on 返回退订闭包;泛型在存储侧擦除为 unknown、取回侧 cast 还原) */
|
|
24
|
+
listeners = new Map();
|
|
25
|
+
/** serial 排队尾(per-name 链式 promise——排队串行) */
|
|
26
|
+
serialQueues = new Map();
|
|
27
|
+
/** 隔离腿异常上报(缺省 stderr 直写;装配根接 logger) */
|
|
28
|
+
onListenerError;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.onListenerError =
|
|
31
|
+
options?.onListenerError ??
|
|
32
|
+
((eventName, err) => {
|
|
33
|
+
// 缺省面:stderr 直写(context 自身的 logger 尚未装配——先有鸡问题)
|
|
34
|
+
console.error(`[context] 事件监听器异常(${eventName})`, err);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/** 注册事件词汇(装载期声明面);撞名拒 EVENT_DUPLICATE(禁静默覆盖) */
|
|
38
|
+
registerEventNames(names) {
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
if (this.registered.has(name)) {
|
|
41
|
+
throw new BaseError('EVENT_DUPLICATE', `事件词汇 ${name} 已注册——拒静默覆盖(词汇注册表单源)`);
|
|
42
|
+
}
|
|
43
|
+
this.registered.add(name);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 判别词汇是否已注册(装配断言/诊断面) */
|
|
47
|
+
isRegistered(name) {
|
|
48
|
+
return this.registered.has(name);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 挂通知型监听器(emit/serial 共用监听面——同词两模式收同一监听器形态)。
|
|
52
|
+
* 词未注册拒 EVENT_NOT_REGISTERED(fail-loud 禁散播未声明事件名)。
|
|
53
|
+
* @returns 退订闭包(消费方经 scope.effect 组合生命周期)
|
|
54
|
+
*/
|
|
55
|
+
on(name, listener) {
|
|
56
|
+
this.assertRegistered(name);
|
|
57
|
+
const list = this.listeners.get(name) ?? [];
|
|
58
|
+
const entry = { notify: listener };
|
|
59
|
+
list.push(entry);
|
|
60
|
+
this.listeners.set(name, list);
|
|
61
|
+
return () => {
|
|
62
|
+
const current = this.listeners.get(name);
|
|
63
|
+
if (!current)
|
|
64
|
+
return;
|
|
65
|
+
const idx = current.indexOf(entry);
|
|
66
|
+
if (idx >= 0)
|
|
67
|
+
current.splice(idx, 1);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 广播:全部监听器并发各跑各,异常互相隔离(单腿炸不波及他腿与调用方——
|
|
72
|
+
* 上报 onListenerError 后照常收口)。
|
|
73
|
+
*/
|
|
74
|
+
async emit(name, data) {
|
|
75
|
+
this.assertRegistered(name);
|
|
76
|
+
const legs = (this.listeners.get(name) ?? []).map((entry) => entry.notify);
|
|
77
|
+
await Promise.all(legs.map(async (listener) => {
|
|
78
|
+
try {
|
|
79
|
+
await listener?.(data);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
this.onListenerError(name, err);
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 串行管线:监听器必须调 next(value) 委托——不调即短路(管线后段不再执行,
|
|
88
|
+
* 返回值为该监听器的产出);异常沿链传播(管线失败语义——守门行依赖)。
|
|
89
|
+
*/
|
|
90
|
+
async waterfall(name, value) {
|
|
91
|
+
this.assertRegistered(name);
|
|
92
|
+
// 存储侧泛型擦除为 unknown——取回 cast 还原为本次调用的 T(注册/派发两侧的
|
|
93
|
+
// 类型对齐由调用方契约保证,这里只做类型往返)
|
|
94
|
+
const chain = (this.listeners.get(name) ?? []).map((entry) => entry.waterfall).filter(Boolean);
|
|
95
|
+
// 自尾向头组合:每层 next 即下一段;尾 = 直通(value 原样出管线)
|
|
96
|
+
let next = (v) => Promise.resolve(v);
|
|
97
|
+
for (const listener of chain.reverse()) {
|
|
98
|
+
const downstream = next;
|
|
99
|
+
next = (v) => Promise.resolve(listener(v, downstream));
|
|
100
|
+
}
|
|
101
|
+
return next(value);
|
|
102
|
+
}
|
|
103
|
+
/** 挂管线监听器(waterfall 专用面——与 on 分开:形态不同防误用) */
|
|
104
|
+
onWaterfall(name, listener) {
|
|
105
|
+
this.assertRegistered(name);
|
|
106
|
+
const list = this.listeners.get(name) ?? [];
|
|
107
|
+
const entry = { waterfall: listener };
|
|
108
|
+
list.push(entry);
|
|
109
|
+
this.listeners.set(name, list);
|
|
110
|
+
return () => {
|
|
111
|
+
const current = this.listeners.get(name);
|
|
112
|
+
if (!current)
|
|
113
|
+
return;
|
|
114
|
+
const idx = current.indexOf(entry);
|
|
115
|
+
if (idx >= 0)
|
|
116
|
+
current.splice(idx, 1);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 全体并发,Promise.all 收口(异常传播——与 emit 的区别即异常语义)。
|
|
121
|
+
* 监听器面与 on 共用(通知型返回值参与收口)。
|
|
122
|
+
*/
|
|
123
|
+
async parallel(name, data) {
|
|
124
|
+
this.assertRegistered(name);
|
|
125
|
+
const legs = (this.listeners.get(name) ?? []).map((entry) => entry.notify);
|
|
126
|
+
const results = await Promise.all(legs.map((listener) => Promise.resolve(listener ? listener(data) : undefined)));
|
|
127
|
+
// 监听器返回面 unknown(通知型契约)——收口侧由调用方以 R 断言(并发计算型的
|
|
128
|
+
// 类型对齐是调用方契约,存储侧无从静态验证)
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 排队串行:逐监听器顺序执行(后段等前段完成);不短路——异常隔离上报后
|
|
133
|
+
* 继续下一段(与 waterfall 的区别 = 无委托链、异常不停段)。
|
|
134
|
+
* 同词多次派发按到达序排队(per-name 链式 promise)。
|
|
135
|
+
*/
|
|
136
|
+
serial(name, data) {
|
|
137
|
+
this.assertRegistered(name);
|
|
138
|
+
const legs = (this.listeners.get(name) ?? []).map((entry) => entry.notify);
|
|
139
|
+
const tail = this.serialQueues.get(name) ?? Promise.resolve();
|
|
140
|
+
const run = tail.then(async () => {
|
|
141
|
+
for (const listener of legs) {
|
|
142
|
+
try {
|
|
143
|
+
await listener?.(data);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
this.onListenerError(name, err);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// 队尾吞异常(隔离语义:一次派发的失败不堵后续派发的排队)
|
|
151
|
+
this.serialQueues.set(name, run.then(() => undefined, () => undefined));
|
|
152
|
+
return run;
|
|
153
|
+
}
|
|
154
|
+
/** 未注册词执法(emit/on/waterfall/parallel/serial 共用) */
|
|
155
|
+
assertRegistered(name) {
|
|
156
|
+
if (!this.registered.has(name)) {
|
|
157
|
+
throw new BaseError('EVENT_NOT_REGISTERED', `事件词 ${name} 未注册——fail-loud(词汇注册表单源,先 registerEventNames)`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context 模块公开面(L1 运行时基座——02 §2.2;一切件在其上)。
|
|
3
|
+
*
|
|
4
|
+
* 本批落码:作用域(effect LIFO 回卷/fork 级联/stale 护栏)+ 事件分派四模式
|
|
5
|
+
* + provide/get 服务注册表 + 自写 logger + canonical 工作区根。
|
|
6
|
+
* jiti 加载原语与目录信任判定随 host 装载批定形(消费面 = 插件装载器——
|
|
7
|
+
* 契约先于实现的纵切序),02 §2.2 职责清单不变。
|
|
8
|
+
*/
|
|
9
|
+
import './codes.js';
|
|
10
|
+
export { Scope, SCOPE_EFFECT_CAPACITY } from './scope.js';
|
|
11
|
+
export { EventDispatch } from './events.js';
|
|
12
|
+
export { LogLevelState, createLogger, childLogger } from './logger.js';
|
|
13
|
+
export { canonicalWorkspaceRoot, clearWorkspaceRootCache } from './workspace.js';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自写轻量 logger(07 §6 日志级别策略——约百行,不引入日志库)。
|
|
3
|
+
*
|
|
4
|
+
* 细则逐条:
|
|
5
|
+
* - 级别四档 error/warn/info/debug + silent 全静默;缺省 info(发布物即生产);
|
|
6
|
+
* - 结构化 JSON 行(time/level/module/msg + 上下文字段)写 stderr;
|
|
7
|
+
* - 模块前缀分级:BERRY_AGENT_LOG_LEVEL 逗号分隔 per-module 条目
|
|
8
|
+
* (`info,session:debug`);条目按 lastIndexOf(':') 切分(模块名含冒号可表达
|
|
9
|
+
* ——`core:memory` 里的冒号不是分隔符);前缀匹配(`session` 命中 `session`
|
|
10
|
+
* 与 `session:*` 全体);
|
|
11
|
+
* - 共享阈值盒:同盒树的 logger 持同一盒引用,setLevel 改盒值全树即时生效;
|
|
12
|
+
* child logger 不入登记表(防慢性泄漏);
|
|
13
|
+
* - 无效条目 stderr 警告后跳过(视同未设——静默失效比无反馈更糟);
|
|
14
|
+
* - 纪律红线(消费方义务):只在 debug 出现的分支,其行为必须同时是 durable
|
|
15
|
+
* 事件或运行时断言——进程日志职责收窄为「启动/崩溃/守门/恢复」诊断。
|
|
16
|
+
*/
|
|
17
|
+
/** 级别权重(数值越大越啰嗦——debug 3 最详;silent 独立档不入权重表) */
|
|
18
|
+
const LEVEL_WEIGHTS = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
19
|
+
/**
|
|
20
|
+
* 阈值盒(共享状态):全局级 + per-module 前缀覆盖表。
|
|
21
|
+
* createLogger 树全体持同一盒引用——setGlobalLevel/setModuleLevel 改盒即全树
|
|
22
|
+
* 即时生效;盒本身即解析态,logger 对象不入任何登记表。
|
|
23
|
+
*/
|
|
24
|
+
export class LogLevelState {
|
|
25
|
+
/** 全局阈值(缺省 info——发布物即生产) */
|
|
26
|
+
global = 'info';
|
|
27
|
+
/** per-module 前缀覆盖(前缀 → 级别;匹配取最长前缀) */
|
|
28
|
+
modules = new Map();
|
|
29
|
+
/** 从 env 串建盒(无效条目 stderr 警告后跳过——视同未设) */
|
|
30
|
+
static fromEnv(env, warn = defaultEnvWarn) {
|
|
31
|
+
const state = new LogLevelState();
|
|
32
|
+
if (!env)
|
|
33
|
+
return state;
|
|
34
|
+
for (const rawEntry of env.split(',')) {
|
|
35
|
+
const entry = rawEntry.trim();
|
|
36
|
+
if (!entry)
|
|
37
|
+
continue;
|
|
38
|
+
// lastIndexOf(':') 切分:模块名含冒号(core:memory)可表达——
|
|
39
|
+
// 最右一个冒号才是「模块:级别」分隔符
|
|
40
|
+
const sep = entry.lastIndexOf(':');
|
|
41
|
+
const levelText = sep >= 0 ? entry.slice(sep + 1) : entry;
|
|
42
|
+
const moduleText = sep >= 0 ? entry.slice(0, sep) : '';
|
|
43
|
+
const level = parseLevel(levelText);
|
|
44
|
+
if (!level) {
|
|
45
|
+
warn(`BERRY_AGENT_LOG_LEVEL 无效条目 "${entry}"(级别 "${levelText}" 不在闭集 error|warn|info|debug|silent)——已跳过`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (sep >= 0 && moduleText)
|
|
49
|
+
state.modules.set(moduleText, level);
|
|
50
|
+
else
|
|
51
|
+
state.setGlobalLevel(level);
|
|
52
|
+
}
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
/** 改全局阈值(全树即时生效——运行期提降级面) */
|
|
56
|
+
setGlobalLevel(level) {
|
|
57
|
+
this.global = level;
|
|
58
|
+
}
|
|
59
|
+
/** 设/覆盖某模块前缀阈值(前缀匹配:`session` 命中 `session` 与 `session:*` 全体) */
|
|
60
|
+
setModuleLevel(prefix, level) {
|
|
61
|
+
this.modules.set(prefix, level);
|
|
62
|
+
}
|
|
63
|
+
/** 解析模块生效级:最长匹配前缀条目 ?? 全局(silent 压倒一切——显式全静默) */
|
|
64
|
+
resolve(module) {
|
|
65
|
+
if (this.global === 'silent')
|
|
66
|
+
return 'silent';
|
|
67
|
+
let best;
|
|
68
|
+
for (const [prefix, level] of this.modules) {
|
|
69
|
+
// 前缀匹配:模块名等于前缀、或以前缀+':' 开头(域分段符)
|
|
70
|
+
const matches = module === prefix || module.startsWith(`${prefix}:`);
|
|
71
|
+
if (!matches)
|
|
72
|
+
continue;
|
|
73
|
+
if (!best || prefix.length > best.prefix.length)
|
|
74
|
+
best = { prefix, level };
|
|
75
|
+
}
|
|
76
|
+
const resolved = best?.level ?? this.global;
|
|
77
|
+
return resolved === 'silent' ? 'silent' : resolved;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** 单条级别字面量解析(大小写不敏感;无效返回 null) */
|
|
81
|
+
function parseLevel(text) {
|
|
82
|
+
const lower = text.toLowerCase();
|
|
83
|
+
if (lower === 'silent')
|
|
84
|
+
return 'silent';
|
|
85
|
+
if (lower in LEVEL_WEIGHTS)
|
|
86
|
+
return lower;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/** env 无效值警告的缺省面(bootstrapping 阶段 stderr 一行) */
|
|
90
|
+
function defaultEnvWarn(msg) {
|
|
91
|
+
process.stderr.write(`[logger] ${msg}\n`);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* logger → 内部三件(盒/sink/clock)弱关联:childLogger 派生的唯一缝。
|
|
95
|
+
* WeakMap 不阻回收(logger 亡即条目亡——「不入登记表防慢性泄漏」的兑现形态:
|
|
96
|
+
* 强引用登记表不存在,弱关联只为派生期取参)。
|
|
97
|
+
*/
|
|
98
|
+
const loggerInternals = new WeakMap();
|
|
99
|
+
/**
|
|
100
|
+
* 建模块 logger(child 面共享 state 盒——树内全树同盒即时生效)。
|
|
101
|
+
* @param module 模块名(域分段用 ':'——如 `session`、`core:memory`)
|
|
102
|
+
* @param state 共享阈值盒(缺省独立盒 info——测试/独立小面用;ctx 装配时传同一盒)
|
|
103
|
+
* @param sink 输出面(缺省 process.stderr;测试注入内存)
|
|
104
|
+
* @param clock 时间源(缺省 Date.now——测试可注入定值)
|
|
105
|
+
*/
|
|
106
|
+
export function createLogger(module, state = new LogLevelState(), sink = (line) => process.stderr.write(`${line}\n`), clock = Date.now) {
|
|
107
|
+
const write = (level, msg, fields) => {
|
|
108
|
+
const resolved = state.resolve(module);
|
|
109
|
+
if (resolved === 'silent')
|
|
110
|
+
return;
|
|
111
|
+
if (LEVEL_WEIGHTS[level] > LEVEL_WEIGHTS[resolved])
|
|
112
|
+
return;
|
|
113
|
+
// 结构化 JSON 行:time/level/module/msg + 上下文字段平铺
|
|
114
|
+
sink(JSON.stringify({ time: clock(), level, module, msg, ...fields }));
|
|
115
|
+
};
|
|
116
|
+
const logger = {
|
|
117
|
+
module,
|
|
118
|
+
debug: (msg, fields) => write('debug', msg, fields),
|
|
119
|
+
info: (msg, fields) => write('info', msg, fields),
|
|
120
|
+
warn: (msg, fields) => write('warn', msg, fields),
|
|
121
|
+
error: (msg, fields) => write('error', msg, fields),
|
|
122
|
+
};
|
|
123
|
+
loggerInternals.set(logger, { state, sink, clock });
|
|
124
|
+
return logger;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 派生 child logger(`session` → `session:sqlite` 式域细分):同盒同 sink 同
|
|
128
|
+
* clock——setLevel 改盒全树(含 child)即时生效。仅接受 createLogger 产物。
|
|
129
|
+
*/
|
|
130
|
+
export function childLogger(parent, subModule) {
|
|
131
|
+
const internals = loggerInternals.get(parent);
|
|
132
|
+
if (!internals) {
|
|
133
|
+
throw new Error('childLogger 仅接受 createLogger 产物(缺内部面——勿手工构造 Logger 对象)');
|
|
134
|
+
}
|
|
135
|
+
return createLogger(`${parent.module}:${subModule}`, internals.state, internals.sink, internals.clock);
|
|
136
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 作用域与服务注册表(04 §6「作用域与事件四模式」前半)。
|
|
3
|
+
*
|
|
4
|
+
* 三机制:
|
|
5
|
+
* - **effect LIFO 回卷**:ctx.effect(() => disposer) 登记可逆副作用,作用域
|
|
6
|
+
* teardown 按登记逆序回卷全部 disposer——装载了什么就必然能卸干净(痕迹
|
|
7
|
+
* 可清算)。生命周期绑位(进程根/会话/run 三级)由使用方创建,本模块只提供机制。
|
|
8
|
+
* - **fork 级联**:scope.fork() 派生子作用域——子先于父回卷、父回卷级联全部
|
|
9
|
+
* 未卷子。fork 不是拷贝:provide 面继承(子见父服务)、effect 面独立。
|
|
10
|
+
* - **stale 护栏**:作用域已回卷后完成的异步腿经 stale 判定收口——迟到的
|
|
11
|
+
* disposer 不再执行(回卷已过)、迟到的服务注册拒绝(SCOPE_STALE)。
|
|
12
|
+
* - **总注册帽**:effect 自有登记达 10^4 拒新登记(SCOPE_EFFECT_CAPACITY——
|
|
13
|
+
* 03 §3.4 可用性防线,真源即执法位点)。
|
|
14
|
+
*/
|
|
15
|
+
import { BaseError } from '../contracts/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* effect 总注册帽(03 §3.4:10^4——可用性防线,失控登记不拖垮回卷序)。
|
|
18
|
+
* 真源即执法位点:自有 disposers 长度即计数,无第二计数位(包装层形否决——
|
|
19
|
+
* 帽下帽双重记账〔03 §3.4 遗漏审计批钉位〕);fork 面独立同律(子作用域
|
|
20
|
+
* 各有自有帽,级联回卷总量不受限)。导出供测试与文档引用。
|
|
21
|
+
*/
|
|
22
|
+
export const SCOPE_EFFECT_CAPACITY = 10_000;
|
|
23
|
+
/**
|
|
24
|
+
* 作用域:服务注册面(provide/get)+ 可逆注册面(effect)+ fork 树。
|
|
25
|
+
* 生命周期三级(进程根/会话/run)= 三次 createScope/fork 的使用形态。
|
|
26
|
+
*/
|
|
27
|
+
export class Scope {
|
|
28
|
+
/** 父作用域(fork 派生关系;根作用域为 undefined) */
|
|
29
|
+
parent;
|
|
30
|
+
/** 自有服务注册(fork 面继承 = get 沿父链上溯;子重提供同名 = 遮蔽合法) */
|
|
31
|
+
services = new Map();
|
|
32
|
+
/**
|
|
33
|
+
* 服务主位账(同主刷新律——04 §6 定形注:/reload 撬名修复批)。键值与
|
|
34
|
+
* services 同集(每次成功 provide 同步落位;匿名提供 = undefined 位)。
|
|
35
|
+
* 刷新判据专用,不另设公共读面。
|
|
36
|
+
*/
|
|
37
|
+
owners = new Map();
|
|
38
|
+
/** 已登记 disposer(登记序)——回卷按逆序 LIFO */
|
|
39
|
+
disposers = [];
|
|
40
|
+
/** 未回卷的子作用域(父回卷时级联先卷) */
|
|
41
|
+
children = new Set();
|
|
42
|
+
/** 回卷旗标(stale 护栏判据;回卷后一切迟到登记拒) */
|
|
43
|
+
disposed = false;
|
|
44
|
+
/** @param parent 父作用域(缺省 = 根作用域);派生走 fork() 不直调本构造器 */
|
|
45
|
+
constructor(parent) {
|
|
46
|
+
this.parent = parent;
|
|
47
|
+
}
|
|
48
|
+
/** 创建根作用域(进程根/会话/run 三级的使用入口之一) */
|
|
49
|
+
static createRoot() {
|
|
50
|
+
return new Scope(undefined);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 派生子作用域:子先于父回卷(逆序天然)、父回卷级联全部未卷子;
|
|
54
|
+
* provide 面继承(get 上溯)、effect 面独立(子的副作用不污染父的回卷序)。
|
|
55
|
+
*/
|
|
56
|
+
fork() {
|
|
57
|
+
if (this.disposed)
|
|
58
|
+
throw new BaseError('SCOPE_STALE', '父作用域已回卷——不能再派生子作用域');
|
|
59
|
+
const child = new Scope(this);
|
|
60
|
+
this.children.add(child);
|
|
61
|
+
return child;
|
|
62
|
+
}
|
|
63
|
+
/** 作用域是否已回卷(异步腿竞速收口的 stale 判据) */
|
|
64
|
+
get isDisposed() {
|
|
65
|
+
return this.disposed;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 登记服务(装载期接线、运行期取用)。
|
|
69
|
+
* 撞名分档(同主刷新律——04 §6 定形注):同 owner 重供 = 原位刷新
|
|
70
|
+
* (upsert——依赖方下次取用即见新值;`/reload` 换代重跑同装配位的结构性
|
|
71
|
+
* 支撑);跨主撞名与匿名重供(任一方匿名即不可证明同主)照旧拒
|
|
72
|
+
* CONTEXT_SERVICE_DUPLICATE(两方抢一名仍是装配 bug)。
|
|
73
|
+
* owner 真源 = 装载器注入非自报(ctx.provide 恒 = 本插件 id——包装层注入;
|
|
74
|
+
* 宿主装配位 = 装配位标识)。迟到注册(已回卷)拒 SCOPE_STALE。
|
|
75
|
+
*/
|
|
76
|
+
provide(name, service, owner) {
|
|
77
|
+
if (this.disposed)
|
|
78
|
+
throw new BaseError('SCOPE_STALE', `迟到注册拒:作用域已回卷(${name})`);
|
|
79
|
+
if (this.services.has(name)) {
|
|
80
|
+
const prevOwner = this.owners.get(name);
|
|
81
|
+
// 匿名任一方即拒:undefined 位不可被刷新(无法证明同主),带主重供匿名
|
|
82
|
+
// 位同理——防匿名注册经刷新律洗白为带主位
|
|
83
|
+
if (owner === undefined || prevOwner === undefined || owner !== prevOwner) {
|
|
84
|
+
throw new BaseError('CONTEXT_SERVICE_DUPLICATE', `服务名 ${name} 在本作用域已被提供(fork 子作用域重提供 = 遮蔽合法,同层撞名 = 装配 bug;同主重供 = 原位刷新)`);
|
|
85
|
+
}
|
|
86
|
+
// 同主原位刷新——非新增(serviceNames 名册零重复)
|
|
87
|
+
this.services.set(name, service);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.services.set(name, service);
|
|
91
|
+
this.owners.set(name, owner);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 取服务(沿 fork 链上溯——provide 面继承)。
|
|
95
|
+
* 缺席 fail-loud(CONTEXT_SERVICE_MISSING,拼错名立即炸)。
|
|
96
|
+
* 判缺席用沿链 has 而非值判 undefined——已注册的 undefined 值服务不算缺席。
|
|
97
|
+
*/
|
|
98
|
+
get(name) {
|
|
99
|
+
let scope = this;
|
|
100
|
+
while (scope) {
|
|
101
|
+
if (scope.services.has(name))
|
|
102
|
+
return scope.services.get(name);
|
|
103
|
+
scope = scope.parent;
|
|
104
|
+
}
|
|
105
|
+
throw new BaseError('CONTEXT_SERVICE_MISSING', `服务 ${name} 缺席(拼错名/装载序缺口——可选消费用 tryGet)`);
|
|
106
|
+
}
|
|
107
|
+
/** 可选消费(「诚实缺席不 fail-loud」的唯一档——如浏览器引擎缺席) */
|
|
108
|
+
tryGet(name) {
|
|
109
|
+
// 沿父链上溯:自有无则问父——fork 继承语义
|
|
110
|
+
let scope = this;
|
|
111
|
+
while (scope) {
|
|
112
|
+
if (scope.services.has(name))
|
|
113
|
+
return scope.services.get(name);
|
|
114
|
+
scope = scope.parent;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 沿 fork 链枚举可见服务名(自身 + 全部祖先,就近在前)。
|
|
120
|
+
* 03 §3.1「服务目录运行时可枚举」的机制腿——ctx.get 缺席报错附现行名单,
|
|
121
|
+
* 点名错误当场响亮可诊。子遮蔽父同名时两现(就近位次即遮蔽序——诚实枚举)。
|
|
122
|
+
*/
|
|
123
|
+
serviceNames() {
|
|
124
|
+
const names = [];
|
|
125
|
+
let scope = this;
|
|
126
|
+
while (scope) {
|
|
127
|
+
names.push(...scope.services.keys());
|
|
128
|
+
scope = scope.parent;
|
|
129
|
+
}
|
|
130
|
+
return names;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 登记可逆副作用:register 立即执行、其返回的 disposer 进 LIFO 回卷序。
|
|
134
|
+
* 迟到登记(已回卷)拒 SCOPE_STALE——迟到的 disposer 不再执行(回卷已过,
|
|
135
|
+
* 补跑只会撕裂状态)。
|
|
136
|
+
*/
|
|
137
|
+
effect(register) {
|
|
138
|
+
if (this.disposed)
|
|
139
|
+
throw new BaseError('SCOPE_STALE', '迟到登记拒:作用域已回卷(disposer 补跑只会撕裂状态)');
|
|
140
|
+
// 总注册帽(03 §3.4 钉 10^4):拒在 register 回调执行前——超帽受理连副作用都不发生
|
|
141
|
+
if (this.disposers.length >= SCOPE_EFFECT_CAPACITY) {
|
|
142
|
+
throw new BaseError('SCOPE_EFFECT_CAPACITY', `effect 总注册帽(${SCOPE_EFFECT_CAPACITY})已满——本作用域登记数已达上限(失控登记防线,宿主/插件同帽一视)`);
|
|
143
|
+
}
|
|
144
|
+
const disposer = register();
|
|
145
|
+
// register 返回后仍可能已并发回卷——回卷序已定格的 disposer 直接执行收口,
|
|
146
|
+
// 不入序(防撕裂:单独跑一次再丢弃,副作用「必然被清算」承诺保持)
|
|
147
|
+
if (this.disposed) {
|
|
148
|
+
void Promise.resolve(disposer()).catch(() => {
|
|
149
|
+
// 迟到腿的清理失败静默(作用域已亡,无回收面——吞并是唯一不撕裂选择)
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.disposers.push(disposer);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 回卷作用域:子先级联回卷 → 自有 disposer 按 LIFO 逆序回卷 → 标记 disposed。
|
|
157
|
+
* 幂等(二次调用直接返回);disposer 异常不中断回卷(清算是尽力序——
|
|
158
|
+
* 单腿炸不掩护其余腿的清算义务)。
|
|
159
|
+
*/
|
|
160
|
+
async dispose() {
|
|
161
|
+
if (this.disposed)
|
|
162
|
+
return;
|
|
163
|
+
this.disposed = true;
|
|
164
|
+
// 级联:子先于父回卷(父回卷时把未卷的子全卷)
|
|
165
|
+
for (const child of [...this.children]) {
|
|
166
|
+
await child.dispose();
|
|
167
|
+
}
|
|
168
|
+
this.children.clear();
|
|
169
|
+
if (this.parent)
|
|
170
|
+
this.parent.children.delete(this);
|
|
171
|
+
// LIFO:后登记的先回卷(登记逆序)
|
|
172
|
+
for (const disposer of this.disposers.reverse()) {
|
|
173
|
+
try {
|
|
174
|
+
await disposer();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// 单腿回卷失败不中断其余腿清算(尽力序;失败腿无二次重试面)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.disposers.length = 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* canonical 工作区根原语(06 §「owner_key 与 canonical 工作区根」;02 §2.2
|
|
3
|
+
* context 职责——共享原语宿主收编:memory owner_key / 技能信任判定 / 未来
|
|
4
|
+
* project 域键三处同源,一处实现三处消费)。
|
|
5
|
+
*
|
|
6
|
+
* 解析律(06 §74 逐条):
|
|
7
|
+
* - 从 cwd 向上找最近 `.git`;
|
|
8
|
+
* - worktree / submodule(`.git` 为文件)解析 `gitdir → commondir` 归并到
|
|
9
|
+
* 主仓库根——同一仓库的主目录、worktree、任意子目录产生同一 project 键;
|
|
10
|
+
* - 非 git 目录回退字面 cwd;
|
|
11
|
+
* - submodule 的 modules gitdir 无 commondir,独立成域(submodule 本就是
|
|
12
|
+
* 独立仓库)——canonical 根取 gitdir 实身;
|
|
13
|
+
* - 探测结果按 cwd 进程内缓存(同路径不重复打 fs;仓库移动属极端场景,
|
|
14
|
+
* 重启自愈);
|
|
15
|
+
* - 不读 GIT_* 宿主环境变量(03 篇配置总线禁令同向)。
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
18
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
19
|
+
/** 探测缓存(cwd → canonical 根;进程内单例——同路径不重复打 fs) */
|
|
20
|
+
const cache = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* 解析 canonical 工作区根。
|
|
23
|
+
* @param cwd 起点目录(缺省 process.cwd);结果按 cwd 缓存
|
|
24
|
+
* @returns canonical 根绝对路径(realpath 后的主仓库根 / gitdir 实身 / 字面 cwd 回退)
|
|
25
|
+
*/
|
|
26
|
+
export function canonicalWorkspaceRoot(cwd = process.cwd()) {
|
|
27
|
+
const cached = cache.get(cwd);
|
|
28
|
+
if (cached !== undefined)
|
|
29
|
+
return cached;
|
|
30
|
+
const root = detect(cwd);
|
|
31
|
+
cache.set(cwd, root);
|
|
32
|
+
return root;
|
|
33
|
+
}
|
|
34
|
+
/** 清空探测缓存(测试用——生产面无清空路径) */
|
|
35
|
+
export function clearWorkspaceRootCache() {
|
|
36
|
+
cache.clear();
|
|
37
|
+
}
|
|
38
|
+
/** 实际探测(无缓存路径) */
|
|
39
|
+
function detect(cwd) {
|
|
40
|
+
// 逐级上溯找最近 .git(含 cwd 自身;existsSync 对断链符号链返回 false——
|
|
41
|
+
// statSync lstat 语义此处不需要,直接 existsSync 探测文件/目录均可)
|
|
42
|
+
let dir = resolve(cwd);
|
|
43
|
+
let gitPath;
|
|
44
|
+
for (;;) {
|
|
45
|
+
const candidate = join(dir, '.git');
|
|
46
|
+
if (pathExists(candidate)) {
|
|
47
|
+
gitPath = candidate;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const parent = dirname(dir);
|
|
51
|
+
if (parent === dir)
|
|
52
|
+
break; // 到文件系统根仍未命中
|
|
53
|
+
dir = parent;
|
|
54
|
+
}
|
|
55
|
+
if (!gitPath) {
|
|
56
|
+
// 非 git 目录:回退字面 cwd(resolve 归一,不做 realpath——字面语义)
|
|
57
|
+
return resolve(cwd);
|
|
58
|
+
}
|
|
59
|
+
const st = statSync(gitPath);
|
|
60
|
+
if (st.isDirectory()) {
|
|
61
|
+
// 普通仓库:.git 目录的父级即仓库根(realpath 消符号链)
|
|
62
|
+
return realpathSync(dirname(gitPath));
|
|
63
|
+
}
|
|
64
|
+
// .git 是文件:worktree / submodule——解析 gitdir: 行
|
|
65
|
+
const gitdir = parseGitdir(gitPath);
|
|
66
|
+
if (!gitdir) {
|
|
67
|
+
// 无法解析的 .git 文件(畸形):退字面 cwd(宁可退也不炸探测)
|
|
68
|
+
return resolve(cwd);
|
|
69
|
+
}
|
|
70
|
+
const commondirPath = join(gitdir, 'commondir');
|
|
71
|
+
if (pathExists(commondirPath)) {
|
|
72
|
+
// worktree:commondir 内容指向主 .git 目录(相对 commondirPath 所在目录或
|
|
73
|
+
// 绝对路径)——归并到主仓库根(主目录/worktree/子目录同一键的兑现点)
|
|
74
|
+
const content = readFileSync(commondirPath, 'utf8').trim();
|
|
75
|
+
const mainGitDir = isAbsolute(content) ? content : resolve(dirname(commondirPath), content);
|
|
76
|
+
return realpathSync(dirname(mainGitDir));
|
|
77
|
+
}
|
|
78
|
+
// submodule:modules gitdir 无 commondir——独立成域,gitdir 实身即根
|
|
79
|
+
return realpathSync(gitdir);
|
|
80
|
+
}
|
|
81
|
+
/** .git 文件解析 gitdir: 行(无该行/读失败返回 undefined) */
|
|
82
|
+
function parseGitdir(gitFile) {
|
|
83
|
+
try {
|
|
84
|
+
const content = readFileSync(gitFile, 'utf8');
|
|
85
|
+
for (const line of content.split('\n')) {
|
|
86
|
+
if (line.startsWith('gitdir:'))
|
|
87
|
+
return line.slice('gitdir:'.length).trim();
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** 存在性探测(文件或目录均可;异常视同不存在——探测不炸) */
|
|
96
|
+
function pathExists(p) {
|
|
97
|
+
try {
|
|
98
|
+
statSync(p);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentEvent 活体事件族契约(04 篇 §2 机制真源——10 型,内存直推、不落日志)。
|
|
3
|
+
*
|
|
4
|
+
* 归位注记(2026-09-08 U3 落码批):本族类型原住 agent 件(src/agent/events.ts),
|
|
5
|
+
* 为插件侧 UI 后端类型可达(03 §2.2 registerUiBackend 定形注记——虚拟主键
|
|
6
|
+
* `berry-agent` 面只达 contracts)随 U3-1 归位本件;类型闭包全 contracts 侧
|
|
7
|
+
* (AgentMessage/StopReason/AgentToolResult 既在本域),机制真源仍 04 §2、
|
|
8
|
+
* agent 件经 re-export 维持公开面与件内消费路径不变(批 11b ApprovalAsk
|
|
9
|
+
* 归位同款先例)。
|
|
10
|
+
*
|
|
11
|
+
* 顺序铁律:每族严格 `start → update* → end`;流式 delta 与工具进度经 update
|
|
12
|
+
* 携带。活体流单向消费、不可逆写 durable(05 篇 §7 分层不变式)——「活体呈现」
|
|
13
|
+
* 与「durable 真相」两条线永不合流。重试续入零新事件型:续入即新
|
|
14
|
+
* agent_start/message 流,用户看到重跑。
|
|
15
|
+
*/
|
|
16
|
+
import type { StopReason } from './llm.js';
|
|
17
|
+
import type { AgentMessage } from './messages.js';
|
|
18
|
+
import type { AgentToolResult } from './tools.js';
|
|
19
|
+
/** run 终态恰三值(04 篇 §2:终态是结算边界——Job 结算、审批对收口、预算记账截断都以它为锚) */
|
|
20
|
+
export type RunStatus = 'completed' | 'aborted' | 'failed';
|
|
21
|
+
/**
|
|
22
|
+
* 三通道词表(04 篇 §4):steer = busy 注入合批 / followUp = idle 起跑 /
|
|
23
|
+
* inject = dismantled 停摆后注入。发送方只声明 backgroundWake、三通道判定是
|
|
24
|
+
* 驱动侧单源(conversation)——本词表只承载判定结果(活体事件字段,用户能
|
|
25
|
+
* 看到消息进了哪条道)。
|
|
26
|
+
*/
|
|
27
|
+
export type DeliverChannel = 'steer' | 'followUp' | 'inject';
|
|
28
|
+
/**
|
|
29
|
+
* AgentEvent 10 型联合。message_start/message_end 携带 channel(可观测性:
|
|
30
|
+
* 消息经哪条通道入列——04 §4;channel 缺省 = 用户直发种子消息)。
|
|
31
|
+
*/
|
|
32
|
+
export type AgentEvent = {
|
|
33
|
+
type: 'agent_start';
|
|
34
|
+
} | {
|
|
35
|
+
type: 'agent_end';
|
|
36
|
+
status: RunStatus;
|
|
37
|
+
stopReason?: StopReason;
|
|
38
|
+
errorMessage?: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'turn_start';
|
|
41
|
+
turn: number;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'turn_end';
|
|
44
|
+
turn: number;
|
|
45
|
+
stopReason: StopReason;
|
|
46
|
+
} | {
|
|
47
|
+
type: 'message_start';
|
|
48
|
+
role: string;
|
|
49
|
+
channel?: DeliverChannel;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'message_update';
|
|
52
|
+
role: string;
|
|
53
|
+
partial: AgentMessage;
|
|
54
|
+
} | {
|
|
55
|
+
type: 'message_end';
|
|
56
|
+
message: AgentMessage;
|
|
57
|
+
channel?: DeliverChannel;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'tool_execution_start';
|
|
60
|
+
toolCallId: string;
|
|
61
|
+
name: string;
|
|
62
|
+
arguments: Record<string, unknown>;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'tool_execution_update';
|
|
65
|
+
toolCallId: string;
|
|
66
|
+
update: unknown;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'tool_execution_end';
|
|
69
|
+
toolCallId: string;
|
|
70
|
+
result: AgentToolResult;
|
|
71
|
+
};
|
|
72
|
+
/** 事件汇(消费面:TUI/SPA 活体呈现、金样录制器等;void/Promise 双形兼容) */
|
|
73
|
+
export type AgentEventSink = (event: AgentEvent) => void | Promise<void>;
|