@armadra/agent 0.0.0-stage → 0.3.0
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 +37 -0
- package/LICENSE +21 -0
- package/README.md +470 -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 +514 -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 +119 -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 +111 -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 +485 -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 +304 -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 +103 -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/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +114 -0
- package/dist/ai/providers/registry.js +458 -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 +448 -0
- package/dist/ai/types.js +19 -0
- package/dist/bundle/ama-sandbox.cjs +324 -0
- package/dist/bundle/ama.cjs +27837 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.js +15 -0
- package/dist/cli/args.d.ts +85 -0
- package/dist/cli/args.js +408 -0
- package/dist/cli/bootstrap.d.ts +18 -0
- package/dist/cli/bootstrap.js +399 -0
- package/dist/cli/compose-providers.d.ts +37 -0
- package/dist/cli/compose-providers.js +107 -0
- package/dist/cli/compose-session.d.ts +72 -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 +108 -0
- package/dist/cli/compose.js +221 -0
- package/dist/cli/default-model.d.ts +16 -0
- package/dist/cli/default-model.js +24 -0
- package/dist/cli/deps.d.ts +207 -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 +27 -0
- package/dist/cli/main.js +177 -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 +202 -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 +53 -0
- package/dist/cli/subcommands/config.js +277 -0
- package/dist/cli/subcommands/context.d.ts +21 -0
- package/dist/cli/subcommands/context.js +42 -0
- package/dist/cli/subcommands/doctor.d.ts +11 -0
- package/dist/cli/subcommands/doctor.js +246 -0
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +22 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -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 +46 -0
- package/dist/cli/subcommands/models-discover.js +232 -0
- package/dist/cli/subcommands/models.d.ts +40 -0
- package/dist/cli/subcommands/models.js +152 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -0
- package/dist/cli/subcommands/providers.d.ts +27 -0
- package/dist/cli/subcommands/providers.js +433 -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/init.d.ts +30 -0
- package/dist/config/init.js +92 -0
- package/dist/config/json-schema.d.ts +14 -0
- package/dist/config/json-schema.js +182 -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 +476 -0
- package/dist/config/trust.d.ts +42 -0
- package/dist/config/trust.js +122 -0
- package/dist/config/types.d.ts +190 -0
- package/dist/config/types.js +29 -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/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -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 +201 -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 +491 -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 +218 -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 +33 -0
- package/dist/modes/interactive/startup-ui.js +268 -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 +17 -0
- package/dist/modes/print/print-mode.js +107 -0
- package/dist/modes/rpc/commands.d.ts +40 -0
- package/dist/modes/rpc/commands.js +263 -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 +173 -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 +184 -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 +201 -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/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -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 +24 -0
- package/dist/tools/read.js +132 -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 +531 -0
- package/docs/rpc.md +253 -0
- package/docs/session-format.md +119 -0
- package/docs/tui.md +170 -0
- package/package.json +101 -4
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 组装根:`SessionAssembly` → `AgentSessionImpl`(实施计划 §1.2、§1.4)。[B6]
|
|
3
|
+
*
|
|
4
|
+
* - 审批:一个 `ApprovalBrokerChain`(串行队列)作 B2 `brokers` 的唯一元素;链首代理宿主 broker
|
|
5
|
+
* (`assembly.host.broker()` 每次现取,宿主可在任意时刻 `setBroker`),链尾代理模式层经
|
|
6
|
+
* `Runtime.approvals.setUiBroker` 晚绑定的 UI broker。子 Agent 的请求由 B2 包上 `context.depth`
|
|
7
|
+
* 后进同一条链,排在父对话框之后。
|
|
8
|
+
* - 系统提示:AGENTS.md + `--instructions` 文件(project_context 节)、Skill 索引(read 读取)、
|
|
9
|
+
* 宿主 instructions(host 节,读成文本)。SessionStart Hook 的 additionalContext 与宿主后加的
|
|
10
|
+
* 指令在每次提示展开前 `updateSystem`——值不变时不产生补丁,前缀保持逐字节稳定(§9.1)。
|
|
11
|
+
* - 事件:`SessionEvent` 桥接到宿主 `AgentEventBus`(§1.4 表;hook_executed、session_start /
|
|
12
|
+
* session_shutdown 由 bootstrap 发,不桥接);第三波加 `cache_miss` / `context_pressure`。
|
|
13
|
+
* - 缓存(第三波 §1.12):config `cache` 段(项目级已在合并时忽略)+ `AMA_CACHE_WARMING` /
|
|
14
|
+
* `AMA_CACHE_RETENTION` 覆盖 → 会话的 `cache` 设置;宿主 `cache.onWarmingDecision` 每次现取。
|
|
15
|
+
* - 会话切换:`switchSession()` 复用同一份装配材料建新会话,`assembly.onSessionReplaced(next)`
|
|
16
|
+
* 让宿主访问、Hook 公共字段与退出 dispose 跟随新会话。
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
19
|
+
import { AgentSessionImpl } from "../agent/session.js";
|
|
20
|
+
import { WARMING_MODES } from "../ai/cache/types.js";
|
|
21
|
+
import { isOverflowErrorText } from "../ai/overflow.js";
|
|
22
|
+
import { CACHE_RETENTIONS } from "../config/types.js";
|
|
23
|
+
import { AmaError, StartupError } from "../errors.js";
|
|
24
|
+
import { ApprovalBrokerChain, DEFAULT_APPROVAL_TIMEOUT_MS } from "../permissions/broker.js";
|
|
25
|
+
import { PermissionPipeline } from "../permissions/pipeline.js";
|
|
26
|
+
import { SessionManager } from "../session/manager.js";
|
|
27
|
+
import { sessionDirForCwd } from "../session/store.js";
|
|
28
|
+
import { expandSkillCommand } from "../skills/expand.js";
|
|
29
|
+
import { formatSkillIndex } from "../skills/index-prompt.js";
|
|
30
|
+
import { expandPromptCommand } from "../skills/templates.js";
|
|
31
|
+
import { PresetToolRegistry } from "../tools/presets.js";
|
|
32
|
+
import { openSession } from "./compose-store.js";
|
|
33
|
+
import { ExitCode } from "./exit-codes.js";
|
|
34
|
+
export function emptyComposeState() {
|
|
35
|
+
return { skills: [], templates: [], warnings: [], session: undefined };
|
|
36
|
+
}
|
|
37
|
+
const records = new WeakMap();
|
|
38
|
+
const LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
39
|
+
/** `AMA_LOG=debug|info|warn|error`(缺省 warn)过滤后写 stderr。 */
|
|
40
|
+
export function stderrLog(env = process.env) {
|
|
41
|
+
const wanted = LOG_LEVELS.indexOf((env["AMA_LOG"] ?? "warn"));
|
|
42
|
+
const min = wanted < 0 ? 2 : wanted;
|
|
43
|
+
return (level, message) => {
|
|
44
|
+
if (LOG_LEVELS.indexOf(level) >= min)
|
|
45
|
+
process.stderr.write(`ama: [${level}] ${message}\n`);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function approvalTimeout(options) {
|
|
49
|
+
const fromEnv = Number(process.env["AMA_APPROVAL_TIMEOUT_MS"]);
|
|
50
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
51
|
+
return fromEnv;
|
|
52
|
+
return options.approvalTimeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS;
|
|
53
|
+
}
|
|
54
|
+
function sourceText(source, cache, log) {
|
|
55
|
+
if (source.kind === "text")
|
|
56
|
+
return source.text;
|
|
57
|
+
const cached = cache.get(source.path);
|
|
58
|
+
if (cached !== undefined)
|
|
59
|
+
return cached;
|
|
60
|
+
let text = "";
|
|
61
|
+
try {
|
|
62
|
+
text = readFileSync(source.path, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
log("warn", `读取指令文件失败:${source.path}:${error.message}`);
|
|
66
|
+
}
|
|
67
|
+
cache.set(source.path, text);
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
function hostInstructions(record) {
|
|
71
|
+
return record.assembly.host.instructions
|
|
72
|
+
.map((s) => sourceText(s, record.hostTexts, record.log))
|
|
73
|
+
.filter((t) => t.trim() !== "");
|
|
74
|
+
}
|
|
75
|
+
/** `--instructions` 文件追加在 AGENTS.md 之后(project_context 节,实施计划表 B17)。 */
|
|
76
|
+
function contextFiles(record) {
|
|
77
|
+
const { resources } = record.assembly;
|
|
78
|
+
const extra = resources.instructions.map((s) => ({
|
|
79
|
+
path: s.kind === "file" ? s.path : (s.name ?? "instructions"),
|
|
80
|
+
content: sourceText(s, record.hostTexts, record.log),
|
|
81
|
+
}));
|
|
82
|
+
return [...resources.contextFiles, ...extra.filter((f) => f.content.trim() !== "")];
|
|
83
|
+
}
|
|
84
|
+
function systemInput(record, active) {
|
|
85
|
+
const system = { contextFiles: contextFiles(record) };
|
|
86
|
+
const index = formatSkillIndex(record.state.skills, {
|
|
87
|
+
hasSkillTool: false,
|
|
88
|
+
hasReadTool: active.includes("read"),
|
|
89
|
+
});
|
|
90
|
+
if (index !== "")
|
|
91
|
+
system.skillsIndex = index;
|
|
92
|
+
const host = hostInstructions(record);
|
|
93
|
+
if (host.length > 0)
|
|
94
|
+
system.hostInstructions = host;
|
|
95
|
+
const hookContext = record.hookContext();
|
|
96
|
+
if (hookContext !== undefined && hookContext !== "")
|
|
97
|
+
system.hookContext = hookContext;
|
|
98
|
+
return system;
|
|
99
|
+
}
|
|
100
|
+
/** 提示展开前刷新会晚到的系统提示材料(值不变则不产生补丁)。 */
|
|
101
|
+
function refreshSystem(record, session) {
|
|
102
|
+
const patch = {};
|
|
103
|
+
const hookContext = record.hookContext();
|
|
104
|
+
if (hookContext !== undefined && hookContext !== "")
|
|
105
|
+
patch.hookContext = hookContext;
|
|
106
|
+
const host = hostInstructions(record);
|
|
107
|
+
if (host.length > 0)
|
|
108
|
+
patch.hostInstructions = host;
|
|
109
|
+
if (Object.keys(patch).length > 0)
|
|
110
|
+
session.updateSystem(patch);
|
|
111
|
+
}
|
|
112
|
+
async function expandPrompt(record, session, text) {
|
|
113
|
+
refreshSystem(record, session);
|
|
114
|
+
const skill = await expandSkillCommand(text, record.state.skills);
|
|
115
|
+
if (skill?.kind === "unknown") {
|
|
116
|
+
const hint = skill.available.length > 0 ? `(可用:${skill.available.join(", ")})` : "";
|
|
117
|
+
throw new AmaError("skill_not_found", `未知 Skill:${skill.name}${hint}`);
|
|
118
|
+
}
|
|
119
|
+
if (skill?.kind === "expanded")
|
|
120
|
+
return { text: skill.text };
|
|
121
|
+
const template = await expandPromptCommand(text, record.state.templates);
|
|
122
|
+
return { text: template?.text ?? text };
|
|
123
|
+
}
|
|
124
|
+
/** config `cache` 段 + 环境变量 → 会话缓存设置;非法的环境变量值忽略并 warning。 */
|
|
125
|
+
export function cacheSettingsFrom(config, env = process.env, warn = () => undefined) {
|
|
126
|
+
const settings = {};
|
|
127
|
+
for (const [key, value] of Object.entries(config.cache ?? {})) {
|
|
128
|
+
if (value !== undefined)
|
|
129
|
+
settings[key] = value;
|
|
130
|
+
}
|
|
131
|
+
const warming = env["AMA_CACHE_WARMING"];
|
|
132
|
+
if (warming !== undefined && warming !== "") {
|
|
133
|
+
if (WARMING_MODES.includes(warming))
|
|
134
|
+
settings.warming = warming;
|
|
135
|
+
else
|
|
136
|
+
warn(`AMA_CACHE_WARMING=${warming} 无效(off | streaming | idle),已忽略`);
|
|
137
|
+
}
|
|
138
|
+
const retention = env["AMA_CACHE_RETENTION"];
|
|
139
|
+
if (retention !== undefined && retention !== "") {
|
|
140
|
+
const value = retention;
|
|
141
|
+
if (CACHE_RETENTIONS.includes(value))
|
|
142
|
+
settings.retention = value;
|
|
143
|
+
else
|
|
144
|
+
warn(`AMA_CACHE_RETENTION=${retention} 无效(none | short | long),已忽略`);
|
|
145
|
+
}
|
|
146
|
+
return settings;
|
|
147
|
+
}
|
|
148
|
+
/** §1.4:SessionEvent → AgentEvents。 */
|
|
149
|
+
export function bridgeEvent(event, bus) {
|
|
150
|
+
switch (event.type) {
|
|
151
|
+
case "before_agent_start":
|
|
152
|
+
void bus.emit("before_agent_start", { prompt: event.prompt });
|
|
153
|
+
return;
|
|
154
|
+
case "agent_start":
|
|
155
|
+
case "turn_start":
|
|
156
|
+
case "turn_end":
|
|
157
|
+
case "agent_before_settle":
|
|
158
|
+
void bus.emit(event.type, {});
|
|
159
|
+
return;
|
|
160
|
+
case "agent_end":
|
|
161
|
+
void bus.emit("agent_end", { stopReason: event.stopReason, willRetry: event.willRetry });
|
|
162
|
+
return;
|
|
163
|
+
case "agent_settled":
|
|
164
|
+
void bus.emit("agent_settled", event.warning === undefined ? {} : { warning: event.warning });
|
|
165
|
+
return;
|
|
166
|
+
case "tool_execution_start":
|
|
167
|
+
void bus.emit("tool_call", {
|
|
168
|
+
toolCallId: event.toolCallId,
|
|
169
|
+
toolName: event.toolName,
|
|
170
|
+
input: event.args,
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
case "tool_execution_end":
|
|
174
|
+
void bus.emit("tool_result", {
|
|
175
|
+
toolCallId: event.toolCallId,
|
|
176
|
+
toolName: event.toolName,
|
|
177
|
+
isError: event.isError,
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
case "permission_request":
|
|
181
|
+
void bus.emit("tool_approval_requested", {
|
|
182
|
+
requestId: event.requestId,
|
|
183
|
+
toolName: event.toolName,
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
case "permission_resolved":
|
|
187
|
+
void bus.emit("tool_approval_resolved", {
|
|
188
|
+
requestId: event.requestId,
|
|
189
|
+
decision: event.decision,
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
case "compaction_end":
|
|
193
|
+
if (event.result !== undefined)
|
|
194
|
+
void bus.emit("session_compact", { tokensBefore: event.result.tokensBefore });
|
|
195
|
+
return;
|
|
196
|
+
case "model_changed":
|
|
197
|
+
void bus.emit("model_select", { model: event.model });
|
|
198
|
+
return;
|
|
199
|
+
case "cache_miss": {
|
|
200
|
+
const { type: _type, ...miss } = event;
|
|
201
|
+
void bus.emit("cache_miss", miss);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
case "context_pressure": {
|
|
205
|
+
const { type: _type, ...pressure } = event;
|
|
206
|
+
void bus.emit("context_pressure", pressure);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
default:
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function buildSession(record, manager, model, thinkingLevel) {
|
|
214
|
+
const { assembly } = record;
|
|
215
|
+
const registry = assembly.tools;
|
|
216
|
+
const all = registry
|
|
217
|
+
.list()
|
|
218
|
+
.map((name) => registry.get(name))
|
|
219
|
+
.filter((tool) => tool !== undefined);
|
|
220
|
+
const active = registry.active().map((tool) => tool.name);
|
|
221
|
+
const config = assembly.config;
|
|
222
|
+
let session;
|
|
223
|
+
const options = {
|
|
224
|
+
sessionManager: manager,
|
|
225
|
+
providers: assembly.providers,
|
|
226
|
+
model,
|
|
227
|
+
thinkingLevel,
|
|
228
|
+
tools: all,
|
|
229
|
+
activeTools: active,
|
|
230
|
+
permission: assembly.permission,
|
|
231
|
+
brokers: [record.chain],
|
|
232
|
+
unattended: assembly.unattended,
|
|
233
|
+
approvalTimeoutMs: approvalTimeout(record.options),
|
|
234
|
+
hooks: assembly.hooks,
|
|
235
|
+
system: systemInput(record, active),
|
|
236
|
+
compaction: { ...config.compaction },
|
|
237
|
+
retry: { ...config.retry },
|
|
238
|
+
expandPrompt: (text) => expandPrompt(record, session, text),
|
|
239
|
+
isContextOverflow: isOverflowErrorText,
|
|
240
|
+
subagents: registry.get("task") === undefined ? false : { maxConcurrent: 4 },
|
|
241
|
+
log: record.log,
|
|
242
|
+
cache: cacheSettingsFrom(config, process.env, (message) => record.log("warn", message)),
|
|
243
|
+
warmingDecider: () => assembly.host.warmingDecider?.(),
|
|
244
|
+
};
|
|
245
|
+
const maxChars = config.tools?.maxToolResultChars;
|
|
246
|
+
if (maxChars !== undefined)
|
|
247
|
+
options.maxToolResultChars = maxChars;
|
|
248
|
+
const hostId = assembly.host.handle?.adapter.id;
|
|
249
|
+
if (hostId !== undefined)
|
|
250
|
+
options.hostId = hostId;
|
|
251
|
+
session = new AgentSessionImpl(options);
|
|
252
|
+
session.subscribe((event) => bridgeEvent(event, assembly.events));
|
|
253
|
+
records.set(session, record);
|
|
254
|
+
record.state.session = session;
|
|
255
|
+
return session;
|
|
256
|
+
}
|
|
257
|
+
/** 第 14 步:组装主会话。 */
|
|
258
|
+
export function composeSession(assembly, state = emptyComposeState(), options = {}) {
|
|
259
|
+
const manager = assembly.sessionManager;
|
|
260
|
+
if (!(manager instanceof SessionManager)) {
|
|
261
|
+
throw new AmaError("invalid_arguments", "组装根只接受 SessionManager 实例(由 sessions.open 创建)");
|
|
262
|
+
}
|
|
263
|
+
const { model } = assembly;
|
|
264
|
+
if (assembly.providers.getApi(model.api) === undefined) {
|
|
265
|
+
const via = model.api === "google-generative-ai" ? ";过渡期请经 openrouter 调用" : "";
|
|
266
|
+
throw new StartupError("provider_not_found", `模型 ${model.provider}/${model.id} 的协议 ${model.api} 尚未实现${via}`, ExitCode.NoModel);
|
|
267
|
+
}
|
|
268
|
+
for (const warning of state.warnings.splice(0))
|
|
269
|
+
assembly.warn(warning);
|
|
270
|
+
const timeoutMs = approvalTimeout(options);
|
|
271
|
+
const chain = new ApprovalBrokerChain({ timeoutMs });
|
|
272
|
+
chain.setHostBroker({
|
|
273
|
+
ask: (request, signal) => assembly.host.broker()?.ask(request, signal) ?? Promise.resolve(undefined),
|
|
274
|
+
});
|
|
275
|
+
chain.setUiBroker({
|
|
276
|
+
ask: (request, signal) => assembly.uiBroker()?.ask(request, signal) ?? Promise.resolve(undefined),
|
|
277
|
+
});
|
|
278
|
+
const record = {
|
|
279
|
+
assembly,
|
|
280
|
+
state,
|
|
281
|
+
options,
|
|
282
|
+
chain,
|
|
283
|
+
hookContext: () => assembly.sessionStartContext(),
|
|
284
|
+
hostTexts: new Map(),
|
|
285
|
+
log: options.log ?? stderrLog(),
|
|
286
|
+
};
|
|
287
|
+
const session = buildSession(record, manager, model, assembly.thinkingLevel);
|
|
288
|
+
record.current = session;
|
|
289
|
+
const registry = assembly.tools;
|
|
290
|
+
if (registry instanceof PresetToolRegistry) {
|
|
291
|
+
// 宿主在会话创建之后注册的工具:追加进当前会话,下次请求以 system 补丁声明(只在末尾追加)。
|
|
292
|
+
registry.onRegister((tool) => {
|
|
293
|
+
const active = registry.active().some((t) => t.name === tool.name);
|
|
294
|
+
try {
|
|
295
|
+
record.current.addTool(tool, active);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
record.log("warn", `追加工具 ${tool.name} 失败:${error.message}`);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return session;
|
|
303
|
+
}
|
|
304
|
+
function recordOf(target) {
|
|
305
|
+
const key = "sessionManager" in target && "paths" in target ? target.session : target;
|
|
306
|
+
return records.get(key);
|
|
307
|
+
}
|
|
308
|
+
/** 模式层当前应操作的会话(`switchSession` 之后不再是 `Runtime.session`)。 */
|
|
309
|
+
export function currentSession(target) {
|
|
310
|
+
const record = recordOf(target);
|
|
311
|
+
if (record !== undefined)
|
|
312
|
+
return record.current;
|
|
313
|
+
return "paths" in target && "sessionManager" in target ? target.session : target;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* `/new`、`/resume`、`/fork`、RPC `new_session / switch_session / fork`:旧会话发 SessionEnd 后
|
|
317
|
+
* dispose,新会话复用装配材料,宿主总线发 session_start,再跑 SessionStart Hook。
|
|
318
|
+
*/
|
|
319
|
+
export async function switchSession(target, request) {
|
|
320
|
+
const record = recordOf(target);
|
|
321
|
+
if (record === undefined)
|
|
322
|
+
throw new AmaError("not_implemented", "该会话不是由组装根创建的,不能切换");
|
|
323
|
+
const old = record.current;
|
|
324
|
+
if (old.state.isStreaming)
|
|
325
|
+
throw new AmaError("busy", "运行中不能切换会话");
|
|
326
|
+
const { assembly } = record;
|
|
327
|
+
const sessionDir = assembly.paths.sessionDir;
|
|
328
|
+
let manager;
|
|
329
|
+
let source;
|
|
330
|
+
switch (request.kind) {
|
|
331
|
+
case "new":
|
|
332
|
+
manager = SessionManager.create(sessionDirForCwd(sessionDir, old.cwd), old.cwd);
|
|
333
|
+
source = "new";
|
|
334
|
+
break;
|
|
335
|
+
case "open":
|
|
336
|
+
manager = SessionManager.open(request.file);
|
|
337
|
+
source = "resume";
|
|
338
|
+
break;
|
|
339
|
+
case "resume":
|
|
340
|
+
manager = openSession({ kind: "resume", id: request.id }, { sessionDir, cwd: old.cwd });
|
|
341
|
+
source = "resume";
|
|
342
|
+
break;
|
|
343
|
+
case "fork":
|
|
344
|
+
manager = old.manager.fork(request.entryId);
|
|
345
|
+
source = "fork";
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
await assembly.hooks
|
|
349
|
+
.run("SessionEnd", { reason: request.kind === "new" ? "new" : "switch" })
|
|
350
|
+
.catch(() => undefined);
|
|
351
|
+
await old.dispose();
|
|
352
|
+
if (assembly.permission instanceof PermissionPipeline)
|
|
353
|
+
assembly.permission.clearSessionGrants();
|
|
354
|
+
let hookContext;
|
|
355
|
+
record.hookContext = () => hookContext;
|
|
356
|
+
const next = buildSession(record, manager, old.model(), old.thinkingLevel());
|
|
357
|
+
record.current = next;
|
|
358
|
+
assembly.onSessionReplaced(next);
|
|
359
|
+
const file = next.state.sessionFile;
|
|
360
|
+
await assembly.events.emit("session_start", {
|
|
361
|
+
sessionId: next.state.sessionId,
|
|
362
|
+
...(file !== undefined ? { sessionFile: file } : {}),
|
|
363
|
+
cwd: next.cwd,
|
|
364
|
+
reason: source,
|
|
365
|
+
});
|
|
366
|
+
const outcome = await assembly.hooks.run("SessionStart", { source }).catch(() => undefined);
|
|
367
|
+
hookContext = outcome?.additionalContext;
|
|
368
|
+
// 会话自身的 session_start 由模式在重新订阅之后调 `announceStart` 发(否则订阅者收不到)。
|
|
369
|
+
return next;
|
|
370
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 组装根:会话存储(实施计划 §1.1 `sessions.*`)。[B6]
|
|
3
|
+
*
|
|
4
|
+
* 会话根目录 `sessionDir` 下按 cwd 分子目录(`sessionDirForCwd`)。
|
|
5
|
+
* - `new` → 延迟落盘新建;`continue` → 本目录最近一条(没有则新建);
|
|
6
|
+
* - `resume{id}` / `fork{id}`:先在本目录、再在全部子目录里按 id 或唯一前缀找文件;找不到 →
|
|
7
|
+
* `session_not_found`,前缀不唯一 → `invalid_arguments` 并列出候选;
|
|
8
|
+
* - `session-id{id}`:找到则打开,否则以该 id 新建;
|
|
9
|
+
* - `fork{id}`:打开后从叶子复制出新文件,原会话立即关闭(释放锁)。
|
|
10
|
+
*/
|
|
11
|
+
import { SessionManager } from "../session/manager.js";
|
|
12
|
+
import type { SessionListItem } from "../session/types.js";
|
|
13
|
+
import type { RuntimeDeps, SessionRequest } from "./deps.js";
|
|
14
|
+
/** 按 id / 唯一前缀找会话文件;本 cwd 目录优先。 */
|
|
15
|
+
export declare function findSessionFile(root: string, id: string, cwd?: string): string | undefined;
|
|
16
|
+
export declare function openSession(request: SessionRequest, context: {
|
|
17
|
+
sessionDir: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
}): SessionManager;
|
|
20
|
+
export declare function listSessions(context: {
|
|
21
|
+
sessionDir: string;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
}): SessionListItem[];
|
|
24
|
+
export declare function createSessionStore(): RuntimeDeps["sessions"];
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 组装根:会话存储(实施计划 §1.1 `sessions.*`)。[B6]
|
|
3
|
+
*
|
|
4
|
+
* 会话根目录 `sessionDir` 下按 cwd 分子目录(`sessionDirForCwd`)。
|
|
5
|
+
* - `new` → 延迟落盘新建;`continue` → 本目录最近一条(没有则新建);
|
|
6
|
+
* - `resume{id}` / `fork{id}`:先在本目录、再在全部子目录里按 id 或唯一前缀找文件;找不到 →
|
|
7
|
+
* `session_not_found`,前缀不唯一 → `invalid_arguments` 并列出候选;
|
|
8
|
+
* - `session-id{id}`:找到则打开,否则以该 id 新建;
|
|
9
|
+
* - `fork{id}`:打开后从叶子复制出新文件,原会话立即关闭(释放锁)。
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { AmaError } from "../errors.js";
|
|
14
|
+
import { SessionManager } from "../session/manager.js";
|
|
15
|
+
import { migrateSessionLines } from "../session/migrate.js";
|
|
16
|
+
import { TRASH_DIR_NAME, listSessionFiles, purgeTrash, readSessionLines, sessionDirForCwd, sessionIdFromFileName, trashSession, } from "../session/store.js";
|
|
17
|
+
/** 根目录下全部 cwd 子目录(不含 trash)。 */
|
|
18
|
+
function cwdDirs(root) {
|
|
19
|
+
if (!existsSync(root))
|
|
20
|
+
return [];
|
|
21
|
+
return readdirSync(root)
|
|
22
|
+
.filter((name) => name !== TRASH_DIR_NAME)
|
|
23
|
+
.map((name) => join(root, name))
|
|
24
|
+
.filter((dir) => {
|
|
25
|
+
try {
|
|
26
|
+
return statSync(dir).isDirectory();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** 按 id / 唯一前缀找会话文件;本 cwd 目录优先。 */
|
|
34
|
+
export function findSessionFile(root, id, cwd) {
|
|
35
|
+
const dirs = cwd === undefined ? cwdDirs(root) : [sessionDirForCwd(root, cwd), ...cwdDirs(root)];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const prefix = [];
|
|
38
|
+
for (const dir of dirs) {
|
|
39
|
+
for (const file of listSessionFiles(dir)) {
|
|
40
|
+
if (seen.has(file))
|
|
41
|
+
continue;
|
|
42
|
+
seen.add(file);
|
|
43
|
+
const fileId = sessionIdFromFileName(file);
|
|
44
|
+
if (fileId === id)
|
|
45
|
+
return file;
|
|
46
|
+
if (fileId?.startsWith(id) === true)
|
|
47
|
+
prefix.push(file);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (prefix.length > 1) {
|
|
51
|
+
const ids = prefix.map((f) => sessionIdFromFileName(f) ?? f).slice(0, 10);
|
|
52
|
+
throw new AmaError("invalid_arguments", `会话 id 前缀 ${id} 不唯一,候选:${ids.join(", ")}`, {
|
|
53
|
+
exitCode: 5,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return prefix[0];
|
|
57
|
+
}
|
|
58
|
+
function requireFile(root, id, cwd) {
|
|
59
|
+
const file = findSessionFile(root, id, cwd);
|
|
60
|
+
if (file === undefined)
|
|
61
|
+
throw new AmaError("session_not_found", `会话不存在:${id}`);
|
|
62
|
+
return file;
|
|
63
|
+
}
|
|
64
|
+
export function openSession(request, context) {
|
|
65
|
+
const { sessionDir: root, cwd } = context;
|
|
66
|
+
const dir = sessionDirForCwd(root, cwd);
|
|
67
|
+
switch (request.kind) {
|
|
68
|
+
case "new":
|
|
69
|
+
return SessionManager.create(dir, cwd);
|
|
70
|
+
case "continue":
|
|
71
|
+
return SessionManager.continueRecent(dir, cwd);
|
|
72
|
+
case "resume":
|
|
73
|
+
return SessionManager.open(requireFile(root, request.id, cwd));
|
|
74
|
+
case "session-id": {
|
|
75
|
+
const file = findSessionFile(root, request.id, cwd);
|
|
76
|
+
return file === undefined
|
|
77
|
+
? SessionManager.create(dir, cwd, { id: request.id })
|
|
78
|
+
: SessionManager.open(file);
|
|
79
|
+
}
|
|
80
|
+
case "fork": {
|
|
81
|
+
const source = SessionManager.open(requireFile(root, request.id, cwd));
|
|
82
|
+
try {
|
|
83
|
+
const leaf = source.leafId();
|
|
84
|
+
if (leaf === null)
|
|
85
|
+
throw new AmaError("session_corrupt", `会话 ${request.id} 没有条目`);
|
|
86
|
+
return source.fork(leaf);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
source.close();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function listSessions(context) {
|
|
95
|
+
const dirs = context.cwd === undefined
|
|
96
|
+
? cwdDirs(context.sessionDir)
|
|
97
|
+
: [sessionDirForCwd(context.sessionDir, context.cwd)];
|
|
98
|
+
return dirs
|
|
99
|
+
.flatMap((dir) => SessionManager.list(dir))
|
|
100
|
+
.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
|
|
101
|
+
}
|
|
102
|
+
export function createSessionStore() {
|
|
103
|
+
return {
|
|
104
|
+
open: (request, context) => openSession(request, context),
|
|
105
|
+
list: async (context) => listSessions(context),
|
|
106
|
+
show: async (id, context) => {
|
|
107
|
+
const file = requireFile(context.sessionDir, id);
|
|
108
|
+
const item = SessionManager.list(join(file, "..")).find((i) => i.file === file);
|
|
109
|
+
if (item === undefined)
|
|
110
|
+
throw new AmaError("session_corrupt", `会话文件无法读取:${file}`);
|
|
111
|
+
const { entries } = migrateSessionLines(readSessionLines(file).lines, file);
|
|
112
|
+
return { item, entries };
|
|
113
|
+
},
|
|
114
|
+
prune: async (context) => {
|
|
115
|
+
const cutoff = Date.now() - context.olderThanDays * 24 * 60 * 60 * 1000;
|
|
116
|
+
const old = listSessions(context).filter((i) => Date.parse(i.modifiedAt) < cutoff);
|
|
117
|
+
if (context.dryRun)
|
|
118
|
+
return { moved: old.map((i) => i.file) };
|
|
119
|
+
for (const item of old)
|
|
120
|
+
trashSession(item.file, context.sessionDir);
|
|
121
|
+
purgeTrash(context.sessionDir);
|
|
122
|
+
return { moved: old.map((i) => i.file) };
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 组装根(composition root,实施计划 §1):`createRuntimeDeps()` 把各批次的实现装成 bootstrap 要的
|
|
3
|
+
* `RuntimeDeps`。CLI(main.ts 动态 import)与 SDK(sdk.ts)共用。[B6]
|
|
4
|
+
*
|
|
5
|
+
* | 字段 | 实现 |
|
|
6
|
+
* | -------------------- | -------------------------------------------------------------------- |
|
|
7
|
+
* | providers.create | compose-providers.ts(ProviderRegistry + auth + 零配置本地探测) |
|
|
8
|
+
* | sessions.* | compose-store.ts(SessionManager 五种请求、list / show / prune) |
|
|
9
|
+
* | tools.create | PresetToolRegistry:内置 + 工具工厂 + extraTools,按预设定活动集 |
|
|
10
|
+
* | permissions.create | 内置 deny(按 builtinDeny 过滤)+ 逐条 parseRule → PermissionPipeline |
|
|
11
|
+
* | resources.discover | skills / 提示模板发现,完整对象存进闭包状态 |
|
|
12
|
+
* | session.create | compose-session.ts |
|
|
13
|
+
* | modes | line / print / rpc 懒加载;interactive 在 B7 之前回落到 line |
|
|
14
|
+
* | ui | 传了 io 时:终端 → 迷你 TUI(懒加载),否则文本问答;stdin 非 TTY 不问 |
|
|
15
|
+
*
|
|
16
|
+
* **跨步骤状态走闭包(表 B·B13,有意保留)**:`RuntimeDeps.tools.create` 的入参没有 skills / 提示模板,
|
|
17
|
+
* 第 13 步 `resources.discover` 把完整对象存进 `ComposeState`,组装会话时(compose-session.ts 的技能
|
|
18
|
+
* 索引与 `/skill:` 展开)再取。不为此扩契约:两步都在同一个 `createRuntimeDeps()` 闭包里,顺序由
|
|
19
|
+
* bootstrap 固定。
|
|
20
|
+
*
|
|
21
|
+
* **工具注册点(给 B10 等)**:`ComposeOptions.toolFactories` 与 `DEFAULT_TOOL_FACTORIES`。工厂在
|
|
22
|
+
* 第 12 步建注册表时调用,产物以 `builtin` 来源注册(受预设管理);工厂拿不到会话对象本身,执行期
|
|
23
|
+
* 需要会话时用 `ctx.session()`,嵌套调用其它工具用 `ToolContext.tools.executeTool`(走完整门禁)。
|
|
24
|
+
*/
|
|
25
|
+
import type { ApiRegistry } from "../ai/apis/api.js";
|
|
26
|
+
import type { ModelRef, ProviderData } from "../ai/types.js";
|
|
27
|
+
import type { AgentSession } from "../agent/types.js";
|
|
28
|
+
import type { AmaConfig } from "../config/types.js";
|
|
29
|
+
import type { Rule } from "../permissions/types.js";
|
|
30
|
+
import { PresetToolRegistry } from "../tools/presets.js";
|
|
31
|
+
import type { ToolDefinition, ToolRegistryApi } from "../tools/types.js";
|
|
32
|
+
import { type ComposeState, type LogFn } from "./compose-session.js";
|
|
33
|
+
import type { CliIo, InteractiveUi, ModeRunner, RuntimeDeps } from "./deps.js";
|
|
34
|
+
import type { RuntimeMode } from "./runtime.js";
|
|
35
|
+
import type { StartupUiOptions } from "../modes/interactive/startup-ui.js";
|
|
36
|
+
import type { TextUiIo } from "../modes/startup-ui-text.js";
|
|
37
|
+
export interface ToolFactoryContext {
|
|
38
|
+
config: AmaConfig;
|
|
39
|
+
cwd: string;
|
|
40
|
+
mode: RuntimeMode;
|
|
41
|
+
registry: ToolRegistryApi;
|
|
42
|
+
/** 会话组装之后才有值;工具执行期取用。 */
|
|
43
|
+
session(): AgentSession | undefined;
|
|
44
|
+
/** 组装期 warning(组装会话时交给 assembly.warn)。 */
|
|
45
|
+
warn(message: string): void;
|
|
46
|
+
}
|
|
47
|
+
/** 返回 undefined = 本次不注册(例如配置关闭)。 */
|
|
48
|
+
export type ToolFactory = (ctx: ToolFactoryContext) => ToolDefinition | undefined;
|
|
49
|
+
/** 缺省工具工厂:codemode(`codemode.mode` 生效值为 off 时不注册)。 */
|
|
50
|
+
export declare const DEFAULT_TOOL_FACTORIES: readonly ToolFactory[];
|
|
51
|
+
export interface ComposeOptions {
|
|
52
|
+
/** 追加 / 覆盖供应商(SDK)。 */
|
|
53
|
+
providers?: ProviderData[];
|
|
54
|
+
apis?: ApiRegistry;
|
|
55
|
+
/** SDK 追加的工具(来源 sdk,总在活动集里)。 */
|
|
56
|
+
extraTools?: ToolDefinition[];
|
|
57
|
+
/** 缺省 DEFAULT_TOOL_FACTORIES。 */
|
|
58
|
+
toolFactories?: readonly ToolFactory[];
|
|
59
|
+
/** 覆盖启动期问答;缺省见 `defaultStartupUi`。 */
|
|
60
|
+
ui?: InteractiveUi;
|
|
61
|
+
modes?: Partial<Record<RuntimeMode, ModeRunner>>;
|
|
62
|
+
/** key 发现用的环境(缺省 process.env)。 */
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
approvalTimeoutMs?: number;
|
|
65
|
+
/** 零配置时探测本地 ollama / lmstudio(缺省 true,`AMA_NO_LOCAL_PROBE=1` 关闭)。 */
|
|
66
|
+
probeLocal?: boolean;
|
|
67
|
+
includeFake?: boolean;
|
|
68
|
+
log?: LogFn;
|
|
69
|
+
}
|
|
70
|
+
export declare function lazyMode(load: () => Promise<ModeRunner>): ModeRunner;
|
|
71
|
+
/** 缺省模式(懒加载:子命令与 --version 不加载模式实现)。 */
|
|
72
|
+
export declare const DEFAULT_MODES: Readonly<Partial<Record<RuntimeMode, ModeRunner>>>;
|
|
73
|
+
/**
|
|
74
|
+
* 第 12 步:内置工具 + 工厂 + extraTools,按预设定活动集;warning 留给组装会话时报告。
|
|
75
|
+
*
|
|
76
|
+
* 先跑工厂(产物暂不注册)再解析预设:codemode 是否可用决定预设与模式;`on` 模式下注册时给
|
|
77
|
+
* 其它工具的描述追加 codemode 提示,`only` 模式活动集独占(codemode/modes.ts)。工厂拿到的
|
|
78
|
+
* `registry` 在执行期才读,此时已登记完全部工具。
|
|
79
|
+
*/
|
|
80
|
+
export declare function createTools(input: {
|
|
81
|
+
config: AmaConfig;
|
|
82
|
+
cwd: string;
|
|
83
|
+
mode: RuntimeMode;
|
|
84
|
+
}, options: ComposeOptions, state: ComposeState): PresetToolRegistry;
|
|
85
|
+
/** 当前模型是否收图片(read 工具用);查不到模型时按收(交给协议层过滤)。 */
|
|
86
|
+
export declare function modelAcceptsImages(state: ComposeState, model: ModelRef | undefined): boolean;
|
|
87
|
+
/** 第 14 步前:内置 deny(builtinDeny 过滤)+ 用户规则;非法规则 warning 后跳过。 */
|
|
88
|
+
export declare function buildRules(specs: readonly {
|
|
89
|
+
effect: "allow" | "deny";
|
|
90
|
+
raw: string;
|
|
91
|
+
source: Rule["source"];
|
|
92
|
+
}[], builtinDeny: boolean | readonly string[] | undefined, warn: (message: string) => void): Rule[];
|
|
93
|
+
type UiIo = Pick<CliIo, "stdinIsTTY" | "stdoutIsTTY" | "env">;
|
|
94
|
+
/**
|
|
95
|
+
* CLI 缺省的启动期问答(第 7 / 8 / 11 步,只在 interactive / line 模式被调用):
|
|
96
|
+
* stdin 非 TTY 时不问(管道里的输入留给行式界面,保持非交互分支);stdout 也是终端、
|
|
97
|
+
* `TERM` 不是 dumb 且没有 `--no-tui` 时用迷你 TUI,否则写 stderr、从 stdin 读一行。
|
|
98
|
+
*/
|
|
99
|
+
export declare function defaultStartupUi(io: UiIo, options?: {
|
|
100
|
+
noTui?: boolean | undefined;
|
|
101
|
+
tui?: StartupUiOptions;
|
|
102
|
+
text?: TextUiIo;
|
|
103
|
+
}): InteractiveUi | undefined;
|
|
104
|
+
export declare function createRuntimeDeps(options?: ComposeOptions & {
|
|
105
|
+
io?: UiIo;
|
|
106
|
+
noTui?: boolean | undefined;
|
|
107
|
+
}): RuntimeDeps;
|
|
108
|
+
export {};
|