@armadra/agent 0.0.0-stage → 0.2.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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +426 -2
- package/dist/agent/agent.d.ts +56 -0
- package/dist/agent/agent.js +129 -0
- package/dist/agent/loop.d.ts +51 -0
- package/dist/agent/loop.js +158 -0
- package/dist/agent/queue.d.ts +25 -0
- package/dist/agent/queue.js +55 -0
- package/dist/agent/retry.d.ts +22 -0
- package/dist/agent/retry.js +92 -0
- package/dist/agent/schema.d.ts +27 -0
- package/dist/agent/schema.js +186 -0
- package/dist/agent/session-cache.d.ts +100 -0
- package/dist/agent/session-cache.js +511 -0
- package/dist/agent/session-compaction.d.ts +49 -0
- package/dist/agent/session-compaction.js +251 -0
- package/dist/agent/session-core.d.ts +103 -0
- package/dist/agent/session-core.js +7 -0
- package/dist/agent/session-run.d.ts +55 -0
- package/dist/agent/session-run.js +246 -0
- package/dist/agent/session-state.d.ts +39 -0
- package/dist/agent/session-state.js +118 -0
- package/dist/agent/session-subagent.d.ts +54 -0
- package/dist/agent/session-subagent.js +157 -0
- package/dist/agent/session-sync.d.ts +20 -0
- package/dist/agent/session-sync.js +104 -0
- package/dist/agent/session-tools.d.ts +24 -0
- package/dist/agent/session-tools.js +277 -0
- package/dist/agent/session.d.ts +117 -0
- package/dist/agent/session.js +484 -0
- package/dist/agent/system-prompt.d.ts +49 -0
- package/dist/agent/system-prompt.js +121 -0
- package/dist/agent/tool-runner.d.ts +64 -0
- package/dist/agent/tool-runner.js +309 -0
- package/dist/agent/transform.d.ts +26 -0
- package/dist/agent/transform.js +171 -0
- package/dist/agent/types.d.ts +378 -0
- package/dist/agent/types.js +21 -0
- package/dist/ai/apis/anthropic-messages.d.ts +26 -0
- package/dist/ai/apis/anthropic-messages.js +214 -0
- package/dist/ai/apis/anthropic-request.d.ts +42 -0
- package/dist/ai/apis/anthropic-request.js +282 -0
- package/dist/ai/apis/api.d.ts +28 -0
- package/dist/ai/apis/api.js +102 -0
- package/dist/ai/apis/cache-params.d.ts +48 -0
- package/dist/ai/apis/cache-params.js +163 -0
- package/dist/ai/apis/google-generative-ai.d.ts +32 -0
- package/dist/ai/apis/google-generative-ai.js +249 -0
- package/dist/ai/apis/google-request.d.ts +40 -0
- package/dist/ai/apis/google-request.js +255 -0
- package/dist/ai/apis/openai-compat.d.ts +35 -0
- package/dist/ai/apis/openai-compat.js +142 -0
- package/dist/ai/apis/openai-completions.d.ts +28 -0
- package/dist/ai/apis/openai-completions.js +246 -0
- package/dist/ai/apis/openai-request.d.ts +35 -0
- package/dist/ai/apis/openai-request.js +303 -0
- package/dist/ai/apis/openai-responses-request.d.ts +52 -0
- package/dist/ai/apis/openai-responses-request.js +266 -0
- package/dist/ai/apis/openai-responses.d.ts +28 -0
- package/dist/ai/apis/openai-responses.js +303 -0
- package/dist/ai/apis/shared.d.ts +56 -0
- package/dist/ai/apis/shared.js +179 -0
- package/dist/ai/cache/economics.d.ts +24 -0
- package/dist/ai/cache/economics.js +69 -0
- package/dist/ai/cache/fingerprint.d.ts +17 -0
- package/dist/ai/cache/fingerprint.js +39 -0
- package/dist/ai/cache/miss.d.ts +49 -0
- package/dist/ai/cache/miss.js +96 -0
- package/dist/ai/cache/reporting.d.ts +39 -0
- package/dist/ai/cache/reporting.js +102 -0
- package/dist/ai/cache/types.d.ts +94 -0
- package/dist/ai/cache/types.js +19 -0
- package/dist/ai/cache/warmer.d.ts +94 -0
- package/dist/ai/cache/warmer.js +224 -0
- package/dist/ai/context.d.ts +35 -0
- package/dist/ai/context.js +114 -0
- package/dist/ai/cost.d.ts +21 -0
- package/dist/ai/cost.js +60 -0
- package/dist/ai/event-stream.d.ts +33 -0
- package/dist/ai/event-stream.js +101 -0
- package/dist/ai/fake/fake-provider.d.ts +68 -0
- package/dist/ai/fake/fake-provider.js +278 -0
- package/dist/ai/fake/fake-script.d.ts +69 -0
- package/dist/ai/fake/fake-script.js +142 -0
- package/dist/ai/http.d.ts +54 -0
- package/dist/ai/http.js +212 -0
- package/dist/ai/json-partial.d.ts +17 -0
- package/dist/ai/json-partial.js +243 -0
- package/dist/ai/overflow.d.ts +22 -0
- package/dist/ai/overflow.js +61 -0
- package/dist/ai/providers/auth.d.ts +71 -0
- package/dist/ai/providers/auth.js +202 -0
- package/dist/ai/providers/builtin.d.ts +23 -0
- package/dist/ai/providers/builtin.js +148 -0
- package/dist/ai/providers/catalog-data.d.ts +5 -0
- package/dist/ai/providers/catalog-data.js +19 -0
- package/dist/ai/providers/catalog.d.ts +28 -0
- package/dist/ai/providers/catalog.js +166 -0
- package/dist/ai/providers/registry.d.ts +83 -0
- package/dist/ai/providers/registry.js +305 -0
- package/dist/ai/sse.d.ts +32 -0
- package/dist/ai/sse.js +140 -0
- package/dist/ai/thinking.d.ts +42 -0
- package/dist/ai/thinking.js +101 -0
- package/dist/ai/types.d.ts +428 -0
- package/dist/ai/types.js +19 -0
- package/dist/bundle/ama-sandbox.cjs +324 -0
- package/dist/bundle/ama.cjs +25770 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.js +15 -0
- package/dist/cli/args.d.ts +82 -0
- package/dist/cli/args.js +381 -0
- package/dist/cli/bootstrap.d.ts +18 -0
- package/dist/cli/bootstrap.js +398 -0
- package/dist/cli/compose-providers.d.ts +37 -0
- package/dist/cli/compose-providers.js +104 -0
- package/dist/cli/compose-session.d.ts +69 -0
- package/dist/cli/compose-session.js +370 -0
- package/dist/cli/compose-store.d.ts +24 -0
- package/dist/cli/compose-store.js +125 -0
- package/dist/cli/compose.d.ts +106 -0
- package/dist/cli/compose.js +206 -0
- package/dist/cli/default-model.d.ts +16 -0
- package/dist/cli/default-model.js +24 -0
- package/dist/cli/deps.d.ts +205 -0
- package/dist/cli/deps.js +12 -0
- package/dist/cli/exit-codes.d.ts +29 -0
- package/dist/cli/exit-codes.js +40 -0
- package/dist/cli/main.d.ts +26 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/runtime.d.ts +90 -0
- package/dist/cli/runtime.js +8 -0
- package/dist/cli/startup-screen.d.ts +15 -0
- package/dist/cli/startup-screen.js +56 -0
- package/dist/cli/startup-steps.d.ts +35 -0
- package/dist/cli/startup-steps.js +197 -0
- package/dist/cli/subcommands/auth.d.ts +12 -0
- package/dist/cli/subcommands/auth.js +91 -0
- package/dist/cli/subcommands/config.d.ts +44 -0
- package/dist/cli/subcommands/config.js +201 -0
- package/dist/cli/subcommands/context.d.ts +21 -0
- package/dist/cli/subcommands/context.js +41 -0
- package/dist/cli/subcommands/doctor.d.ts +11 -0
- package/dist/cli/subcommands/doctor.js +246 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
- package/dist/cli/subcommands/models-cache-probe.js +190 -0
- package/dist/cli/subcommands/models-discover.d.ts +40 -0
- package/dist/cli/subcommands/models-discover.js +204 -0
- package/dist/cli/subcommands/models.d.ts +40 -0
- package/dist/cli/subcommands/models.js +144 -0
- package/dist/cli/subcommands/sessions.d.ts +11 -0
- package/dist/cli/subcommands/sessions.js +100 -0
- package/dist/codemode/capability.d.ts +37 -0
- package/dist/codemode/capability.js +55 -0
- package/dist/codemode/declarations.d.ts +44 -0
- package/dist/codemode/declarations.js +111 -0
- package/dist/codemode/host-side.d.ts +59 -0
- package/dist/codemode/host-side.js +253 -0
- package/dist/codemode/modes.d.ts +23 -0
- package/dist/codemode/modes.js +40 -0
- package/dist/codemode/protocol.d.ts +91 -0
- package/dist/codemode/protocol.js +114 -0
- package/dist/codemode/sandbox-entry.d.ts +25 -0
- package/dist/codemode/sandbox-entry.js +323 -0
- package/dist/codemode/store.d.ts +26 -0
- package/dist/codemode/store.js +53 -0
- package/dist/codemode/tool.d.ts +105 -0
- package/dist/codemode/tool.js +283 -0
- package/dist/compaction/branch-summary.d.ts +30 -0
- package/dist/compaction/branch-summary.js +79 -0
- package/dist/compaction/breaker.d.ts +32 -0
- package/dist/compaction/breaker.js +65 -0
- package/dist/compaction/cut-point.d.ts +23 -0
- package/dist/compaction/cut-point.js +70 -0
- package/dist/compaction/estimate.d.ts +30 -0
- package/dist/compaction/estimate.js +114 -0
- package/dist/compaction/prune-tier.d.ts +31 -0
- package/dist/compaction/prune-tier.js +83 -0
- package/dist/compaction/serialize.d.ts +20 -0
- package/dist/compaction/serialize.js +115 -0
- package/dist/compaction/summarize-tier.d.ts +111 -0
- package/dist/compaction/summarize-tier.js +305 -0
- package/dist/config/auth-file.d.ts +39 -0
- package/dist/config/auth-file.js +92 -0
- package/dist/config/context-files.d.ts +30 -0
- package/dist/config/context-files.js +77 -0
- package/dist/config/load.d.ts +45 -0
- package/dist/config/load.js +97 -0
- package/dist/config/merge.d.ts +73 -0
- package/dist/config/merge.js +248 -0
- package/dist/config/paths.d.ts +38 -0
- package/dist/config/paths.js +108 -0
- package/dist/config/profile.d.ts +27 -0
- package/dist/config/profile.js +65 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.js +413 -0
- package/dist/config/trust.d.ts +42 -0
- package/dist/config/trust.js +122 -0
- package/dist/config/types.d.ts +172 -0
- package/dist/config/types.js +27 -0
- package/dist/config/write.d.ts +10 -0
- package/dist/config/write.js +25 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +30 -0
- package/dist/hooks/config.d.ts +49 -0
- package/dist/hooks/config.js +102 -0
- package/dist/hooks/dispatcher.d.ts +41 -0
- package/dist/hooks/dispatcher.js +104 -0
- package/dist/hooks/matcher.d.ts +22 -0
- package/dist/hooks/matcher.js +108 -0
- package/dist/hooks/protocol.d.ts +33 -0
- package/dist/hooks/protocol.js +240 -0
- package/dist/hooks/runner.d.ts +30 -0
- package/dist/hooks/runner.js +143 -0
- package/dist/hooks/types.d.ts +147 -0
- package/dist/hooks/types.js +20 -0
- package/dist/host/api-impl.d.ts +75 -0
- package/dist/host/api-impl.js +173 -0
- package/dist/host/loader.d.ts +31 -0
- package/dist/host/loader.js +147 -0
- package/dist/host/types.d.ts +169 -0
- package/dist/host/types.js +14 -0
- package/dist/host.d.ts +5 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +28 -0
- package/dist/modes/commands-core.d.ts +44 -0
- package/dist/modes/commands-core.js +171 -0
- package/dist/modes/interactive/approval-dialog.d.ts +48 -0
- package/dist/modes/interactive/approval-dialog.js +202 -0
- package/dist/modes/interactive/commands.d.ts +41 -0
- package/dist/modes/interactive/commands.js +202 -0
- package/dist/modes/interactive/completion.d.ts +53 -0
- package/dist/modes/interactive/completion.js +157 -0
- package/dist/modes/interactive/interactive-mode.d.ts +46 -0
- package/dist/modes/interactive/interactive-mode.js +487 -0
- package/dist/modes/interactive/key-dispatch.d.ts +29 -0
- package/dist/modes/interactive/key-dispatch.js +107 -0
- package/dist/modes/interactive/line/line-editor.d.ts +50 -0
- package/dist/modes/interactive/line/line-editor.js +232 -0
- package/dist/modes/interactive/line/line-mode.d.ts +20 -0
- package/dist/modes/interactive/line/line-mode.js +216 -0
- package/dist/modes/interactive/line/line-render.d.ts +35 -0
- package/dist/modes/interactive/line/line-render.js +171 -0
- package/dist/modes/interactive/line/paste-state.d.ts +26 -0
- package/dist/modes/interactive/line/paste-state.js +72 -0
- package/dist/modes/interactive/message-view.d.ts +77 -0
- package/dist/modes/interactive/message-view.js +269 -0
- package/dist/modes/interactive/pickers.d.ts +42 -0
- package/dist/modes/interactive/pickers.js +115 -0
- package/dist/modes/interactive/startup-ui.d.ts +28 -0
- package/dist/modes/interactive/startup-ui.js +238 -0
- package/dist/modes/interactive/status-bar.d.ts +48 -0
- package/dist/modes/interactive/status-bar.js +139 -0
- package/dist/modes/interactive/tool-view.d.ts +95 -0
- package/dist/modes/interactive/tool-view.js +344 -0
- package/dist/modes/print/json-event.d.ts +15 -0
- package/dist/modes/print/json-event.js +39 -0
- package/dist/modes/print/print-mode.d.ts +15 -0
- package/dist/modes/print/print-mode.js +96 -0
- package/dist/modes/rpc/commands.d.ts +40 -0
- package/dist/modes/rpc/commands.js +258 -0
- package/dist/modes/rpc/jsonl.d.ts +16 -0
- package/dist/modes/rpc/jsonl.js +55 -0
- package/dist/modes/rpc/rpc-mode.d.ts +22 -0
- package/dist/modes/rpc/rpc-mode.js +153 -0
- package/dist/modes/session-report.d.ts +55 -0
- package/dist/modes/session-report.js +278 -0
- package/dist/modes/shared.d.ts +13 -0
- package/dist/modes/shared.js +52 -0
- package/dist/modes/startup-ui-text.d.ts +16 -0
- package/dist/modes/startup-ui-text.js +103 -0
- package/dist/permissions/broker.d.ts +47 -0
- package/dist/permissions/broker.js +107 -0
- package/dist/permissions/dangerous.d.ts +47 -0
- package/dist/permissions/dangerous.js +370 -0
- package/dist/permissions/pipeline.d.ts +51 -0
- package/dist/permissions/pipeline.js +181 -0
- package/dist/permissions/preview.d.ts +43 -0
- package/dist/permissions/preview.js +463 -0
- package/dist/permissions/rules.d.ts +58 -0
- package/dist/permissions/rules.js +249 -0
- package/dist/permissions/types.d.ts +100 -0
- package/dist/permissions/types.js +18 -0
- package/dist/rpc.d.ts +171 -0
- package/dist/rpc.js +13 -0
- package/dist/sdk.d.ts +98 -0
- package/dist/sdk.js +195 -0
- package/dist/session/manager.d.ts +70 -0
- package/dist/session/manager.js +278 -0
- package/dist/session/migrate.d.ts +19 -0
- package/dist/session/migrate.js +53 -0
- package/dist/session/projection.d.ts +50 -0
- package/dist/session/projection.js +180 -0
- package/dist/session/store.d.ts +47 -0
- package/dist/session/store.js +210 -0
- package/dist/session/tree.d.ts +21 -0
- package/dist/session/tree.js +88 -0
- package/dist/session/types.d.ts +199 -0
- package/dist/session/types.js +18 -0
- package/dist/skills/discover.d.ts +60 -0
- package/dist/skills/discover.js +160 -0
- package/dist/skills/expand.d.ts +35 -0
- package/dist/skills/expand.js +46 -0
- package/dist/skills/frontmatter.d.ts +27 -0
- package/dist/skills/frontmatter.js +157 -0
- package/dist/skills/index-prompt.d.ts +13 -0
- package/dist/skills/index-prompt.js +42 -0
- package/dist/skills/templates.d.ts +52 -0
- package/dist/skills/templates.js +177 -0
- package/dist/tools/bash.d.ts +49 -0
- package/dist/tools/bash.js +222 -0
- package/dist/tools/edit-fuzzy.d.ts +36 -0
- package/dist/tools/edit-fuzzy.js +129 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +241 -0
- package/dist/tools/file-mutex.d.ts +8 -0
- package/dist/tools/file-mutex.js +29 -0
- package/dist/tools/glob.d.ts +44 -0
- package/dist/tools/glob.js +217 -0
- package/dist/tools/grep.d.ts +39 -0
- package/dist/tools/grep.js +179 -0
- package/dist/tools/ignore.d.ts +40 -0
- package/dist/tools/ignore.js +174 -0
- package/dist/tools/ls.d.ts +21 -0
- package/dist/tools/ls.js +96 -0
- package/dist/tools/output-accumulator.d.ts +48 -0
- package/dist/tools/output-accumulator.js +106 -0
- package/dist/tools/paths.d.ts +20 -0
- package/dist/tools/paths.js +58 -0
- package/dist/tools/presets.d.ts +71 -0
- package/dist/tools/presets.js +128 -0
- package/dist/tools/process-tree.d.ts +35 -0
- package/dist/tools/process-tree.js +125 -0
- package/dist/tools/read.d.ts +28 -0
- package/dist/tools/read.js +181 -0
- package/dist/tools/registry.d.ts +43 -0
- package/dist/tools/registry.js +106 -0
- package/dist/tools/shell.d.ts +38 -0
- package/dist/tools/shell.js +91 -0
- package/dist/tools/task.d.ts +29 -0
- package/dist/tools/task.js +107 -0
- package/dist/tools/todo.d.ts +28 -0
- package/dist/tools/todo.js +107 -0
- package/dist/tools/truncate.d.ts +44 -0
- package/dist/tools/truncate.js +175 -0
- package/dist/tools/types.d.ts +135 -0
- package/dist/tools/types.js +19 -0
- package/dist/tools/write.d.ts +20 -0
- package/dist/tools/write.js +84 -0
- package/dist/tui/ansi.d.ts +29 -0
- package/dist/tui/ansi.js +420 -0
- package/dist/tui/component.d.ts +38 -0
- package/dist/tui/component.js +13 -0
- package/dist/tui/components/box.d.ts +26 -0
- package/dist/tui/components/box.js +65 -0
- package/dist/tui/components/container.d.ts +14 -0
- package/dist/tui/components/container.js +39 -0
- package/dist/tui/components/editor-buffer.d.ts +67 -0
- package/dist/tui/components/editor-buffer.js +300 -0
- package/dist/tui/components/editor-paste.d.ts +29 -0
- package/dist/tui/components/editor-paste.js +64 -0
- package/dist/tui/components/editor.d.ts +114 -0
- package/dist/tui/components/editor.js +530 -0
- package/dist/tui/components/key-value.d.ts +29 -0
- package/dist/tui/components/key-value.js +48 -0
- package/dist/tui/components/loader.d.ts +35 -0
- package/dist/tui/components/loader.js +71 -0
- package/dist/tui/components/markdown.d.ts +90 -0
- package/dist/tui/components/markdown.js +462 -0
- package/dist/tui/components/meter.d.ts +29 -0
- package/dist/tui/components/meter.js +47 -0
- package/dist/tui/components/overlay.d.ts +24 -0
- package/dist/tui/components/overlay.js +52 -0
- package/dist/tui/components/select-list.d.ts +57 -0
- package/dist/tui/components/select-list.js +179 -0
- package/dist/tui/components/spacer.d.ts +11 -0
- package/dist/tui/components/spacer.js +16 -0
- package/dist/tui/components/text.d.ts +30 -0
- package/dist/tui/components/text.js +65 -0
- package/dist/tui/keybindings.d.ts +66 -0
- package/dist/tui/keybindings.js +120 -0
- package/dist/tui/keys.d.ts +38 -0
- package/dist/tui/keys.js +275 -0
- package/dist/tui/stdin-buffer.d.ts +41 -0
- package/dist/tui/stdin-buffer.js +206 -0
- package/dist/tui/terminal.d.ts +79 -0
- package/dist/tui/terminal.js +153 -0
- package/dist/tui/theme.d.ts +31 -0
- package/dist/tui/theme.js +199 -0
- package/dist/tui/tui.d.ts +103 -0
- package/dist/tui/tui.js +363 -0
- package/dist/tui/vt-screen.d.ts +46 -0
- package/dist/tui/vt-screen.js +251 -0
- package/dist/tui.d.ts +30 -0
- package/dist/tui.js +31 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.js +24 -0
- package/docs/codemode.md +102 -0
- package/docs/hooks.md +196 -0
- package/docs/host-api.md +161 -0
- package/docs/providers.md +316 -0
- package/docs/rpc.md +253 -0
- package/docs/session-format.md +119 -0
- package/docs/tui.md +168 -0
- package/package.json +101 -4
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK(设计 §13.1、实施计划 §1.3 C2)。[B6]
|
|
3
|
+
*
|
|
4
|
+
* - `createRuntime(options)`:与 CLI 同一条启动序列(bootstrap 第 3–14 步 + 组装根),读用户级 /
|
|
5
|
+
* 项目级配置、AGENTS.md、Skill、hooks.json、auth.json;不进入任何模式。参数可以写成字段,
|
|
6
|
+
* 也可以直接给 `argv`(与 `ama` 命令行同语法)。
|
|
7
|
+
* - `createAgentSession(options)`:不读文件系统配置的轻量装配——内存会话(或传入的
|
|
8
|
+
* SessionManager)、选定的工具、规则与回调审批,直接返回 `AgentSessionImpl`
|
|
9
|
+
* (含 `addTool / updateSystem / navigate` 等扩展方法)。
|
|
10
|
+
*
|
|
11
|
+
* 两者共用组装根(cli/compose*.ts),审批链、系统提示装配、工具预设与缓存行为与 CLI 一致。
|
|
12
|
+
*/
|
|
13
|
+
import { resolve } from "node:path";
|
|
14
|
+
import { ProviderRegistry } from "./ai/providers/registry.js";
|
|
15
|
+
import { parseArgs } from "./cli/args.js";
|
|
16
|
+
import { bootstrap } from "./cli/bootstrap.js";
|
|
17
|
+
import { withExtraProviders } from "./cli/compose-providers.js";
|
|
18
|
+
import { composeSession, emptyComposeState } from "./cli/compose-session.js";
|
|
19
|
+
import { buildRules, createRuntimeDeps, createTools } from "./cli/compose.js";
|
|
20
|
+
import { pickDefaultModel } from "./cli/default-model.js";
|
|
21
|
+
import { findContextFiles } from "./config/context-files.js";
|
|
22
|
+
import { DEFAULT_CONFIG, mergeConfig } from "./config/merge.js";
|
|
23
|
+
import { resolveConfigDir, resolveDataDir } from "./config/paths.js";
|
|
24
|
+
import { AmaError } from "./errors.js";
|
|
25
|
+
import { hooksFromConfig } from "./hooks/config.js";
|
|
26
|
+
import { HookDispatcher } from "./hooks/dispatcher.js";
|
|
27
|
+
import { AgentEventBus } from "./host/api-impl.js";
|
|
28
|
+
import { PermissionPipeline } from "./permissions/pipeline.js";
|
|
29
|
+
import { SessionManager } from "./session/manager.js";
|
|
30
|
+
import { discoverSkills } from "./skills/discover.js";
|
|
31
|
+
import { PresetToolRegistry } from "./tools/presets.js";
|
|
32
|
+
export async function createRuntime(options = {}) {
|
|
33
|
+
const parsed = parseArgs(options.argv ?? []);
|
|
34
|
+
if (parsed.kind !== "run")
|
|
35
|
+
throw new AmaError("invalid_arguments", "createRuntime 不接受子命令");
|
|
36
|
+
const args = parsed.args;
|
|
37
|
+
if (options.model !== undefined)
|
|
38
|
+
args.model = options.model;
|
|
39
|
+
if (options.thinkingLevel !== undefined)
|
|
40
|
+
args.thinking = options.thinkingLevel;
|
|
41
|
+
if (options.permissionMode !== undefined)
|
|
42
|
+
args.permissionMode = options.permissionMode;
|
|
43
|
+
if (options.toolsPreset !== undefined)
|
|
44
|
+
args.toolsPreset = options.toolsPreset;
|
|
45
|
+
if (options.trust !== undefined)
|
|
46
|
+
args.trust = options.trust;
|
|
47
|
+
if (options.sessionDir !== undefined)
|
|
48
|
+
args.sessionDir = options.sessionDir;
|
|
49
|
+
if (options.profile !== undefined)
|
|
50
|
+
args.profile = options.profile;
|
|
51
|
+
if (options.unattended === true)
|
|
52
|
+
args.print = true;
|
|
53
|
+
const env = options.env ?? process.env;
|
|
54
|
+
const io = {
|
|
55
|
+
stdout: () => undefined,
|
|
56
|
+
stderr: options.stderr ?? ((text) => void process.stderr.write(text)),
|
|
57
|
+
stdinIsTTY: false,
|
|
58
|
+
stdoutIsTTY: false,
|
|
59
|
+
env,
|
|
60
|
+
cwd: options.cwd ?? process.cwd(),
|
|
61
|
+
readStdin: async () => "",
|
|
62
|
+
};
|
|
63
|
+
return bootstrap(args, createRuntimeDeps({ env, ...options.compose }), io);
|
|
64
|
+
}
|
|
65
|
+
function keyOptions(auth) {
|
|
66
|
+
switch (auth?.kind) {
|
|
67
|
+
case undefined:
|
|
68
|
+
return {};
|
|
69
|
+
case "file":
|
|
70
|
+
return { authFile: auth.path, userAuthFile: null, useEnv: false };
|
|
71
|
+
case "env":
|
|
72
|
+
return { userAuthFile: null, useEnv: true };
|
|
73
|
+
case "inline":
|
|
74
|
+
return { configKeys: auth.keys, userAuthFile: null, useEnv: false };
|
|
75
|
+
case "none":
|
|
76
|
+
return { userAuthFile: null, useEnv: false };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function createAgentSession(options = {}) {
|
|
80
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
81
|
+
const warn = options.onWarning ?? (() => undefined);
|
|
82
|
+
let config = mergeConfig(DEFAULT_CONFIG, options.config);
|
|
83
|
+
if (options.toolsPreset !== undefined)
|
|
84
|
+
config = mergeConfig(config, { tools: { preset: options.toolsPreset } });
|
|
85
|
+
const providers = new ProviderRegistry({
|
|
86
|
+
config: withExtraProviders(config, options.providers),
|
|
87
|
+
apis: options.apis,
|
|
88
|
+
includeFake: true,
|
|
89
|
+
keys: keyOptions(options.auth),
|
|
90
|
+
onWarning: warn,
|
|
91
|
+
});
|
|
92
|
+
const ref = typeof options.model === "object"
|
|
93
|
+
? `${options.model.provider}/${options.model.id}`
|
|
94
|
+
: (options.model ?? config.defaultModel);
|
|
95
|
+
let choice;
|
|
96
|
+
if (ref !== undefined) {
|
|
97
|
+
const found = providers.findModel(ref);
|
|
98
|
+
if (!found.ok)
|
|
99
|
+
throw new AmaError("model_not_found", `模型不存在:${ref}`, { detail: found.candidates });
|
|
100
|
+
choice = found;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
choice = await pickDefaultModel(providers);
|
|
104
|
+
if (choice === undefined)
|
|
105
|
+
throw new AmaError("no_api_key", "没有可用模型:传 model,或配置任一供应商的 key");
|
|
106
|
+
}
|
|
107
|
+
const state = emptyComposeState();
|
|
108
|
+
let tools;
|
|
109
|
+
if (options.tools === undefined || options.tools === "default") {
|
|
110
|
+
tools = createTools({ config, cwd, mode: "line" }, { extraTools: options.extraTools ?? [] }, state);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
tools = new PresetToolRegistry();
|
|
114
|
+
for (const tool of options.tools === "none" ? [] : options.tools)
|
|
115
|
+
tools.register(tool, "sdk");
|
|
116
|
+
for (const tool of options.extraTools ?? [])
|
|
117
|
+
tools.register(tool, "sdk");
|
|
118
|
+
}
|
|
119
|
+
for (const name of options.disableTools ?? [])
|
|
120
|
+
tools.disable(name);
|
|
121
|
+
const mode = options.permission?.mode ?? config.permission?.mode ?? "default";
|
|
122
|
+
const rules = buildRules([
|
|
123
|
+
...(options.permission?.allow ?? []).map((raw) => ({
|
|
124
|
+
effect: "allow",
|
|
125
|
+
raw,
|
|
126
|
+
source: "sdk",
|
|
127
|
+
})),
|
|
128
|
+
...(options.permission?.deny ?? []).map((raw) => ({
|
|
129
|
+
effect: "deny",
|
|
130
|
+
raw,
|
|
131
|
+
source: "sdk",
|
|
132
|
+
})),
|
|
133
|
+
], config.permission?.builtinDeny, warn);
|
|
134
|
+
const permission = new PermissionPipeline({ mode, rules, cwd });
|
|
135
|
+
const manager = options.sessionManager ?? SessionManager.inMemory(cwd);
|
|
136
|
+
let session;
|
|
137
|
+
const hooks = new HookDispatcher({
|
|
138
|
+
hooks: options.hooks === undefined || options.hooks === false ? [] : hooksFromConfig(options.hooks),
|
|
139
|
+
context: () => ({
|
|
140
|
+
sessionId: manager.id,
|
|
141
|
+
cwd,
|
|
142
|
+
model: session?.state.model ?? { provider: choice.model.provider, id: choice.model.id },
|
|
143
|
+
permissionMode: permission.mode,
|
|
144
|
+
depth: 0,
|
|
145
|
+
}),
|
|
146
|
+
onWarning: warn,
|
|
147
|
+
});
|
|
148
|
+
const configDir = resolveConfigDir();
|
|
149
|
+
if (options.skillDirs !== undefined && options.skillDirs.length > 0) {
|
|
150
|
+
const sources = options.skillDirs.map((dir) => ({
|
|
151
|
+
dir: resolve(cwd, dir),
|
|
152
|
+
scope: "cli",
|
|
153
|
+
requiresTrust: false,
|
|
154
|
+
}));
|
|
155
|
+
const found = await discoverSkills(sources, { trusted: true });
|
|
156
|
+
state.skills = found.skills;
|
|
157
|
+
found.warnings.forEach((w) => warn(w));
|
|
158
|
+
}
|
|
159
|
+
const context = options.contextFiles === true ? findContextFiles({ cwd, configDir }) : undefined;
|
|
160
|
+
const ask = options.permission?.ask;
|
|
161
|
+
const assembly = {
|
|
162
|
+
mode: options.unattended === true ? "print" : "line",
|
|
163
|
+
paths: { configDir, dataDir: resolveDataDir(), sessionDir: manager.directory() ?? cwd, cwd },
|
|
164
|
+
config,
|
|
165
|
+
trust: { trusted: true, source: "default" },
|
|
166
|
+
resources: {
|
|
167
|
+
contextFiles: context?.files.map((f) => ({ path: f.path, content: f.content })) ?? [],
|
|
168
|
+
skills: state.skills.map((s) => ({
|
|
169
|
+
name: s.name,
|
|
170
|
+
description: s.description,
|
|
171
|
+
location: s.location,
|
|
172
|
+
})),
|
|
173
|
+
prompts: [],
|
|
174
|
+
instructions: [],
|
|
175
|
+
},
|
|
176
|
+
providers,
|
|
177
|
+
model: choice.model,
|
|
178
|
+
provider: choice.provider,
|
|
179
|
+
thinkingLevel: options.thinkingLevel ?? config.thinkingLevel ?? "medium",
|
|
180
|
+
sessionManager: manager,
|
|
181
|
+
source: manager.entries().length > 0 ? "resume" : "startup",
|
|
182
|
+
hooks,
|
|
183
|
+
permission,
|
|
184
|
+
tools,
|
|
185
|
+
events: new AgentEventBus(),
|
|
186
|
+
host: { handle: undefined, broker: () => undefined, instructions: options.instructions ?? [] },
|
|
187
|
+
uiBroker: () => (ask === undefined ? undefined : { ask }),
|
|
188
|
+
onSessionReplaced: () => undefined,
|
|
189
|
+
sessionStartContext: () => undefined,
|
|
190
|
+
unattended: options.unattended === true,
|
|
191
|
+
warn,
|
|
192
|
+
};
|
|
193
|
+
session = composeSession(assembly, state, { log: options.log ?? (() => undefined) });
|
|
194
|
+
return session;
|
|
195
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionManager(设计 §8、§11.1 第 7 / 16 步):JSONL 条目树的读写与叶子管理。[B2]
|
|
3
|
+
*
|
|
4
|
+
* - `inMemory(cwd)`:从不落盘。
|
|
5
|
+
* - `create(dir, cwd)`:**延迟落盘**——`append()` 先记在内存,直到 `flush()`(AgentSession 在
|
|
6
|
+
* 首次模型请求前调用)才建文件、写头与已缓存条目、加锁;之后每次 `append()` 立即追加一行。
|
|
7
|
+
* - `open(file)`:读文件(修复末尾半行)、校验版本、加锁;叶子 = 文件中最后一条条目,
|
|
8
|
+
* 若其后还有 `leaf` 行(`/tree` 换叶子落盘,第三波 A7)则取最后一条 leaf 行。
|
|
9
|
+
* - `setLeaf(id)`:已落盘时追加一行 `leaf{id, timestamp}`;延迟会话在 `flush()` 时补写。
|
|
10
|
+
* - `fork(entryId)`:复制 root → entryId 的分支到新文件(头的 parentSession 指回本文件),
|
|
11
|
+
* 不复制 leaf 行。
|
|
12
|
+
* - `close()`:释放锁;之后的 append 抛错。
|
|
13
|
+
*/
|
|
14
|
+
import type { SessionEntry, SessionEntryInput, SessionHeader, SessionListItem, SessionManagerApi, SessionTreeNode } from "./types.js";
|
|
15
|
+
export interface SessionManagerOptions {
|
|
16
|
+
/** 测试注入时钟。 */
|
|
17
|
+
now?: () => Date;
|
|
18
|
+
/** 头里的 parentSession。 */
|
|
19
|
+
parentSession?: string;
|
|
20
|
+
/** 指定会话 id(`--session-id`)。 */
|
|
21
|
+
id?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class SessionManager implements SessionManagerApi {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly cwd: string;
|
|
26
|
+
private readonly _header;
|
|
27
|
+
private readonly _entries;
|
|
28
|
+
private readonly index;
|
|
29
|
+
private leaf;
|
|
30
|
+
private storage;
|
|
31
|
+
private closed;
|
|
32
|
+
private readonly now;
|
|
33
|
+
private constructor();
|
|
34
|
+
private static newHeader;
|
|
35
|
+
static inMemory(cwd: string, options?: SessionManagerOptions): SessionManager;
|
|
36
|
+
/** `dir` 是会话文件所在目录(通常是 `sessionDirForCwd(sessionsRoot, cwd)`)。 */
|
|
37
|
+
static create(dir: string, cwd: string, options?: SessionManagerOptions): SessionManager;
|
|
38
|
+
static createForCwd(sessionsRoot: string, cwd: string, options?: SessionManagerOptions): SessionManager;
|
|
39
|
+
static open(file: string, options?: {
|
|
40
|
+
now?: () => Date;
|
|
41
|
+
}): SessionManager;
|
|
42
|
+
/** `--continue`:本目录最近一条;没有则新建(延迟落盘)。 */
|
|
43
|
+
static continueRecent(dir: string, cwd: string): SessionManager;
|
|
44
|
+
/** 只读列出目录下的会话(最新在前);损坏的文件跳过。 */
|
|
45
|
+
static list(dir: string): SessionListItem[];
|
|
46
|
+
file(): string | undefined;
|
|
47
|
+
header(): SessionHeader;
|
|
48
|
+
entries(): readonly SessionEntry[];
|
|
49
|
+
getEntry(id: string): SessionEntry | undefined;
|
|
50
|
+
leafId(): string | null;
|
|
51
|
+
append(input: SessionEntryInput): SessionEntry;
|
|
52
|
+
setLeaf(id: string | null): void;
|
|
53
|
+
private leafLine;
|
|
54
|
+
branch(leafId?: string | null): SessionEntry[];
|
|
55
|
+
getEntries(since?: string): {
|
|
56
|
+
entries: SessionEntry[];
|
|
57
|
+
leafId: string | null;
|
|
58
|
+
};
|
|
59
|
+
getTree(): SessionTreeNode[];
|
|
60
|
+
name(): string | undefined;
|
|
61
|
+
setName(name: string): void;
|
|
62
|
+
fork(entryId: string): SessionManager;
|
|
63
|
+
/** 延迟会话:建文件、写头与缓存条目、加锁。返回文件路径(内存会话为 undefined)。幂等。 */
|
|
64
|
+
flush(): string | undefined;
|
|
65
|
+
isPersisted(): boolean;
|
|
66
|
+
isInMemory(): boolean;
|
|
67
|
+
/** 会话文件所在目录(内存会话 undefined)。 */
|
|
68
|
+
directory(): string | undefined;
|
|
69
|
+
close(): void;
|
|
70
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionManager(设计 §8、§11.1 第 7 / 16 步):JSONL 条目树的读写与叶子管理。[B2]
|
|
3
|
+
*
|
|
4
|
+
* - `inMemory(cwd)`:从不落盘。
|
|
5
|
+
* - `create(dir, cwd)`:**延迟落盘**——`append()` 先记在内存,直到 `flush()`(AgentSession 在
|
|
6
|
+
* 首次模型请求前调用)才建文件、写头与已缓存条目、加锁;之后每次 `append()` 立即追加一行。
|
|
7
|
+
* - `open(file)`:读文件(修复末尾半行)、校验版本、加锁;叶子 = 文件中最后一条条目,
|
|
8
|
+
* 若其后还有 `leaf` 行(`/tree` 换叶子落盘,第三波 A7)则取最后一条 leaf 行。
|
|
9
|
+
* - `setLeaf(id)`:已落盘时追加一行 `leaf{id, timestamp}`;延迟会话在 `flush()` 时补写。
|
|
10
|
+
* - `fork(entryId)`:复制 root → entryId 的分支到新文件(头的 parentSession 指回本文件),
|
|
11
|
+
* 不复制 leaf 行。
|
|
12
|
+
* - `close()`:释放锁;之后的 append 抛错。
|
|
13
|
+
*/
|
|
14
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
15
|
+
import { statSync } from "node:fs";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { AmaError } from "../errors.js";
|
|
18
|
+
import { AMA_VERSION } from "../version.js";
|
|
19
|
+
import { migrateSessionLines } from "./migrate.js";
|
|
20
|
+
import { acquireLock, appendLines, listSessionFiles, readSessionLines, sessionDirForCwd, sessionFileName, writeNewSessionFile, } from "./store.js";
|
|
21
|
+
import { buildTree, indexEntries, pathToRoot } from "./tree.js";
|
|
22
|
+
import { SESSION_FORMAT_VERSION } from "./types.js";
|
|
23
|
+
function newEntryId(taken) {
|
|
24
|
+
for (;;) {
|
|
25
|
+
const id = randomBytes(4).toString("hex");
|
|
26
|
+
if (!taken.has(id))
|
|
27
|
+
return id;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class SessionManager {
|
|
31
|
+
id;
|
|
32
|
+
cwd;
|
|
33
|
+
_header;
|
|
34
|
+
_entries;
|
|
35
|
+
index;
|
|
36
|
+
leaf;
|
|
37
|
+
storage;
|
|
38
|
+
closed = false;
|
|
39
|
+
now;
|
|
40
|
+
constructor(header, entries, storage, now, leafId) {
|
|
41
|
+
this._header = header;
|
|
42
|
+
this.id = header.id;
|
|
43
|
+
this.cwd = header.cwd;
|
|
44
|
+
this._entries = entries;
|
|
45
|
+
this.index = indexEntries(entries);
|
|
46
|
+
const last = entries.at(-1)?.id ?? null;
|
|
47
|
+
this.leaf =
|
|
48
|
+
leafId === undefined || (leafId !== null && !this.index.has(leafId)) ? last : leafId;
|
|
49
|
+
this.storage = storage;
|
|
50
|
+
this.now = now;
|
|
51
|
+
}
|
|
52
|
+
static newHeader(cwd, options, now) {
|
|
53
|
+
const header = {
|
|
54
|
+
type: "session",
|
|
55
|
+
version: SESSION_FORMAT_VERSION,
|
|
56
|
+
id: options.id ?? randomUUID(),
|
|
57
|
+
timestamp: now.toISOString(),
|
|
58
|
+
cwd,
|
|
59
|
+
agent: { name: "ama", version: AMA_VERSION },
|
|
60
|
+
};
|
|
61
|
+
if (options.parentSession !== undefined)
|
|
62
|
+
header.parentSession = options.parentSession;
|
|
63
|
+
return header;
|
|
64
|
+
}
|
|
65
|
+
static inMemory(cwd, options = {}) {
|
|
66
|
+
const now = options.now ?? (() => new Date());
|
|
67
|
+
return new SessionManager(this.newHeader(cwd, options, now()), [], { kind: "memory" }, now);
|
|
68
|
+
}
|
|
69
|
+
/** `dir` 是会话文件所在目录(通常是 `sessionDirForCwd(sessionsRoot, cwd)`)。 */
|
|
70
|
+
static create(dir, cwd, options = {}) {
|
|
71
|
+
const now = options.now ?? (() => new Date());
|
|
72
|
+
return new SessionManager(this.newHeader(cwd, options, now()), [], { kind: "lazy", dir }, now);
|
|
73
|
+
}
|
|
74
|
+
static createForCwd(sessionsRoot, cwd, options = {}) {
|
|
75
|
+
return this.create(sessionDirForCwd(sessionsRoot, cwd), cwd, options);
|
|
76
|
+
}
|
|
77
|
+
static open(file, options = {}) {
|
|
78
|
+
const lock = acquireLock(file);
|
|
79
|
+
try {
|
|
80
|
+
const { lines } = readSessionLines(file, { repair: true });
|
|
81
|
+
const { header, entries, leafId } = migrateSessionLines(lines, file);
|
|
82
|
+
return new SessionManager(header, entries, { kind: "file", file, lock }, options.now ?? (() => new Date()), leafId);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
lock.release();
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** `--continue`:本目录最近一条;没有则新建(延迟落盘)。 */
|
|
90
|
+
static continueRecent(dir, cwd) {
|
|
91
|
+
const [latest] = listSessionFiles(dir);
|
|
92
|
+
return latest === undefined ? this.create(dir, cwd) : this.open(latest);
|
|
93
|
+
}
|
|
94
|
+
/** 只读列出目录下的会话(最新在前);损坏的文件跳过。 */
|
|
95
|
+
static list(dir) {
|
|
96
|
+
const items = [];
|
|
97
|
+
for (const file of listSessionFiles(dir)) {
|
|
98
|
+
try {
|
|
99
|
+
const { header, entries } = migrateSessionLines(readSessionLines(file).lines, file);
|
|
100
|
+
let name;
|
|
101
|
+
let firstPrompt;
|
|
102
|
+
let messageCount = 0;
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
if (entry.type === "session_info" && entry.name !== undefined)
|
|
105
|
+
name = entry.name;
|
|
106
|
+
if (entry.type !== "message" || entry.message.role === "system")
|
|
107
|
+
continue;
|
|
108
|
+
messageCount++;
|
|
109
|
+
if (firstPrompt === undefined && entry.message.role === "user") {
|
|
110
|
+
const { content } = entry.message;
|
|
111
|
+
firstPrompt =
|
|
112
|
+
typeof content === "string"
|
|
113
|
+
? content
|
|
114
|
+
: content.map((block) => (block.type === "text" ? block.text : "")).join("");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const item = {
|
|
118
|
+
id: header.id,
|
|
119
|
+
file,
|
|
120
|
+
cwd: header.cwd,
|
|
121
|
+
createdAt: header.timestamp,
|
|
122
|
+
modifiedAt: statSync(file).mtime.toISOString(),
|
|
123
|
+
messageCount,
|
|
124
|
+
};
|
|
125
|
+
if (name !== undefined)
|
|
126
|
+
item.name = name;
|
|
127
|
+
if (firstPrompt !== undefined)
|
|
128
|
+
item.firstPrompt = firstPrompt.slice(0, 200);
|
|
129
|
+
items.push(item);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// 损坏 / 不可读:列表里跳过(sessions show 会给出具体错误)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return items;
|
|
136
|
+
}
|
|
137
|
+
// -------------------------------------------------------------------------
|
|
138
|
+
// SessionManagerApi
|
|
139
|
+
// -------------------------------------------------------------------------
|
|
140
|
+
file() {
|
|
141
|
+
return this.storage.kind === "file" ? this.storage.file : undefined;
|
|
142
|
+
}
|
|
143
|
+
header() {
|
|
144
|
+
return this._header;
|
|
145
|
+
}
|
|
146
|
+
entries() {
|
|
147
|
+
return this._entries;
|
|
148
|
+
}
|
|
149
|
+
getEntry(id) {
|
|
150
|
+
return this.index.get(id);
|
|
151
|
+
}
|
|
152
|
+
leafId() {
|
|
153
|
+
return this.leaf;
|
|
154
|
+
}
|
|
155
|
+
append(input) {
|
|
156
|
+
if (this.closed)
|
|
157
|
+
throw new AmaError("session_closed", "session manager is closed");
|
|
158
|
+
const entry = {
|
|
159
|
+
...input,
|
|
160
|
+
id: newEntryId(this.index),
|
|
161
|
+
parentId: this.leaf,
|
|
162
|
+
timestamp: this.now().toISOString(),
|
|
163
|
+
};
|
|
164
|
+
if (this.storage.kind === "file")
|
|
165
|
+
appendLines(this.storage.file, [entry]);
|
|
166
|
+
this._entries.push(entry);
|
|
167
|
+
this.index.set(entry.id, entry);
|
|
168
|
+
this.leaf = entry.id;
|
|
169
|
+
return entry;
|
|
170
|
+
}
|
|
171
|
+
setLeaf(id) {
|
|
172
|
+
if (id !== null && !this.index.has(id)) {
|
|
173
|
+
throw new AmaError("invalid_arguments", `no such session entry: ${id}`);
|
|
174
|
+
}
|
|
175
|
+
this.leaf = id;
|
|
176
|
+
if (this.storage.kind === "file")
|
|
177
|
+
appendLines(this.storage.file, [this.leafLine()]);
|
|
178
|
+
}
|
|
179
|
+
leafLine() {
|
|
180
|
+
return { type: "leaf", id: this.leaf, timestamp: this.now().toISOString() };
|
|
181
|
+
}
|
|
182
|
+
branch(leafId) {
|
|
183
|
+
return pathToRoot(this.index, leafId === undefined ? this.leaf : leafId);
|
|
184
|
+
}
|
|
185
|
+
getEntries(since) {
|
|
186
|
+
if (since === undefined)
|
|
187
|
+
return { entries: [...this._entries], leafId: this.leaf };
|
|
188
|
+
const at = this._entries.findIndex((entry) => entry.id === since);
|
|
189
|
+
return {
|
|
190
|
+
entries: at < 0 ? [...this._entries] : this._entries.slice(at + 1),
|
|
191
|
+
leafId: this.leaf,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
getTree() {
|
|
195
|
+
return buildTree(this._entries);
|
|
196
|
+
}
|
|
197
|
+
name() {
|
|
198
|
+
let name;
|
|
199
|
+
for (const entry of this._entries)
|
|
200
|
+
if (entry.type === "session_info")
|
|
201
|
+
name = entry.name;
|
|
202
|
+
return name;
|
|
203
|
+
}
|
|
204
|
+
setName(name) {
|
|
205
|
+
this.append({ type: "session_info", name });
|
|
206
|
+
}
|
|
207
|
+
fork(entryId) {
|
|
208
|
+
if (!this.index.has(entryId)) {
|
|
209
|
+
throw new AmaError("invalid_arguments", `no such session entry: ${entryId}`);
|
|
210
|
+
}
|
|
211
|
+
const copied = this.branch(entryId).map((entry) => structuredClone(entry));
|
|
212
|
+
const options = { now: this.now };
|
|
213
|
+
const parentFile = this.file();
|
|
214
|
+
if (parentFile !== undefined)
|
|
215
|
+
options.parentSession = parentFile;
|
|
216
|
+
const header = SessionManager.newHeader(this.cwd, options, this.now());
|
|
217
|
+
let storage;
|
|
218
|
+
if (this.storage.kind === "memory")
|
|
219
|
+
storage = { kind: "memory" };
|
|
220
|
+
else if (this.storage.kind === "lazy")
|
|
221
|
+
storage = { kind: "lazy", dir: this.storage.dir };
|
|
222
|
+
else
|
|
223
|
+
storage = { kind: "lazy", dir: dirname(this.storage.file) };
|
|
224
|
+
const forked = new SessionManager(header, copied, storage, this.now);
|
|
225
|
+
if (parentFile !== undefined)
|
|
226
|
+
forked.flush();
|
|
227
|
+
return forked;
|
|
228
|
+
}
|
|
229
|
+
// -------------------------------------------------------------------------
|
|
230
|
+
// 落盘控制
|
|
231
|
+
// -------------------------------------------------------------------------
|
|
232
|
+
/** 延迟会话:建文件、写头与缓存条目、加锁。返回文件路径(内存会话为 undefined)。幂等。 */
|
|
233
|
+
flush() {
|
|
234
|
+
if (this.closed)
|
|
235
|
+
throw new AmaError("session_closed", "session manager is closed");
|
|
236
|
+
if (this.storage.kind === "memory")
|
|
237
|
+
return undefined;
|
|
238
|
+
if (this.storage.kind === "file")
|
|
239
|
+
return this.storage.file;
|
|
240
|
+
const file = join(this.storage.dir, sessionFileName(new Date(this._header.timestamp), this.id));
|
|
241
|
+
const moved = this.leaf !== (this._entries.at(-1)?.id ?? null);
|
|
242
|
+
writeNewSessionFile(file, [
|
|
243
|
+
this._header,
|
|
244
|
+
...this._entries,
|
|
245
|
+
...(moved ? [this.leafLine()] : []),
|
|
246
|
+
]);
|
|
247
|
+
let lock;
|
|
248
|
+
try {
|
|
249
|
+
lock = acquireLock(file);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
lock = undefined;
|
|
253
|
+
}
|
|
254
|
+
this.storage = { kind: "file", file, lock };
|
|
255
|
+
return file;
|
|
256
|
+
}
|
|
257
|
+
isPersisted() {
|
|
258
|
+
return this.storage.kind === "file";
|
|
259
|
+
}
|
|
260
|
+
isInMemory() {
|
|
261
|
+
return this.storage.kind === "memory";
|
|
262
|
+
}
|
|
263
|
+
/** 会话文件所在目录(内存会话 undefined)。 */
|
|
264
|
+
directory() {
|
|
265
|
+
if (this.storage.kind === "lazy")
|
|
266
|
+
return this.storage.dir;
|
|
267
|
+
if (this.storage.kind === "file")
|
|
268
|
+
return dirname(this.storage.file);
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
close() {
|
|
272
|
+
if (this.closed)
|
|
273
|
+
return;
|
|
274
|
+
this.closed = true;
|
|
275
|
+
if (this.storage.kind === "file")
|
|
276
|
+
this.storage.lock?.release();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话文件版本兜底(设计 §1.2「v1 文件读取兜底(预留)」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 当前格式版本 1。读入的首行必须是 `type: "session"` 的头:
|
|
5
|
+
* - version 1 → 原样返回条目;
|
|
6
|
+
* - 更高版本 → 拒绝(session_corrupt,提示升级 ama);
|
|
7
|
+
* - 缺头 / 头损坏 / 条目缺 id → 拒绝,不猜测、不重写文件。
|
|
8
|
+
* - [W3-C1b] `leaf` 行(`/tree` 位置,第三波 A7)不是条目:不进 entries;最后一条 leaf 行若在
|
|
9
|
+
* 最后一条条目之后,作为 `leafId` 返回(之后又追加了条目则作废)。id 须为字符串或 null。
|
|
10
|
+
* 将来格式升级时在这里加「旧版本 → 当前」的内存迁移(文件只追加,不回写)。
|
|
11
|
+
*/
|
|
12
|
+
import type { SessionEntry, SessionHeader, SessionLine } from "./types.js";
|
|
13
|
+
export interface MigratedSession {
|
|
14
|
+
header: SessionHeader;
|
|
15
|
+
entries: SessionEntry[];
|
|
16
|
+
/** 晚于最后一条条目的 leaf 行给出的叶子;没有则缺省(叶子 = 最后一条条目)。 */
|
|
17
|
+
leafId?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function migrateSessionLines(lines: readonly SessionLine[], file?: string): MigratedSession;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话文件版本兜底(设计 §1.2「v1 文件读取兜底(预留)」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 当前格式版本 1。读入的首行必须是 `type: "session"` 的头:
|
|
5
|
+
* - version 1 → 原样返回条目;
|
|
6
|
+
* - 更高版本 → 拒绝(session_corrupt,提示升级 ama);
|
|
7
|
+
* - 缺头 / 头损坏 / 条目缺 id → 拒绝,不猜测、不重写文件。
|
|
8
|
+
* - [W3-C1b] `leaf` 行(`/tree` 位置,第三波 A7)不是条目:不进 entries;最后一条 leaf 行若在
|
|
9
|
+
* 最后一条条目之后,作为 `leafId` 返回(之后又追加了条目则作废)。id 须为字符串或 null。
|
|
10
|
+
* 将来格式升级时在这里加「旧版本 → 当前」的内存迁移(文件只追加,不回写)。
|
|
11
|
+
*/
|
|
12
|
+
import { AmaError } from "../errors.js";
|
|
13
|
+
import { SESSION_FORMAT_VERSION } from "./types.js";
|
|
14
|
+
function isObject(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
export function migrateSessionLines(lines, file = "<memory>") {
|
|
18
|
+
const [first, ...rest] = lines;
|
|
19
|
+
if (!isObject(first) || first["type"] !== "session") {
|
|
20
|
+
throw new AmaError("session_corrupt", `${file}: missing session header`);
|
|
21
|
+
}
|
|
22
|
+
const version = first["version"];
|
|
23
|
+
if (version !== SESSION_FORMAT_VERSION) {
|
|
24
|
+
throw new AmaError("session_corrupt", `${file}: unsupported session format version ${String(version)} (this ama reads version ${SESSION_FORMAT_VERSION})`);
|
|
25
|
+
}
|
|
26
|
+
if (typeof first["id"] !== "string" || typeof first["cwd"] !== "string") {
|
|
27
|
+
throw new AmaError("session_corrupt", `${file}: malformed session header`);
|
|
28
|
+
}
|
|
29
|
+
const entries = [];
|
|
30
|
+
let leafId;
|
|
31
|
+
rest.forEach((line, i) => {
|
|
32
|
+
if (isObject(line) && line["type"] === "leaf") {
|
|
33
|
+
const id = line["id"];
|
|
34
|
+
if (id !== null && typeof id !== "string") {
|
|
35
|
+
throw new AmaError("session_corrupt", `${file}:${i + 2}: malformed leaf line`);
|
|
36
|
+
}
|
|
37
|
+
leafId = id;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!isObject(line) || typeof line["id"] !== "string" || typeof line["type"] !== "string") {
|
|
41
|
+
throw new AmaError("session_corrupt", `${file}:${i + 2}: malformed entry`);
|
|
42
|
+
}
|
|
43
|
+
if (line["type"] === "session") {
|
|
44
|
+
throw new AmaError("session_corrupt", `${file}:${i + 2}: duplicate session header`);
|
|
45
|
+
}
|
|
46
|
+
entries.push(line);
|
|
47
|
+
leafId = undefined;
|
|
48
|
+
});
|
|
49
|
+
const result = { header: first, entries };
|
|
50
|
+
if (leafId !== undefined)
|
|
51
|
+
result.leafId = leafId;
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 投影(设计 §8、§9):活动分支 → 进上下文的消息。[B2]
|
|
3
|
+
*
|
|
4
|
+
* - `buildContextEntries(branch)`:若路径上有 compaction,取最新一条:先放 compaction,再放
|
|
5
|
+
* `firstKeptEntryId` 到 compaction 之间的非 system 条目,再放 compaction 之后的条目。
|
|
6
|
+
* - `buildProjection(branch)`:对上一步的条目套用 `context_edit`(同一目标在活动分支上最新一条赢;
|
|
7
|
+
* `null` 剔除,字符串只换内容、保留角色与元数据),并把每个条目映射为 `AgentMessage`。
|
|
8
|
+
* 有 compaction 时,compaction 之前的全部 system 消息折成一条完整的 system 检查点放在最前
|
|
9
|
+
* (会话条目里不另存检查点,重放即可得到)。
|
|
10
|
+
* - `buildContext(branch)`:投影消息 + 路径上最近的模型 / 思考级别选择。
|
|
11
|
+
*/
|
|
12
|
+
import type { Message, ModelRef, ModelThinkingLevel, SystemMessage, ToolDecl } from "../ai/types.js";
|
|
13
|
+
import type { AgentMessage, CompactionEntry, ContextEditEntry, SessionEntry } from "./types.js";
|
|
14
|
+
export interface ContextItem {
|
|
15
|
+
/** 来源条目(system 检查点的来源是 compaction 条目)。 */
|
|
16
|
+
entry: SessionEntry;
|
|
17
|
+
message: AgentMessage;
|
|
18
|
+
}
|
|
19
|
+
export interface Projection {
|
|
20
|
+
items: ContextItem[];
|
|
21
|
+
messages: AgentMessage[];
|
|
22
|
+
/** 生效的最新 compaction。 */
|
|
23
|
+
compaction: CompactionEntry | undefined;
|
|
24
|
+
}
|
|
25
|
+
export interface ProjectedContext {
|
|
26
|
+
messages: AgentMessage[];
|
|
27
|
+
model: ModelRef | undefined;
|
|
28
|
+
thinkingLevel: ModelThinkingLevel | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 条目 → 上下文消息;不进上下文的条目返回 undefined(model_change、label、custom、
|
|
32
|
+
* 第三波的 `usage` 条目等)。
|
|
33
|
+
*/
|
|
34
|
+
export declare function entryToMessage(entry: SessionEntry): AgentMessage | undefined;
|
|
35
|
+
export declare function buildContextEntries(branch: readonly SessionEntry[]): SessionEntry[];
|
|
36
|
+
/** 活动分支上每个目标最新的 context_edit。 */
|
|
37
|
+
export declare function collectContextEdits(branch: readonly SessionEntry[]): Map<string, ContextEditEntry>;
|
|
38
|
+
/** 用替换文本换掉消息内容,保留角色与元数据。 */
|
|
39
|
+
export declare function applyReplacement(message: AgentMessage, text: string): AgentMessage;
|
|
40
|
+
export interface SystemState {
|
|
41
|
+
/** 节名 → 文本,按首次出现顺序。 */
|
|
42
|
+
sections: Record<string, string>;
|
|
43
|
+
tools: ToolDecl[];
|
|
44
|
+
}
|
|
45
|
+
/** 依次应用转录里的 system 消息(首条全量,之后是补丁);没有 system 消息 → undefined。 */
|
|
46
|
+
export declare function replaySystem(messages: Iterable<AgentMessage | Message>): SystemState | undefined;
|
|
47
|
+
/** 把重放结果写成一条完整的 system 消息。 */
|
|
48
|
+
export declare function systemCheckpoint(state: SystemState, timestamp: number): SystemMessage;
|
|
49
|
+
export declare function buildProjection(branch: readonly SessionEntry[]): Projection;
|
|
50
|
+
export declare function buildContext(branch: readonly SessionEntry[]): ProjectedContext;
|