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,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* llm — StreamFn 适配层(04 §3:pi-ai 流 → agent StreamFn 契约)。
|
|
3
|
+
*
|
|
4
|
+
* 「永不抛错」契约的实现侧:一切解析/装配失败在本层编码为流内 error 终止
|
|
5
|
+
* 事件 + stopReason='error' 的最终消息(错误是数据不是异常,loop 零 try/catch
|
|
6
|
+
* 的根基)。pi-ai 自身的 lazyStream 已把 auth/网络装配失败编码为流 error
|
|
7
|
+
* 事件,本层只补齐「模型解析失败」与「在飞帽拒绝」两个前置环节。
|
|
8
|
+
*
|
|
9
|
+
* 直通策略:messages 标准三角色零转换直通 pi-ai(同构形状,结构化赋值即可);
|
|
10
|
+
* 工具描述仅做类型收口(parameters 已是 JSON Schema 对象)。
|
|
11
|
+
*/
|
|
12
|
+
// 注意:AssistantMessageEventStream 类名被 pi-ai types 的 type-only 再输出遮蔽,
|
|
13
|
+
// 值只能经官方工厂函数取得(该工厂即为此用途提供——"for use in extensions")
|
|
14
|
+
import { createAssistantMessageEventStream } from '@earendil-works/pi-ai';
|
|
15
|
+
import { BaseError } from '../contracts/index.js';
|
|
16
|
+
/** 零用量(错误合成消息用) */
|
|
17
|
+
const NO_USAGE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0 };
|
|
18
|
+
/**
|
|
19
|
+
* 创建 agent StreamFn(模型层整体注入 loop 的那一个函数)。
|
|
20
|
+
* @param runtime llm 运行时(Models 宿主)
|
|
21
|
+
* @param defaults 请求参数默认值(重试/采样档位,闭包持有)
|
|
22
|
+
* @param tracker per-provider 在飞计数器(04 §3.6——host 装配根与 complete
|
|
23
|
+
* 单发路共享同一份传入,「两出口同源计数」名实相符;不传 = 不限)
|
|
24
|
+
* @param hookDispatch 钩子派发段只读面(03 §3.4 执法——02 §5.3 LLM_CALL_IN_HOOK
|
|
25
|
+
* 接线:钩子 handler 执行段内流式调用即耦合死锁位,前置查命中转错误流
|
|
26
|
+
* 携码;不传 = 该执法缺席〔lib/测试形〕。02 §4.1 llm 无 host 逆边——
|
|
27
|
+
* 窗态经装配注入,本域自持同形结构副本)
|
|
28
|
+
*/
|
|
29
|
+
export function createStreamFn(runtime, defaults = {}, tracker, hookDispatch) {
|
|
30
|
+
return (context, options, signal) => {
|
|
31
|
+
// 钩子派发段前置查(03 §3.4):钩子 handler 内 await 流式模型面 = 耦合
|
|
32
|
+
// 违法(fire-and-forget 尾链窗外合法——guard 深度随派发收口归零);先于
|
|
33
|
+
// 模型解析(调用方违例与模型配置无关,最先判)
|
|
34
|
+
if (hookDispatch?.inHookDispatch() === true) {
|
|
35
|
+
return errorStream('钩子执行段禁模型调用(03 §3.4——钩子 handler 内 await 流式即耦合;起异步任务不等结果合法)', 'LLM_CALL_IN_HOOK');
|
|
36
|
+
}
|
|
37
|
+
// 模型解析失败 → 编码为错误流(永不抛错;错误在此转数据)。
|
|
38
|
+
// 文案携带 [CODE] 前缀维持人读可辨(机器判定位是 errorCode 字段)
|
|
39
|
+
let model;
|
|
40
|
+
try {
|
|
41
|
+
model = runtime.resolveModel(options.model);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const baseError = error instanceof BaseError ? error : null;
|
|
45
|
+
const code = baseError?.code ? `[${baseError.code}] ` : '';
|
|
46
|
+
return errorStream(`模型解析失败:${code}${baseError?.message ?? String(error)}`, baseError?.code);
|
|
47
|
+
}
|
|
48
|
+
// 在飞帽(04 §3.6):达帽显式拒绝——错误流带 errorCode(transient 桶,
|
|
49
|
+
// 会话层 auto-retry 退避后槽已释放重试成功)。模型解析先行是顺序必然:
|
|
50
|
+
// provider 名来自解析产物。
|
|
51
|
+
if (tracker !== undefined) {
|
|
52
|
+
const slot = tracker.tryAcquire(model.provider);
|
|
53
|
+
if (slot === null) {
|
|
54
|
+
return errorStream(`在飞请求达帽(provider=${model.provider}):并发压力自解,会话层退避后重试`, 'LLM_INFLIGHT_LIMIT');
|
|
55
|
+
}
|
|
56
|
+
return withRelease(runtime.models.streamSimple(model, buildPiContext(context), buildPiOptions(defaults, options, signal)), slot);
|
|
57
|
+
}
|
|
58
|
+
// 事件流结构同构(12 型协议 + result()),超集兼容子集直通
|
|
59
|
+
return runtime.models.streamSimple(model, buildPiContext(context), buildPiOptions(defaults, options, signal));
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** 标准三角色零转换直通(超集兼容子集;引用同一数组,无拷贝) */
|
|
63
|
+
function buildPiContext(context) {
|
|
64
|
+
return {
|
|
65
|
+
systemPrompt: context.systemPrompt,
|
|
66
|
+
messages: context.messages,
|
|
67
|
+
tools: context.tools?.map(toPiTool),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** defaults 打底 + 具名覆盖 + signal 透传(reasoning 无 'off' 档——undefined 即关闭) */
|
|
71
|
+
function buildPiOptions(defaults, options, signal) {
|
|
72
|
+
return {
|
|
73
|
+
...defaults,
|
|
74
|
+
reasoning: options.thinkingLevel !== undefined && options.thinkingLevel !== 'off' ? options.thinkingLevel : undefined,
|
|
75
|
+
...(options.apiKey !== undefined ? { apiKey: options.apiKey } : {}),
|
|
76
|
+
...(signal !== undefined ? { signal } : {}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 流终结释放包装(释放三保险的流侧两路):消费面 for-await **自然耗尽**
|
|
81
|
+
* (next() 返回 done——正常结束不触发 return())或**中途退出**(break/throw
|
|
82
|
+
* 触发迭代 return())即释放;不迭代的消费形态走 result() 的 finally 兜底
|
|
83
|
+
* (见下)。任一路先到即减计数(slot.release 幂等,多路径只生效一次)。
|
|
84
|
+
* 转发不拦截:事件与终值原样透传。
|
|
85
|
+
*/
|
|
86
|
+
function withRelease(stream, slot) {
|
|
87
|
+
return {
|
|
88
|
+
[Symbol.asyncIterator]() {
|
|
89
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
90
|
+
return {
|
|
91
|
+
next: async (value) => {
|
|
92
|
+
const r = await iterator.next(value);
|
|
93
|
+
if (r.done)
|
|
94
|
+
slot.release(); // 自然耗尽路:流终结即释放(不依赖消费面调 result())
|
|
95
|
+
return r;
|
|
96
|
+
},
|
|
97
|
+
return: (value) => {
|
|
98
|
+
slot.release();
|
|
99
|
+
return iterator.return?.(value) ?? Promise.resolve({ value: undefined, done: true });
|
|
100
|
+
},
|
|
101
|
+
throw: (error) => {
|
|
102
|
+
slot.release();
|
|
103
|
+
return iterator.throw ? iterator.throw(error) : Promise.reject(error);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
result: async () => {
|
|
108
|
+
try {
|
|
109
|
+
return await stream.result();
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
slot.release(); // result() 兜底:流未被迭代(或已早 break)的终态取值形态
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** 工具描述收口:LlmTool → pi-ai Tool(parameters 已是 JSON Schema 对象) */
|
|
118
|
+
function toPiTool(tool) {
|
|
119
|
+
return {
|
|
120
|
+
name: tool.name,
|
|
121
|
+
description: tool.description,
|
|
122
|
+
// 类型层面 JSON Schema 对象 → TSchema(运行时同一对象,typebox schema 即
|
|
123
|
+
// JSON Schema;LlmTool.parameters 为宽收 object——兼容构建器产物与手写
|
|
124
|
+
// schema 两种来源)
|
|
125
|
+
parameters: tool.parameters,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 合成错误流:单 error 终止事件 + 终值消息(pi-ai 事件流原语自建,协议同构)。
|
|
130
|
+
* @param errorMessage 人读错误说明(携 [CODE] 前缀)
|
|
131
|
+
* @param errorCode 宿主合成码(classifyError 判定序的机器判定位)
|
|
132
|
+
*/
|
|
133
|
+
function errorStream(errorMessage, errorCode) {
|
|
134
|
+
const message = {
|
|
135
|
+
role: 'assistant',
|
|
136
|
+
content: [],
|
|
137
|
+
usage: NO_USAGE,
|
|
138
|
+
stopReason: 'error',
|
|
139
|
+
errorMessage: errorCode !== undefined ? `[${errorCode}] ${errorMessage}` : errorMessage,
|
|
140
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
};
|
|
143
|
+
const stream = createAssistantMessageEventStream();
|
|
144
|
+
stream.push({ type: 'error', reason: 'error', error: message });
|
|
145
|
+
stream.end(message);
|
|
146
|
+
return stream;
|
|
147
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lsp 桥域错误码注册(03 §10.2——LSP_ 前缀族首批,02 §5.3 明列三码、
|
|
3
|
+
* 2026-09-13 f-2 批增列第四码)。
|
|
4
|
+
*
|
|
5
|
+
* 码名清单与语义真源 = 03 篇 §10.2 生命周期条 LSP_ 三码 + config 坏形条
|
|
6
|
+
* LSP_CONFIG_INVALID。本文件由模块公开面 index.ts 引入(注册纪律:写入点
|
|
7
|
+
* 文件必须实际 import 本文件注册才发生——与 issue/scheduler/mcp 先例同款)。
|
|
8
|
+
*/
|
|
9
|
+
import { registerErrorCodes } from '../contracts/index.js';
|
|
10
|
+
registerErrorCodes([
|
|
11
|
+
{
|
|
12
|
+
code: 'LSP_CONNECT_FAILED',
|
|
13
|
+
module: 'lsp',
|
|
14
|
+
description: 'LSP 实例 spawn/initialize 握手失败或超时(实例标 failed + ui.notify warn、在途/后续调用工具结果 error、下用再试并计熔断一败)',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
code: 'LSP_FRAME_INVALID',
|
|
18
|
+
module: 'lsp',
|
|
19
|
+
description: 'Content-Length 帧坏形或帧资源双帽超限(攒头 16KiB / 攒正文 16MiB——声明值即缓冲吸收上界)——连接死归因 + 同步树杀,计熔断一败(坏帧 crash 与 close 事件经幂等闸不双计)',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
code: 'LSP_CIRCUIT_OPEN',
|
|
23
|
+
module: 'lsp',
|
|
24
|
+
description: '同 server 3 连败熔断开(实例级旗标、复位走 /reload、行内他服务器不受累;作用域回卷的协议化关停不计熔断计数)',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
code: 'LSP_CONFIG_INVALID',
|
|
28
|
+
module: 'lsp',
|
|
29
|
+
description: '行 config 坏形装载期归一响亮拒——servers 对象形/command 绝对路径/languages 非空/超时正数域逐一校验,坏形 = 行级装载失败 /reload 时刻可修(03 §10.2 config 坏形条,2026-09-13 f-2 批增列)',
|
|
30
|
+
},
|
|
31
|
+
]);
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 帧式 JSON-RPC 连接(03 §10.2 帧层承载——Content-Length 帧上的请求关联表)。
|
|
3
|
+
*
|
|
4
|
+
* 与 mcp 件 JsonRpcConnection 同律异帧(行帧 vs 长度帧)——DAG 零边故平行
|
|
5
|
+
* 实现,词面独立律(02 §4.1):
|
|
6
|
+
* - 请求关联表:id 递增登记、三源结清竞速先到即摘(响应/超时/failAll);
|
|
7
|
+
* - 服务器→客户端请求一律 -32601 拒答(v1 不承载 workspace/configuration
|
|
8
|
+
* 等反向能力面);
|
|
9
|
+
* - 服务器通知按 method 分发给订阅面(publishDiagnostics 唯一真消费);
|
|
10
|
+
* - 帧 fatal(双帽超限/坏头)→ onFatal 上报一次 + pending 总清(载体级
|
|
11
|
+
* 失败收场归实例侧:封读 + 同步树杀)。
|
|
12
|
+
*/
|
|
13
|
+
import { BaseError } from '../contracts/index.js';
|
|
14
|
+
import { FrameDecoder, encodeFrame } from './frame.js';
|
|
15
|
+
/**
|
|
16
|
+
* 单实例线协议连接。生命周期语义:dead 旗(载体死/fatal)后 request 即拒
|
|
17
|
+
* (快失败不悬挂);failAll 是 pending 的总结清面(crash/超限共用)。
|
|
18
|
+
*/
|
|
19
|
+
export class LspWire {
|
|
20
|
+
nextId = 1;
|
|
21
|
+
pending = new Map();
|
|
22
|
+
decoder = new FrameDecoder();
|
|
23
|
+
notificationHandlers = new Map();
|
|
24
|
+
options;
|
|
25
|
+
dead = false;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.options = options;
|
|
28
|
+
}
|
|
29
|
+
/** 发请求(关联表登记 + 可选超时执法——两钟接力:握手钟/请求钟各自传入) */
|
|
30
|
+
request(method, params, timeoutMs) {
|
|
31
|
+
if (this.dead) {
|
|
32
|
+
return Promise.reject(new BaseError('LSP_CONNECT_FAILED', '连接已死(载体级失败/crash)——不再发请求'));
|
|
33
|
+
}
|
|
34
|
+
const id = this.nextId++;
|
|
35
|
+
const frame = { jsonrpc: '2.0', id, method };
|
|
36
|
+
if (params !== undefined)
|
|
37
|
+
frame.params = params;
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
let timer;
|
|
40
|
+
if (timeoutMs !== undefined && timeoutMs > 0) {
|
|
41
|
+
timer = setTimeout(() => {
|
|
42
|
+
this.pending.delete(id);
|
|
43
|
+
reject(new Error(`LSP 请求超时(${method},预算 ${timeoutMs}ms)`));
|
|
44
|
+
}, timeoutMs);
|
|
45
|
+
}
|
|
46
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
47
|
+
this.options.send(encodeFrame(JSON.stringify(frame)));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** 发通知(无 id 不等回复——initialized/didOpen/didChange/didClose/exit) */
|
|
51
|
+
notify(method, params) {
|
|
52
|
+
if (this.dead)
|
|
53
|
+
return;
|
|
54
|
+
const frame = { jsonrpc: '2.0', method };
|
|
55
|
+
if (params !== undefined)
|
|
56
|
+
frame.params = params;
|
|
57
|
+
this.options.send(encodeFrame(JSON.stringify(frame)));
|
|
58
|
+
}
|
|
59
|
+
/** 订阅服务器通知(method 分发;返回退订闭包) */
|
|
60
|
+
onNotification(method, handler) {
|
|
61
|
+
const list = this.notificationHandlers.get(method) ?? [];
|
|
62
|
+
list.push(handler);
|
|
63
|
+
this.notificationHandlers.set(method, list);
|
|
64
|
+
return () => {
|
|
65
|
+
const current = this.notificationHandlers.get(method);
|
|
66
|
+
if (current === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const idx = current.indexOf(handler);
|
|
69
|
+
if (idx >= 0)
|
|
70
|
+
current.splice(idx, 1);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** stdout 字节喂入(帧劈 → 逐帧分派;帧 fatal 即载体级失败收口) */
|
|
74
|
+
feed(chunk) {
|
|
75
|
+
const { frames, fatal } = this.decoder.feed(chunk);
|
|
76
|
+
if (fatal) {
|
|
77
|
+
this.die(new BaseError('LSP_FRAME_INVALID', 'Content-Length 帧坏形或双帽超限(攒头 16KiB / 攒正文 16MiB)——载体级失败'), '帧双帽超限/坏头');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
for (const frame of frames)
|
|
81
|
+
this.dispatch(frame);
|
|
82
|
+
}
|
|
83
|
+
/** 载体死结清(crash/close——pending 全拒;幂等) */
|
|
84
|
+
failAll(error) {
|
|
85
|
+
this.die(error, undefined);
|
|
86
|
+
}
|
|
87
|
+
/** 内部死面:dead 封口 + pending 总清(fatal 时另报 onFatal 一次) */
|
|
88
|
+
die(error, fatalReason) {
|
|
89
|
+
if (this.dead)
|
|
90
|
+
return;
|
|
91
|
+
this.dead = true;
|
|
92
|
+
for (const entry of this.pending.values()) {
|
|
93
|
+
if (entry.timer !== undefined)
|
|
94
|
+
clearTimeout(entry.timer);
|
|
95
|
+
entry.reject(error);
|
|
96
|
+
}
|
|
97
|
+
this.pending.clear();
|
|
98
|
+
if (fatalReason !== undefined)
|
|
99
|
+
this.options.onFatal?.(fatalReason);
|
|
100
|
+
}
|
|
101
|
+
/** 单帧分派(响应结清 / 服务器方向请求拒答 / 通知分发) */
|
|
102
|
+
dispatch(frame) {
|
|
103
|
+
let msg;
|
|
104
|
+
try {
|
|
105
|
+
msg = JSON.parse(frame);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
this.options.warn?.(`LSP 帧 JSON 坏形跳过:${frame.slice(0, 120)}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (typeof msg !== 'object' || msg === null)
|
|
112
|
+
return;
|
|
113
|
+
const m = msg;
|
|
114
|
+
if (typeof m.method === 'string') {
|
|
115
|
+
if (m.id !== undefined) {
|
|
116
|
+
// 服务器→客户端请求:一律 -32601(v1 不承载反向能力面)
|
|
117
|
+
if (!this.dead) {
|
|
118
|
+
this.options.send(encodeFrame(JSON.stringify({
|
|
119
|
+
jsonrpc: '2.0',
|
|
120
|
+
id: m.id,
|
|
121
|
+
error: { code: -32601, message: `v1 拒答服务器请求 ${m.method}` },
|
|
122
|
+
})));
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// 通知:按 method 分发给订阅面(publishDiagnostics 唯一真消费)
|
|
127
|
+
for (const handler of [...(this.notificationHandlers.get(m.method) ?? [])]) {
|
|
128
|
+
handler({ method: m.method, params: m.params });
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (typeof m.id === 'number') {
|
|
133
|
+
const entry = this.pending.get(m.id);
|
|
134
|
+
if (entry === undefined)
|
|
135
|
+
return; // 迟到/未知 id——超时已结清
|
|
136
|
+
this.pending.delete(m.id);
|
|
137
|
+
if (entry.timer !== undefined)
|
|
138
|
+
clearTimeout(entry.timer);
|
|
139
|
+
if (m.error !== undefined) {
|
|
140
|
+
entry.reject(new Error(`LSP ${m.error.code}: ${m.error.message}`));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
entry.resolve(m.result);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-Length 帧编解码(03 §10.2 帧层——手写零依赖)。
|
|
3
|
+
*
|
|
4
|
+
* 帧形:`Content-Length: <n>\r\n[其余头行……]\r\n\r<n 字节正文>`(LSP stdio
|
|
5
|
+
* 载体标准形;正文 UTF-8 JSON)。
|
|
6
|
+
*
|
|
7
|
+
* 帧资源卫生双帽(03 §10.2 明律):
|
|
8
|
+
* - 攒头态 16KiB——无 \r\n\r\n 分隔符的持续流不无界攒积;
|
|
9
|
+
* - 攒正文态 16MiB——**Content-Length 声明值即缓冲吸收上界**:头解析时刻
|
|
10
|
+
* 即拒超帽声明(不攒了才发现——巨帧在头落地时就被拦在门外)。
|
|
11
|
+
* 任一超帽/坏头 = fatal 坏帧:同批已解帧全弃(半批不发),此后解码器恒死
|
|
12
|
+
* (不再攒、不再上报——一次语义);fatal 后残余不外发(pending 由连接层
|
|
13
|
+
* 结清)。与 mcp 件 LineDecoder 同律异形(行帧 vs 长度帧)——DAG 零边故
|
|
14
|
+
* 平行实现,词面独立律(02 §4.1)。
|
|
15
|
+
*/
|
|
16
|
+
import { LSP_BODY_LIMIT_BYTES, LSP_HEADER_LIMIT_BYTES } from './types.js';
|
|
17
|
+
/** 头分隔符(\r\n\r\n——四字节) */
|
|
18
|
+
const HEADER_SEPARATOR = '\r\n\r\n';
|
|
19
|
+
/** 帧编码(payload → Content-Length 帧 Buffer——写出面单源) */
|
|
20
|
+
export function encodeFrame(payload) {
|
|
21
|
+
const body = Buffer.from(payload, 'utf8');
|
|
22
|
+
return Buffer.concat([Buffer.from(`Content-Length: ${body.length}\r\n\r\n`, 'ascii'), body]);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 帧解码器(字节面——攒头/攒正文两态;跨 chunk 拼接)。
|
|
26
|
+
*
|
|
27
|
+
* 状态即 `pendingBodyLength`:undefined = 攒头态;数值 = 攒正文态(已闭
|
|
28
|
+
* 合头的声明正文长)。凑满一帧即出帧、余段自动进入下一帧解析循环。
|
|
29
|
+
*/
|
|
30
|
+
export class FrameDecoder {
|
|
31
|
+
buf = [];
|
|
32
|
+
size = 0;
|
|
33
|
+
fatal = false;
|
|
34
|
+
pendingBodyLength;
|
|
35
|
+
/**
|
|
36
|
+
* 喂入一段字节;返回完整帧正文(UTF-8 解码)。
|
|
37
|
+
* fatal = true 即载体级失败(本次起解码器死——后续 feed 恒空恒不报;
|
|
38
|
+
* 同批先出的正常帧一并弃——半批不发)。
|
|
39
|
+
*/
|
|
40
|
+
feed(chunk) {
|
|
41
|
+
if (this.fatal)
|
|
42
|
+
return { frames: [], fatal: false };
|
|
43
|
+
this.buf.push(chunk);
|
|
44
|
+
this.size += chunk.length;
|
|
45
|
+
const frames = [];
|
|
46
|
+
for (;;) {
|
|
47
|
+
if (this.pendingBodyLength === undefined) {
|
|
48
|
+
// ── 攒头态:找 \r\n\r\n ──
|
|
49
|
+
const merged = this.concat();
|
|
50
|
+
const sep = merged.indexOf(HEADER_SEPARATOR, 'ascii');
|
|
51
|
+
if (sep < 0) {
|
|
52
|
+
// 头未闭合——攒头帽执法(无分隔符持续流不无界攒积)
|
|
53
|
+
if (this.size > LSP_HEADER_LIMIT_BYTES)
|
|
54
|
+
return this.die();
|
|
55
|
+
this.buf = [Buffer.from(merged)]; // subarray 视图拷贝释放(帽内拷贝有界)
|
|
56
|
+
return { frames, fatal: false };
|
|
57
|
+
}
|
|
58
|
+
const declared = parseContentLength(merged.subarray(0, sep).toString('ascii'));
|
|
59
|
+
if (declared === undefined || declared > LSP_BODY_LIMIT_BYTES)
|
|
60
|
+
return this.die();
|
|
61
|
+
this.pendingBodyLength = declared;
|
|
62
|
+
const rest = merged.subarray(sep + HEADER_SEPARATOR.length);
|
|
63
|
+
this.buf = rest.length > 0 ? [Buffer.from(rest)] : [];
|
|
64
|
+
this.size = rest.length;
|
|
65
|
+
// 落入攒正文态——继续循环凑正文
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// ── 攒正文态:凑满即出帧 ──
|
|
69
|
+
if (this.size < this.pendingBodyLength)
|
|
70
|
+
return { frames, fatal: false }; // 还不够
|
|
71
|
+
const merged = this.concat();
|
|
72
|
+
frames.push(merged.subarray(0, this.pendingBodyLength).toString('utf8'));
|
|
73
|
+
const rest = merged.subarray(this.pendingBodyLength);
|
|
74
|
+
this.buf = rest.length > 0 ? [Buffer.from(rest)] : [];
|
|
75
|
+
this.size = rest.length;
|
|
76
|
+
this.pendingBodyLength = undefined;
|
|
77
|
+
if (this.size === 0)
|
|
78
|
+
return { frames, fatal: false }; // 恰好耗尽——零余段
|
|
79
|
+
// 余段可能含后续帧——回循环重新走攒头态
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** 缓冲合并(单段零拷贝直取;攒头帽内的合并有界) */
|
|
84
|
+
concat() {
|
|
85
|
+
return this.buf.length === 1 ? this.buf[0] : Buffer.concat(this.buf);
|
|
86
|
+
}
|
|
87
|
+
/** 死面(清槽 + 恒死封口;同批已解帧全弃) */
|
|
88
|
+
die() {
|
|
89
|
+
this.fatal = true;
|
|
90
|
+
this.buf = [];
|
|
91
|
+
this.size = 0;
|
|
92
|
+
this.pendingBodyLength = undefined;
|
|
93
|
+
return { frames: [], fatal: true };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** 头区解析 Content-Length(大小写不敏感;坏形/负数返 undefined) */
|
|
97
|
+
function parseContentLength(headerText) {
|
|
98
|
+
for (const line of headerText.split('\r\n')) {
|
|
99
|
+
const colon = line.indexOf(':');
|
|
100
|
+
if (colon < 0)
|
|
101
|
+
continue;
|
|
102
|
+
if (line.slice(0, colon).trim().toLowerCase() !== 'content-length')
|
|
103
|
+
continue;
|
|
104
|
+
const value = Number.parseInt(line.slice(colon + 1).trim(), 10);
|
|
105
|
+
if (!Number.isFinite(value) || value < 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core:lsp 公开面(02 §4.1 席位 core:lsp——Content-Length 帧手写 + 惰性
|
|
3
|
+
* per-(server, rootUri) 实例 + 诊断回流,03 §10.2 六条款全兑现)。
|
|
4
|
+
*
|
|
5
|
+
* DAG deps = {contracts, context};spawn/注册/作用域/事件/文件系统全经窄面
|
|
6
|
+
* 注入(LspSpawnFace 等——compat.test 真 SpawnPipeline 结构互证)。装载态
|
|
7
|
+
* 集成归批 12 装载面后的装配批。
|
|
8
|
+
*/
|
|
9
|
+
import './codes.js';
|
|
10
|
+
export * from './types.js';
|
|
11
|
+
export { FrameDecoder, encodeFrame } from './frame.js';
|
|
12
|
+
export { LspWire } from './connection.js';
|
|
13
|
+
export { connectLspInstance } from './instance.js';
|
|
14
|
+
export { buildLspToolDefs } from './tools.js';
|
|
15
|
+
export { createDiagnosticsInjector } from './inject.js';
|
|
16
|
+
export { createLspService } from './service.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TOOL_POST_EXECUTE_EVENT } from '../contracts/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* 组 tools_post_execute waterfall 监听器(onWaterfall 挂线)。失败恒
|
|
4
|
+
* contained:吞错 + warn + 原样透传。
|
|
5
|
+
*/
|
|
6
|
+
export function createDiagnosticsInjector(deps) {
|
|
7
|
+
return async (input, next) => {
|
|
8
|
+
const posted = await next(input); // 先透传——注入纯后置
|
|
9
|
+
try {
|
|
10
|
+
await maybeInject(posted.result, posted, deps);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
// contained 铁律:注入失败不改写原结果、不置 isError——只 warn
|
|
14
|
+
deps.warn(`诊断 post 注入失败(contained 吞错):${err instanceof Error ? err.message : String(err)}`);
|
|
15
|
+
}
|
|
16
|
+
return posted;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** 注入判定与编舞(await 收口——竞速钟帽内落定,管道返回前诊断段已追加) */
|
|
20
|
+
async function maybeInject(result, input, deps) {
|
|
21
|
+
const { tool, args } = input;
|
|
22
|
+
if (result.isError === true)
|
|
23
|
+
return; // 失败写不注入(成功 = 非 isError)
|
|
24
|
+
const written = [];
|
|
25
|
+
const deleted = [];
|
|
26
|
+
if (tool.name === 'write') {
|
|
27
|
+
if (typeof args.path === 'string')
|
|
28
|
+
written.push(args.path);
|
|
29
|
+
}
|
|
30
|
+
else if (tool.name === 'edit') {
|
|
31
|
+
// edit 结构化操作账:{operations: [{op: 'add'|'update'|'delete', path}]}
|
|
32
|
+
const details = result.details;
|
|
33
|
+
if (details !== null && typeof details === 'object' && Array.isArray(details.operations)) {
|
|
34
|
+
for (const op of details.operations) {
|
|
35
|
+
if (typeof op?.path !== 'string')
|
|
36
|
+
continue;
|
|
37
|
+
if (op.op === 'delete')
|
|
38
|
+
deleted.push(op.path);
|
|
39
|
+
else
|
|
40
|
+
written.push(op.path);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return; // 非 {write, edit} 工具——零注入零等待
|
|
46
|
+
}
|
|
47
|
+
if (deleted.length > 0)
|
|
48
|
+
deps.face.closeDocuments(deleted); // didClose 唯一触发点
|
|
49
|
+
if (written.length === 0)
|
|
50
|
+
return;
|
|
51
|
+
const segments = await deps.face.injectDiagnostics(written); // 竞速钟帽内(面内执法)
|
|
52
|
+
for (const segment of segments) {
|
|
53
|
+
if (segment !== '')
|
|
54
|
+
result.content.push({ type: 'text', text: segment });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** 注入器挂线事件名(单源再导出——装配根与测试共用) */
|
|
58
|
+
export { TOOL_POST_EXECUTE_EVENT };
|