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,279 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { stdin, stdout, stderr } from 'node:process';
|
|
3
|
+
import { createSdkBackend, decodeWireLine, encodeWireLine, isSdkRequest, splitWireLines } from '../channels/index.js';
|
|
4
|
+
import { assembleHostStack } from './assembly.js';
|
|
5
|
+
import { startSchedulerClock } from './core-plugins.js';
|
|
6
|
+
import { openWebuiFace } from './webui-bridge.js';
|
|
7
|
+
/**
|
|
8
|
+
* 会话事件 → durable 平铺投影(双轨律重放段载体——type/seq/time/data 四字段
|
|
9
|
+
* 原样平铺,词汇零二校验〔05 §3.5 第一腿〕)。
|
|
10
|
+
*/
|
|
11
|
+
function toDurableEntry(event) {
|
|
12
|
+
return { type: event.type, seq: event.seq, time: event.time, data: event.data };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 装配桥:ConversationStack → SdkWireDeps 桥接(05 §3.5 serve 外推三腿的
|
|
16
|
+
* 桥接位;导出面供 serve-entry 传输环消费与组装级测试直测)。
|
|
17
|
+
*
|
|
18
|
+
* 映射律(在册会话内存日志优先——write-behind 在飞事件的权威源;未开会话
|
|
19
|
+
* 走库面纯读不 attach):
|
|
20
|
+
* - submitPrompt:sessionId 缺席 manager.create 新建(workspaceRoot 锚 cwd)、
|
|
21
|
+
* 显式携带 manager.open 落驱动(幂等续接;missing/closed 已被线核先决门
|
|
22
|
+
* 拦截);routedChannel 按受理时刻 driver.running 推导(03 §10.6 注记);
|
|
23
|
+
* 归因 `channel:sdk` + messageId→dedupeKey 落账(05 §3.5 两词一字段两面)。
|
|
24
|
+
* - queryEntries:单页全窗(重放腿跟尽 nextCursor 义务由桥并页兑现——cursor
|
|
25
|
+
* 形参 v1 无第二页可续)。
|
|
26
|
+
* - highWaterOf:在册 = 内存日志长度;未开 = 行 lastSeq(= 末次落账事件数)。
|
|
27
|
+
*/
|
|
28
|
+
export function createServeBridge(stack, runtime, anchors) {
|
|
29
|
+
/** 会话日志权威读(在册走内存——含 write-behind 在飞;未开走库面纯读) */
|
|
30
|
+
const logOf = (sessionId) => {
|
|
31
|
+
const driver = stack.driverOf(sessionId);
|
|
32
|
+
if (driver !== undefined)
|
|
33
|
+
return driver.session.events();
|
|
34
|
+
return runtime.persistence.store.loadEvents(sessionId);
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
submitPrompt: (input) => {
|
|
38
|
+
let sessionId = input.sessionId;
|
|
39
|
+
if (sessionId === undefined) {
|
|
40
|
+
sessionId = stack.manager.create({ workspaceRoot: anchors.cwd }).sessionId;
|
|
41
|
+
}
|
|
42
|
+
// 显式会话可能仅库面在册——open 幂等落驱动(线核先决门已拦 missing/closed)
|
|
43
|
+
const driver = stack.driverOf(sessionId) ?? stack.manager.open(sessionId).driver;
|
|
44
|
+
// routedChannel 观察推导(03 §10.6 注记:受理时刻在飞 run → steer、否则
|
|
45
|
+
// followUp——驱动侧单源路由的受理时刻投影,非控制位)
|
|
46
|
+
const routedChannel = driver.running ? 'steer' : 'followUp';
|
|
47
|
+
// 起跑让位一拍(queueMicrotask):线核在 submitPrompt 返回后才同步挂
|
|
48
|
+
// 自动订阅——同步起跑会让 agent_start 先于订阅点入空(首帧丢)。让位
|
|
49
|
+
// 一拍保证订阅点先落、直播自 agent_start 起。回执经信封回流;回执面
|
|
50
|
+
// 错误经 submitText 内部 notify 收口(无未处理拒绝)
|
|
51
|
+
queueMicrotask(() => {
|
|
52
|
+
stack.submitText(sessionId, input.content, {
|
|
53
|
+
source: 'channel:sdk',
|
|
54
|
+
dedupeKey: input.messageId,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
return { sessionId, routedChannel };
|
|
58
|
+
},
|
|
59
|
+
lookupDedupeKey: (sessionId, messageId) => {
|
|
60
|
+
// durable 档查重(05 §3.5 第二腿):返回原内容串——admit 同键异内容
|
|
61
|
+
// 判定源;块形内容(非 serve 线来源理论不达)JSON 稳定化兜底
|
|
62
|
+
for (const event of logOf(sessionId)) {
|
|
63
|
+
if (event.type !== 'user/message')
|
|
64
|
+
continue;
|
|
65
|
+
const data = event.data;
|
|
66
|
+
if (data.dedupeKey !== messageId)
|
|
67
|
+
continue;
|
|
68
|
+
return typeof data.content === 'string' ? data.content : (JSON.stringify(data.content) ?? '');
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
},
|
|
72
|
+
interruptSession: (sessionId) => {
|
|
73
|
+
stack.interrupt(sessionId);
|
|
74
|
+
},
|
|
75
|
+
queryEntries: (sessionId, since) => {
|
|
76
|
+
const log = logOf(sessionId);
|
|
77
|
+
// (since, 高水位) 窗口(高水位 = 日志长度 = 下一 seq——线核同式)
|
|
78
|
+
return { entries: log.filter((e) => e.seq > since && e.seq < log.length).map(toDurableEntry) };
|
|
79
|
+
},
|
|
80
|
+
listSessions: () => stack.manager.list().map((row) => ({
|
|
81
|
+
id: row.id,
|
|
82
|
+
title: row.title ?? null, // 无标题不造占位串(03 §10.6 sessions 词面)
|
|
83
|
+
lastActivityAt: row.updatedAt,
|
|
84
|
+
})),
|
|
85
|
+
highWaterOf: (sessionId) => {
|
|
86
|
+
const driver = stack.driverOf(sessionId);
|
|
87
|
+
if (driver !== undefined)
|
|
88
|
+
return driver.session.events().length;
|
|
89
|
+
const row = runtime.persistence.store.getSessionRow(sessionId);
|
|
90
|
+
return row === undefined ? undefined : row.lastSeq;
|
|
91
|
+
},
|
|
92
|
+
sessionStateOf: (sessionId) => {
|
|
93
|
+
const driver = stack.driverOf(sessionId);
|
|
94
|
+
if (driver !== undefined)
|
|
95
|
+
return driver.dismantled ? 'closed' : 'open';
|
|
96
|
+
// 库面在册 = 可续(prompt/订阅经 manager.open 落驱动)
|
|
97
|
+
return runtime.persistence.store.getSessionRow(sessionId) === undefined ? 'missing' : 'open';
|
|
98
|
+
},
|
|
99
|
+
retryProbeOf: (sessionId) => stack.driverOf(sessionId)?.retryState ?? null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* serve 主入口。阻塞至调用方收线(stdin EOF)/ 信号(经 main 编舞 →
|
|
104
|
+
* runtime.shutdown → closer)/ 传输面坏死 / 过载断连;返回进程退出码。
|
|
105
|
+
*/
|
|
106
|
+
export async function runServeEntry(options) {
|
|
107
|
+
// —— daemon 防御拒(13e-3 起 daemon 编舞归 main 分派层 → serve-daemon 件:
|
|
108
|
+
// spawner spawn 自镜像 + HTTP 面常驻。本入口只承载 stdio 前台形态——直调
|
|
109
|
+
// 传 daemon:true = 绕过分派层的编程误用,诚实拒不静默降级。占标记前拒——
|
|
110
|
+
// 不留半开场)——
|
|
111
|
+
if (options.flags.daemon) {
|
|
112
|
+
stderr.write('--daemon 归 main 分派层(serve-daemon 编舞件)——serve-entry 只承载 stdio 前台形态\n');
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
// —— 装配公共段(批 19a-3 迁 assembly 件——与 TUI 同一合成代码路径:
|
|
116
|
+
// 运行时→logger→共享根→栈→**插件装载**〔core 注册表 + enabled.yaml 真跑〕;
|
|
117
|
+
// 单活跃机/开库/清单损坏失败档归一 {ok:false}——干净退出不写 crash.log)——
|
|
118
|
+
const assembly = await assembleHostStack({
|
|
119
|
+
runtime: {
|
|
120
|
+
...(options.dataDir !== undefined ? { dataDir: options.dataDir } : {}),
|
|
121
|
+
...(options.memory === true ? { memory: true } : {}),
|
|
122
|
+
},
|
|
123
|
+
noPlugins: false,
|
|
124
|
+
debug: options.flags.debug,
|
|
125
|
+
version: options.version ?? '0.0.0-unknown',
|
|
126
|
+
...(options.providers !== undefined ? { providers: options.providers } : {}),
|
|
127
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
128
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
129
|
+
...(options.sandboxMode !== undefined ? { sandboxMode: options.sandboxMode } : {}),
|
|
130
|
+
...(options.onRuntime !== undefined ? { onRuntime: options.onRuntime } : {}),
|
|
131
|
+
});
|
|
132
|
+
if (!assembly.ok) {
|
|
133
|
+
// 两档呈报:crashed = 意外异常(crash.log 已在装配件内写);干净退出档
|
|
134
|
+
// = 启动失败(单活跃机/开库/启用清单损坏——消息自带前缀)
|
|
135
|
+
stderr.write(`${assembly.crashed ? `serve 运行失败:${assembly.message}` : assembly.message}\n`);
|
|
136
|
+
return assembly.exitCode;
|
|
137
|
+
}
|
|
138
|
+
const { runtime, stack, logger, scope } = assembly;
|
|
139
|
+
let exitCode = 0;
|
|
140
|
+
try {
|
|
141
|
+
// —— scheduler 挂钟起钟(批 20c——长驻形编舞):stdio 前台长驻同 TUI 律
|
|
142
|
+
// (件在场即起 + 停钟挂 closer——engine 真身定时器非 unref 不挂会拖活
|
|
143
|
+
// 进程;件缺席 no-op)——
|
|
144
|
+
startSchedulerClock(scope, runtime);
|
|
145
|
+
// —— --port 统一 HTTP 面 TCP 人面(18a-3' 三入口咬合;03 §10.4 host
|
|
146
|
+
// 接线):stdio 线与 TCP 面并存双活——面承载 SPA + /api/* + /v1/* 三族
|
|
147
|
+
// (bridge 同走 createServeBridge 零第二套映射);披露两行走 stderr
|
|
148
|
+
// (serve 前台披露通道);closer 挂运行时退出序——EOF/信号/过载三路
|
|
149
|
+
// 收场同享面收口(LIFO 在 serve-backend 之前注册 = drain 晚于 stdio 线
|
|
150
|
+
// 收口——网络面先关)。批 19e 件在场执法:sdk 件缺席 = 面本体件禁用
|
|
151
|
+
// 语义族——warn 一行不开面(daemon 形拒启退 2 同族,前台形降级——
|
|
152
|
+
// stdio 传输本体不受累);webui 件缺席 = 面开而 /api/* 404(mountKit
|
|
153
|
+
// 缺席形——openWebuiFace 内分档披露)——
|
|
154
|
+
if (options.flags.port !== undefined) {
|
|
155
|
+
const sdkKit = scope.tryGet('sdk-http-face');
|
|
156
|
+
if (sdkKit === undefined) {
|
|
157
|
+
stderr.write('warn:core:sdk 件未装载——--port 人面不开(07 §5 daemon 拒启同族;stdio 传输不受累)\n');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const mountKit = scope.tryGet('webui-face-mount');
|
|
161
|
+
await openWebuiFace({
|
|
162
|
+
stack,
|
|
163
|
+
runtime,
|
|
164
|
+
port: options.flags.port,
|
|
165
|
+
...(mountKit !== undefined ? { mountKit } : {}),
|
|
166
|
+
// U5-2:插件道路由受理器经 core:sdk kit 透传(snapshot/attachFace)
|
|
167
|
+
...(sdkKit.pluginRoutes !== undefined ? { pluginRoutes: sdkKit.pluginRoutes } : {}),
|
|
168
|
+
...(options.onWebuiOpen !== undefined ? { onOpen: options.onWebuiOpen } : {}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const io = options.io ?? { input: stdin, output: stdout };
|
|
173
|
+
const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 5_000;
|
|
174
|
+
// —— 传输环状态(收场单次性由 settled 门保证)——
|
|
175
|
+
let settled = false;
|
|
176
|
+
let remainder = '';
|
|
177
|
+
let resolveExit;
|
|
178
|
+
const exited = new Promise((resolve) => {
|
|
179
|
+
resolveExit = resolve;
|
|
180
|
+
});
|
|
181
|
+
// 出站 sink:stdout 直写,false = 背压(帧入线核有界队列待 drain)
|
|
182
|
+
const sink = {
|
|
183
|
+
write: (frame) => {
|
|
184
|
+
try {
|
|
185
|
+
return io.output.write(encodeWireLine(frame));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// 同步写异常(ENOBUFS 族)按背压处理——'error' 事件位统一退避重试
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
const bridge = createServeBridge(stack, runtime, { cwd: options.cwd ?? process.cwd() });
|
|
194
|
+
const handle = createSdkBackend({ ...bridge, sink, onOverload: () => shutdown(1) }, // 过载直写后宿主关传输(错误档退 1)
|
|
195
|
+
{
|
|
196
|
+
heartbeatIntervalMs,
|
|
197
|
+
initialConnectionNoDelta: options.flags.noDelta, // 07 §5 --no-delta 宿主立场缺省
|
|
198
|
+
});
|
|
199
|
+
stack.channels.addBackend(handle.backend); // 信封回流自动馈送(conversation-stack onEvent → emit)
|
|
200
|
+
// 心跳:装配层定时驱动(线核零自驱时钟——13b 纪律);drain 同拍 belt
|
|
201
|
+
//('drain' 事件为主路,定时兜底防事件丢失挂队列)
|
|
202
|
+
const heartbeatTimer = setInterval(() => {
|
|
203
|
+
handle.core.heartbeatTick();
|
|
204
|
+
handle.core.drain();
|
|
205
|
+
}, heartbeatIntervalMs);
|
|
206
|
+
// 收场序:清定时 → dispose(在飞 ask cancel + core.close)→ runtime 六步
|
|
207
|
+
// 退出序(幂等——EOF 路径与信号路径同享,closer 保证信号路径同样执行)
|
|
208
|
+
const shutdown = (code) => {
|
|
209
|
+
if (settled)
|
|
210
|
+
return;
|
|
211
|
+
settled = true;
|
|
212
|
+
clearInterval(heartbeatTimer);
|
|
213
|
+
handle.dispose();
|
|
214
|
+
void runtime
|
|
215
|
+
.shutdown()
|
|
216
|
+
.catch(() => { }) // 六步退出序内部已 fail-loud 记账——收场不二次抛
|
|
217
|
+
.then(() => resolveExit(code));
|
|
218
|
+
};
|
|
219
|
+
runtime.registerCloser({ label: 'serve-backend', fn: () => shutdown(0) }); // 信号路径优雅档(settled 门幂等——过载/坏死路径自带 1 不被覆写)
|
|
220
|
+
// —— 入站环:分帧 → 解码 → 线核受理 ——
|
|
221
|
+
io.input.setEncoding?.('utf8');
|
|
222
|
+
io.input.on('data', (chunk) => {
|
|
223
|
+
if (settled)
|
|
224
|
+
return;
|
|
225
|
+
const split = splitWireLines(chunk, remainder);
|
|
226
|
+
remainder = split.remainder;
|
|
227
|
+
for (const line of split.lines) {
|
|
228
|
+
let decoded;
|
|
229
|
+
try {
|
|
230
|
+
decoded = decodeWireLine(line);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
// 坏行不杀连接(fail-loud 诊断在 stderr——协议鲁棒位)
|
|
234
|
+
logger.warn(`行解码失败跳过:${err instanceof Error ? err.message : String(err)}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!isSdkRequest(decoded)) {
|
|
238
|
+
logger.warn('收到帧形行(服务端只受理请求行)——跳过');
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
handle.core.handleRequest(decoded);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
// EOF = 调用方收线——优雅收场;管道断裂同语义(无对端可写即服务终了)
|
|
245
|
+
io.input.on('end', () => shutdown(0));
|
|
246
|
+
io.input.on('close', () => shutdown(0));
|
|
247
|
+
io.input.on('error', (err) => {
|
|
248
|
+
logger.warn(`stdin 异常:${err.message}`);
|
|
249
|
+
shutdown(1);
|
|
250
|
+
});
|
|
251
|
+
// —— 出站背压恢复与传输面健康 ——
|
|
252
|
+
io.output.on('drain', () => {
|
|
253
|
+
if (!settled)
|
|
254
|
+
handle.core.drain();
|
|
255
|
+
});
|
|
256
|
+
io.output.on('error', (err) => {
|
|
257
|
+
if (settled)
|
|
258
|
+
return;
|
|
259
|
+
// ENOBUFS/EAGAIN 族 = 管道瞬时拥塞(pi output-guard 三件套执法位——
|
|
260
|
+
// 宿主 serve 即执法宿主):退避重试 drain;其余 = 传输面坏死收场退 1
|
|
261
|
+
if (err.code === 'ENOBUFS' || err.code === 'EAGAIN') {
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
if (!settled)
|
|
264
|
+
handle.core.drain();
|
|
265
|
+
}, 50);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
logger.warn(`stdout 异常:${err.message}`);
|
|
269
|
+
shutdown(1);
|
|
270
|
+
});
|
|
271
|
+
exitCode = await exited;
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
runtime.writeCrashLog(err); // 崩溃取证先行(memory 形跳过——件内语义)
|
|
275
|
+
stderr.write(`serve 运行失败:${err instanceof Error ? err.message : String(err)}\n`);
|
|
276
|
+
exitCode = 1;
|
|
277
|
+
}
|
|
278
|
+
return exitCode;
|
|
279
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话锚 ALS 件(07 §4.3 消费腿条款档位 2——交互动词族批 ix-2 落码)。
|
|
3
|
+
*
|
|
4
|
+
* 命令执行窗自动锚的实现载体:AsyncLocalStorage 语境继承语义——命令派生
|
|
5
|
+
* 的 fire-and-forget 尾链(Job 完成回调、定时器尾链等)保留锚,后台尾链
|
|
6
|
+
* 问「原会话」零自觉正确;**两处语境遮蔽**(装载器 apply 包裹位 + 钩子派发
|
|
7
|
+
* 包裹位)经 withoutSessionAnchor 显式清空——装载期(含命令触发的装载流
|
|
8
|
+
* 嵌套,如 /reload)与钩子语境结构性无自动锚(规范条款的执法位)。
|
|
9
|
+
*
|
|
10
|
+
* 注入侧 = tui-entry dispatchCommand 闭包(命令派发时锚聚焦会话);消费
|
|
11
|
+
* 侧 = plugin-context ctx.ui 面(阻塞三件/setStatus/setWidget 的锚解析);
|
|
12
|
+
* 遮蔽侧 = loader invokeApply + plugin-context withCallbackWindow。三侧同
|
|
13
|
+
* 模块(host),零拓扑新边。
|
|
14
|
+
*/
|
|
15
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
16
|
+
/** 单例存储——store 形即锚本体(sessionId 只读) */
|
|
17
|
+
const anchorStorage = new AsyncLocalStorage();
|
|
18
|
+
/**
|
|
19
|
+
* 读 ambient 会话锚(无锚语境 = undefined——装载期/钩子期经遮蔽后恒此值)。
|
|
20
|
+
* ctx.ui 锚解析的缺省源:显式 opts.sessionId 优先、缺席回落本值。
|
|
21
|
+
*/
|
|
22
|
+
export function readSessionAnchor() {
|
|
23
|
+
return anchorStorage.getStore()?.sessionId;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 以会话锚执行 fn(命令派发包裹位——tui-entry dispatchCommand 闭包)。
|
|
27
|
+
* fn 同步返回值直通;async 语境跟随(fn 派生的整个异步链均见锚——
|
|
28
|
+
* 「锚随异步链继承」的机制本体)。
|
|
29
|
+
*/
|
|
30
|
+
export function runWithSessionAnchor(sessionId, fn) {
|
|
31
|
+
return anchorStorage.run({ sessionId }, fn);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 清空 ambient 锚执行 fn(两处语境遮蔽——07 §4.3 档位 2 规范条款执法位)。
|
|
35
|
+
* ALS.exit 语义:fn 执行段(含 async 续体)结构性无锚;fn 返回后外层
|
|
36
|
+
* 语境原样恢复(fire-and-forget 尾链自起时 ambient 锚自然回来——「锚判
|
|
37
|
+
* 随后自理」的边界即此)。
|
|
38
|
+
*/
|
|
39
|
+
export function withoutSessionAnchor(fn) {
|
|
40
|
+
return anchorStorage.exit(fn);
|
|
41
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/sessions-cmd — `sessions` 子命令族 CLI 入口(07 §5 会话管理命令族;
|
|
3
|
+
* CLI 对等律射界 02 §2.3 多会话面;批 20d)。
|
|
4
|
+
*
|
|
5
|
+
* 五子动词分账(开库形态两分):
|
|
6
|
+
* - **读腿(list/search)+ 维护动词(reindex)——零装配直开库**:不开运行
|
|
7
|
+
* 时、不占单活跃机标记、不装载插件(同 serve status 只读豁免族——短命
|
|
8
|
+
* 查询/维护动词不是第二宿主实例)。开库走 Persistence 直开 + 宿主迁移链
|
|
9
|
+
* 尾单源(HOST_MIGRATION_TAIL——开库门禁按链 head 校验 user_version,
|
|
10
|
+
* 短链开真库会被同库拒降级运行,链必须与运行时全同)。reindex 是派生物
|
|
11
|
+
* 重建(05 §9「派生物不修不补——重建即修复」),写面只 session_fts 表。
|
|
12
|
+
* - **fork——全装配**:与 run --fork / TUI fork 同机(assembly 公共段:
|
|
13
|
+
* 单活跃机占标记 + 插件装载——session_before_fork 钩子保真〔checkpoint
|
|
14
|
+
* waterfall gate 等在场〕;05 §5.0 边界快照机制单源在 SessionManager.fork)。
|
|
15
|
+
* - **resume <id>——进 TUI**:直托 runTuiEntry(resumeSessionId 载体——
|
|
16
|
+
* 「指定 id 续接」的 CLI 半边,与无参 TUI 的按 cwd 取最新互补〔07 §5〕);
|
|
17
|
+
* 非 TTY 卫兵同 TUI 主入口律(管道/CI 退 2 指引改 run --session)。
|
|
18
|
+
*
|
|
19
|
+
* 退出码:0 成功(含空清单/零命中——诚实空非失败)/ 1 执行失败(会话不
|
|
20
|
+
* 存在、fork 否决、装配失败)/ 2 环境态误用(resume 非 TTY——07 §5 三态)。
|
|
21
|
+
*/
|
|
22
|
+
import { stdin as processStdin, stdout as processStdout, stderr as processStderr } from 'node:process';
|
|
23
|
+
import { Persistence } from '../persist/index.js';
|
|
24
|
+
import { assembleHostStack } from './assembly.js';
|
|
25
|
+
import { HOST_MIGRATION_TAIL } from './runtime.js';
|
|
26
|
+
import { runTuiEntry } from './tui-entry.js';
|
|
27
|
+
/** sessions 子命令族主入口。返回进程退出码(0/1/2;用法错归解析层) */
|
|
28
|
+
export async function runSessionsEntry(sub, options) {
|
|
29
|
+
switch (sub.sub) {
|
|
30
|
+
case 'list':
|
|
31
|
+
return runList(options);
|
|
32
|
+
case 'search':
|
|
33
|
+
return runSearch(options, sub.query);
|
|
34
|
+
case 'reindex':
|
|
35
|
+
return runReindex(options);
|
|
36
|
+
case 'resume':
|
|
37
|
+
return runResume(options, sub.id);
|
|
38
|
+
case 'fork':
|
|
39
|
+
return runFork(options, sub.id);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/* ---------------- 读腿基建(零装配直开库) ---------------- */
|
|
43
|
+
/**
|
|
44
|
+
* 读腿/维护动词开库(宿主迁移链尾单源;库文件路径走三级梯子缺省——与一切
|
|
45
|
+
* 入口同库同律;warn 走错误面——毒丸/撕裂尾告警直达人面)。
|
|
46
|
+
*/
|
|
47
|
+
function openReadSide(options, err) {
|
|
48
|
+
return Persistence.open({
|
|
49
|
+
...(options.dbPath !== undefined ? { dbPath: options.dbPath } : {}),
|
|
50
|
+
...(options.dataDir !== undefined ? { dataDir: options.dataDir } : {}),
|
|
51
|
+
migrations: HOST_MIGRATION_TAIL,
|
|
52
|
+
warn: (message) => err(`warn:${message}`),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** ISO 时间形态(ms epoch → 确定性串——测试可断言) */
|
|
56
|
+
function isoOf(ms) {
|
|
57
|
+
return new Date(ms).toISOString();
|
|
58
|
+
}
|
|
59
|
+
/** 血缘形(origin + 父链箭头——统一式不逐 origin 造文案) */
|
|
60
|
+
function lineageOf(origin, parentId) {
|
|
61
|
+
return parentId === undefined ? origin : `${origin}←${parentId}`;
|
|
62
|
+
}
|
|
63
|
+
/* ---------------- list ---------------- */
|
|
64
|
+
/** list:会话清单(id/标题/时间/血缘;updated 倒序) */
|
|
65
|
+
async function runList(options) {
|
|
66
|
+
const out = options.writeOut ?? ((text) => processStdout.write(`${text}\n`));
|
|
67
|
+
const err = options.writeErr ?? ((text) => processStderr.write(`${text}\n`));
|
|
68
|
+
const persistence = openReadSide(options, err);
|
|
69
|
+
try {
|
|
70
|
+
const rows = persistence.store.listSessions({ limit: 100 });
|
|
71
|
+
if (rows.length === 0) {
|
|
72
|
+
out('无会话(数据目录尚无在册会话——首跑 run/TUI 即建)');
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
const lines = [`共 ${rows.length} 个会话(updated 倒序,帽 100):`];
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
lines.push(` ${row.id} ${row.title ?? '(无标题)'} 创建 ${isoOf(row.createdAt)} 更新 ${isoOf(row.updatedAt)} ${lineageOf(row.origin, row.parentId)}`);
|
|
78
|
+
}
|
|
79
|
+
out(lines.join('\n'));
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await persistence.close();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/* ---------------- search ---------------- */
|
|
87
|
+
/** 命中行 snippet 切窗(首现位 ±30 字符;空白折叠单行——body 为索引投影原文,消费侧自切〔05 §9〕) */
|
|
88
|
+
function snippetOf(body, query) {
|
|
89
|
+
const flat = body.replace(/\s+/g, ' ').trim();
|
|
90
|
+
const at = flat.toLowerCase().indexOf(query.toLowerCase());
|
|
91
|
+
if (at < 0)
|
|
92
|
+
return flat.length > 64 ? `${flat.slice(0, 64)}…` : flat; // trigram 归一后首现位缺席——退头窗
|
|
93
|
+
const start = Math.max(0, at - 30);
|
|
94
|
+
const end = Math.min(flat.length, at + query.length + 30);
|
|
95
|
+
return `${start > 0 ? '…' : ''}${flat.slice(start, end)}${end < flat.length ? '…' : ''}`;
|
|
96
|
+
}
|
|
97
|
+
/** search:跨会话 FTS 全文检索(bm25 序;输出 id/标题/命中行) */
|
|
98
|
+
async function runSearch(options, query) {
|
|
99
|
+
const out = options.writeOut ?? ((text) => processStdout.write(`${text}\n`));
|
|
100
|
+
const err = options.writeErr ?? ((text) => processStderr.write(`${text}\n`));
|
|
101
|
+
const persistence = openReadSide(options, err);
|
|
102
|
+
try {
|
|
103
|
+
const hits = persistence.store.searchFtsGlobal(query, 50);
|
|
104
|
+
if (hits.length === 0) {
|
|
105
|
+
out(`无命中(${query})`);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
// 标题面一次读齐(命中行带 session 归属——行面反查人读标题)
|
|
109
|
+
const titles = new Map();
|
|
110
|
+
for (const row of persistence.store.listSessions({ limit: 1000 })) {
|
|
111
|
+
titles.set(row.id, { title: row.title, origin: row.origin, parentId: row.parentId });
|
|
112
|
+
}
|
|
113
|
+
const lines = [`命中 ${hits.length} 处(bm25 序,帽 50):`];
|
|
114
|
+
for (const hit of hits) {
|
|
115
|
+
const meta = titles.get(hit.sessionId);
|
|
116
|
+
const head = `${hit.sessionId} ${meta?.title ?? '(无标题)'}`;
|
|
117
|
+
lines.push(` ${head} #${hit.seq} ${snippetOf(hit.body, query)}`);
|
|
118
|
+
}
|
|
119
|
+
out(lines.join('\n'));
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await persistence.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/* ---------------- reindex ---------------- */
|
|
127
|
+
/** reindex:FTS 全量重建(05 §9 托付的手动重建命令——诊断索引漂移后的一键复位;派生物不修不补) */
|
|
128
|
+
async function runReindex(options) {
|
|
129
|
+
const out = options.writeOut ?? ((text) => processStdout.write(`${text}\n`));
|
|
130
|
+
const err = options.writeErr ?? ((text) => processStderr.write(`${text}\n`));
|
|
131
|
+
const persistence = openReadSide(options, err);
|
|
132
|
+
try {
|
|
133
|
+
const result = persistence.store.rebuildFts();
|
|
134
|
+
out(`FTS 重建完成:${result.sessions} 会话 / ${result.events} 事件入索引(派生物重建即修复)`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
await persistence.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/* ---------------- resume(进 TUI) ---------------- */
|
|
142
|
+
/** resume:按 id 续接后进 TUI(runTuiEntry resumeSessionId 载体;非 TTY 卫兵同 TUI 主入口律) */
|
|
143
|
+
async function runResume(options, id) {
|
|
144
|
+
const err = options.writeErr ?? ((text) => processStderr.write(`${text}\n`));
|
|
145
|
+
const stdinIsTTY = options.stdinIsTTY ?? processStdin.isTTY === true;
|
|
146
|
+
const stdoutIsTTY = options.stdoutIsTTY ?? processStdout.isTTY === true;
|
|
147
|
+
if (!stdinIsTTY || !stdoutIsTTY) {
|
|
148
|
+
err('非交互环境(stdin/stdout 非 TTY)——sessions resume 进 TUI 不适用于管道/CI;单发续接改用 `berry-agent run --session <id> "<message>"`。');
|
|
149
|
+
return 2;
|
|
150
|
+
}
|
|
151
|
+
// 直托 TUI 主入口(装配序单源):resumeSessionId 在场即按 id 续接,缺席
|
|
152
|
+
// 语义面归 tui-entry;id 打错的干净退 1 由其内呈报
|
|
153
|
+
const flags = { noPlugins: false, debug: false };
|
|
154
|
+
return runTuiEntry({
|
|
155
|
+
flags,
|
|
156
|
+
version: options.version,
|
|
157
|
+
...(options.cwd !== undefined ? { cwd: options.cwd } : {}),
|
|
158
|
+
...(options.dataDir !== undefined ? { dataDir: options.dataDir } : {}),
|
|
159
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
160
|
+
...(options.providers !== undefined ? { providers: options.providers } : {}),
|
|
161
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
162
|
+
...(options.sandboxMode !== undefined ? { sandboxMode: options.sandboxMode } : {}),
|
|
163
|
+
...(options.io !== undefined ? { io: options.io } : {}),
|
|
164
|
+
...(options.onRuntime !== undefined ? { onRuntime: options.onRuntime } : {}),
|
|
165
|
+
resumeSessionId: id,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/* ---------------- fork(全装配) ---------------- */
|
|
169
|
+
/** fork:边界快照分叉(05 §5.0 机制单源在 SessionManager.fork;与 run --fork / TUI fork 同机——钩子保真 + 单活跃机占标记) */
|
|
170
|
+
async function runFork(options, id) {
|
|
171
|
+
const out = options.writeOut ?? ((text) => processStdout.write(`${text}\n`));
|
|
172
|
+
const err = options.writeErr ?? ((text) => processStderr.write(`${text}\n`));
|
|
173
|
+
const assembly = await assembleHostStack({
|
|
174
|
+
runtime: {
|
|
175
|
+
...(options.dataDir !== undefined ? { dataDir: options.dataDir } : {}),
|
|
176
|
+
},
|
|
177
|
+
noPlugins: false, // fork 与 TUI 同构装载面——session_before_fork 钩子(checkpoint gate 等)保真
|
|
178
|
+
debug: false,
|
|
179
|
+
version: options.version,
|
|
180
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
181
|
+
...(options.providers !== undefined ? { providers: options.providers } : {}),
|
|
182
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
183
|
+
...(options.sandboxMode !== undefined ? { sandboxMode: options.sandboxMode } : {}),
|
|
184
|
+
...(options.onRuntime !== undefined ? { onRuntime: options.onRuntime } : {}),
|
|
185
|
+
});
|
|
186
|
+
if (!assembly.ok) {
|
|
187
|
+
err(assembly.message); // 单活跃机/开库/启用清单损坏——干净退出档(不写 crash.log)
|
|
188
|
+
return assembly.exitCode;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
// 源存在性预检:manager.fork 的缺席 throw 是调用序 bug 面(PERSIST_DATA_
|
|
192
|
+
// CORRUPT)——CLI 人面把打错 id 呈报为干净失败档
|
|
193
|
+
const row = assembly.runtime.persistence.store.getSessionRow(id);
|
|
194
|
+
if (row === undefined) {
|
|
195
|
+
err(`会话不存在:${id}——用 sessions list 查在册 id`);
|
|
196
|
+
return 1;
|
|
197
|
+
}
|
|
198
|
+
let forked;
|
|
199
|
+
try {
|
|
200
|
+
forked = await assembly.stack.manager.fork(id);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
err(`fork 失败:${error instanceof Error ? error.message : String(error)}`);
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
if (forked.status === 'vetoed') {
|
|
207
|
+
// session_before_fork 钩子否决——非错误路径但 fork 无法进行(退 1 执行失败档)
|
|
208
|
+
err(`fork 被钩子否决:${forked.reason}`);
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
out(`已分叉:${forked.sessionId}`);
|
|
212
|
+
out(` 源会话 ${id}(种子 ${forked.lineage.seedLength} 事件)`);
|
|
213
|
+
out(` 续接:berry-agent sessions resume ${forked.sessionId}`);
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
await assembly.runtime.shutdown();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 'sessions' 服务面(03 §4.4/§4.5 + 06 §318 appendEvent 最小面——批 19 销账笔)。
|
|
3
|
+
*
|
|
4
|
+
* 宿主装配根 provide 的活引用面:appendEventFor(sessionId) 按会话解析**当下**
|
|
5
|
+
* 活体驱动(/new 热切换安全 = 调用时点解析非装配期冻结);无活体驱动 =
|
|
6
|
+
* undefined 降级(服务照常 provide——诚实缺席律:消费方〔core:memory 差分
|
|
7
|
+
* 落账腿〕捕获后自行降级,mirror 不锁步)。
|
|
8
|
+
*
|
|
9
|
+
* 二道闸(06 §318 定稿条款——闭环在闭包内非依赖 SessionLog 下游):
|
|
10
|
+
* ①核心事件词伪造拒写——核心词写入权属宿主(核心事件族 = 驱动单源);
|
|
11
|
+
* ②未注册词汇拒写——与 SessionLog.append 下游 SESSION_UNKNOWN_EVENT_TYPE
|
|
12
|
+
* 同判据(前置在此 = 错误信息可携带服务面上下文;下游仍兜底)。
|
|
13
|
+
*
|
|
14
|
+
* 完整 ctx.sessions 面(只读四件 + 受理制写两腿 + 三闸)归后续批——本件只落
|
|
15
|
+
* appendEvent 最小面(memory/diff 唯一 durable 出口的承载位)+ surfaceOp
|
|
16
|
+
* 信封参数腿(03 §4.5 修缝批 2026-09-08:受控注入原语可选信封位——改道
|
|
17
|
+
* appendWithSurfaceOp 正门,插件自定义压缩类操作的唯一合法投影写径)。
|
|
18
|
+
*/
|
|
19
|
+
import { BaseError, CORE_EVENT_TYPE_NAMES, isKnownEventType } from '../contracts/index.js';
|
|
20
|
+
/** 建 sessions 服务面(装配根:`scope.provide('sessions', createSessionsFace({ driverOf }))`) */
|
|
21
|
+
export function createSessionsFace(options) {
|
|
22
|
+
return {
|
|
23
|
+
appendEventFor(sessionId) {
|
|
24
|
+
const log = options.driverOf(sessionId)?.session;
|
|
25
|
+
if (log === undefined)
|
|
26
|
+
return undefined; // 无活体驱动——诚实缺席(不造回库替身)
|
|
27
|
+
return (type, data, surfaceOp, sourceEventSeqs) => {
|
|
28
|
+
// 闸一:核心事件词伪造拒写(核心词写入权属宿主——双入口纪律的受理侧)
|
|
29
|
+
if (CORE_EVENT_TYPE_NAMES.includes(type)) {
|
|
30
|
+
throw new BaseError('SESSION_CORE_TYPE_FORBIDDEN', `核心事件词 ${type} 拒经 sessions.appendEventFor 写入(核心事件族写入权属宿主驱动单源——06 §6 二道闸①)`);
|
|
31
|
+
}
|
|
32
|
+
// 闸二:未注册词汇拒写(词汇注册表单源;判据与 SessionLog.append 下游同源)
|
|
33
|
+
if (!isKnownEventType(type)) {
|
|
34
|
+
throw new BaseError('SESSION_UNKNOWN_EVENT_TYPE', `事件词 ${type} 未在词汇注册表(sessions.appendEventFor 前置闸——下游 SessionLog.append 同判据兜底)`);
|
|
35
|
+
}
|
|
36
|
+
// surfaceOp 信封参数腿(03 §4.5):携带信封即改道 appendWithSurfaceOp
|
|
37
|
+
// 正门——一切改投影历史的唯一正门(05 §2.1)。边缘纪律五条与
|
|
38
|
+
// SESSION_SURFACE_OP_INVALID 由正门单点执法(受理面零第二校验面);
|
|
39
|
+
// 二道闸对两路同前置(核心词携信封同拦——词面收窄注记的执法位)。
|
|
40
|
+
if (surfaceOp !== undefined) {
|
|
41
|
+
return log.appendWithSurfaceOp(type, data, surfaceOp, sourceEventSeqs);
|
|
42
|
+
}
|
|
43
|
+
return log.append(type, data);
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|