@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,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentSessionImpl(设计 §4.2–§4.3、§9、§11.1 第 16 步、§13.1)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 会话 = SessionManager(JSONL 树,事实来源)+ Agent(上下文消息、队列、run)+ 压缩 / 重试 / Hook 调度。
|
|
5
|
+
* - 每条 message_end 落盘为条目;Agent 的上下文 = 活动分支的投影(压缩 / context_edit 后重建)。
|
|
6
|
+
* - 首次模型请求前把系统提示 + 工具表作为首条 `system` 消息落盘并 `flush()`(此时才创建文件);
|
|
7
|
+
* 之后节或工具表变化落 system 补丁。
|
|
8
|
+
* - `prompt()`:运行中且无 streamingBehavior → `AmaError{code:"busy"}`;否则展开(expandPrompt)→
|
|
9
|
+
* UserPromptSubmit Hook → 阈值压缩检查 → 会话周期(session-run.ts);周期结束后 resolve。
|
|
10
|
+
* - `steer()` / `followUp()`:运行中入队(返回 "queued");空闲时直接以该消息开始一个周期("handled")。
|
|
11
|
+
* - `abort()`:中断供应商流、工具、重试等待与压缩;回到 idle 后 resolve;不清队列。
|
|
12
|
+
*/
|
|
13
|
+
import { modelRefOf } from "../ai/providers/channels.js";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { AmaError } from "../errors.js";
|
|
16
|
+
import { buildProjection } from "../session/projection.js";
|
|
17
|
+
import { Agent } from "./agent.js";
|
|
18
|
+
import { queuedText } from "./queue.js";
|
|
19
|
+
import { resolveRetrySettings } from "./retry.js";
|
|
20
|
+
import { SessionCacheController, resolveCacheSettings } from "./session-cache.js";
|
|
21
|
+
import { CompactionController } from "./session-compaction.js";
|
|
22
|
+
import { makeUserMessage, runPrompt } from "./session-run.js";
|
|
23
|
+
import { buildSessionState, computeStats, lastAssistantText } from "./session-state.js";
|
|
24
|
+
import { DEFAULT_SUBAGENT_CONCURRENCY, SubagentPool, runSubagent } from "./session-subagent.js";
|
|
25
|
+
import { persistMessage, runHookWithEvents, syncSystemMessage } from "./session-sync.js";
|
|
26
|
+
import { createToolRunnerOptions } from "./session-tools.js";
|
|
27
|
+
import { convertToLlm } from "./transform.js";
|
|
28
|
+
/** `"user"` = 普通用户输入,不写 origin。 */
|
|
29
|
+
function normalizeOrigin(origin) {
|
|
30
|
+
return origin === undefined || origin === "user" ? undefined : origin;
|
|
31
|
+
}
|
|
32
|
+
export class AgentSessionImpl {
|
|
33
|
+
options;
|
|
34
|
+
manager;
|
|
35
|
+
agent;
|
|
36
|
+
cwd;
|
|
37
|
+
depth;
|
|
38
|
+
readFiles = new Set();
|
|
39
|
+
stream;
|
|
40
|
+
cache;
|
|
41
|
+
currentModel;
|
|
42
|
+
currentThinking;
|
|
43
|
+
allTools = new Map();
|
|
44
|
+
activeNames;
|
|
45
|
+
listeners = new Set();
|
|
46
|
+
compaction;
|
|
47
|
+
retrySettings;
|
|
48
|
+
cycle;
|
|
49
|
+
retrying = false;
|
|
50
|
+
stopReason;
|
|
51
|
+
stopFlag = false;
|
|
52
|
+
turns = 0;
|
|
53
|
+
systemInput;
|
|
54
|
+
subagentPool;
|
|
55
|
+
disposed = false;
|
|
56
|
+
/** 已入队、尚未投递的消息(投递时发 queue_update)。 */
|
|
57
|
+
queuedMessages = new WeakSet();
|
|
58
|
+
constructor(options) {
|
|
59
|
+
this.options = options;
|
|
60
|
+
this.manager = options.sessionManager;
|
|
61
|
+
this.cwd = this.manager.cwd;
|
|
62
|
+
this.depth = options.depth ?? 0;
|
|
63
|
+
this.currentModel = options.model;
|
|
64
|
+
this.currentThinking = options.thinkingLevel ?? "off";
|
|
65
|
+
for (const tool of options.tools ?? [])
|
|
66
|
+
this.allTools.set(tool.name, tool);
|
|
67
|
+
this.activeNames = [...(options.activeTools ?? this.allTools.keys())].filter((name) => this.allTools.has(name));
|
|
68
|
+
this.systemInput = { ...(options.system ?? {}) };
|
|
69
|
+
this.retrySettings = resolveRetrySettings(options.retry);
|
|
70
|
+
this.cache = new SessionCacheController(this, resolveCacheSettings(options.cache), {
|
|
71
|
+
decider: () => options.warmingDecider?.(),
|
|
72
|
+
});
|
|
73
|
+
this.stream = this.cache.wrapStream((model, context, streamOptions) => {
|
|
74
|
+
const api = options.providers.getApi(model.api);
|
|
75
|
+
if (api === undefined) {
|
|
76
|
+
throw new AmaError("provider_not_found", `no implementation registered for api ${model.api}`);
|
|
77
|
+
}
|
|
78
|
+
return api.stream(model, context, streamOptions);
|
|
79
|
+
});
|
|
80
|
+
const runner = createToolRunnerOptions(this);
|
|
81
|
+
const agentOptions = {
|
|
82
|
+
hooks: {
|
|
83
|
+
convertToLlm: (messages) => convertToLlm(messages, {
|
|
84
|
+
provider: this.currentModel.provider,
|
|
85
|
+
model: this.currentModel.id,
|
|
86
|
+
}),
|
|
87
|
+
beforeToolCall: runner.beforeToolCall,
|
|
88
|
+
prepareNextTurn: async () => {
|
|
89
|
+
const signal = this.agent.signal ?? this.cycle?.controller.signal;
|
|
90
|
+
if (signal !== undefined)
|
|
91
|
+
await this.compaction.checkThreshold(signal);
|
|
92
|
+
},
|
|
93
|
+
finishTurn: async () => {
|
|
94
|
+
this.turns++;
|
|
95
|
+
if (this.stopFlag)
|
|
96
|
+
return "end";
|
|
97
|
+
const maxTurns = options.maxTurns;
|
|
98
|
+
return maxTurns !== undefined && this.turns >= maxTurns ? "end" : "continue";
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
stream: this.stream,
|
|
102
|
+
getModel: () => this.currentModel,
|
|
103
|
+
getThinkingLevel: () => this.currentThinking,
|
|
104
|
+
streamOptions: async () => {
|
|
105
|
+
const apiKey = await this.resolveApiKey();
|
|
106
|
+
return apiKey === undefined
|
|
107
|
+
? { sessionId: this.manager.id }
|
|
108
|
+
: { apiKey, sessionId: this.manager.id };
|
|
109
|
+
},
|
|
110
|
+
beforeRequest: async () => {
|
|
111
|
+
this.syncSystem();
|
|
112
|
+
this.manager.flush();
|
|
113
|
+
},
|
|
114
|
+
tools: runner,
|
|
115
|
+
messages: buildProjection(this.manager.branch()).messages,
|
|
116
|
+
onListenerError: (error, event) => this.log("warn", `listener failed on ${event.type}: ${String(error)}`),
|
|
117
|
+
};
|
|
118
|
+
if (options.steeringMode !== undefined)
|
|
119
|
+
agentOptions.steeringMode = options.steeringMode;
|
|
120
|
+
if (options.followUpMode !== undefined)
|
|
121
|
+
agentOptions.followUpMode = options.followUpMode;
|
|
122
|
+
this.agent = new Agent(agentOptions);
|
|
123
|
+
this.agent.subscribe((event) => this.onAgentEvent(event));
|
|
124
|
+
this.compaction = new CompactionController(this, options.compaction);
|
|
125
|
+
const subagents = options.subagents === false ? undefined : options.subagents;
|
|
126
|
+
this.subagentPool = new SubagentPool(subagents?.maxConcurrent ?? DEFAULT_SUBAGENT_CONCURRENCY);
|
|
127
|
+
}
|
|
128
|
+
// -------------------------------------------------------------------------
|
|
129
|
+
// SessionCore
|
|
130
|
+
// -------------------------------------------------------------------------
|
|
131
|
+
model() {
|
|
132
|
+
return this.currentModel;
|
|
133
|
+
}
|
|
134
|
+
thinkingLevel() {
|
|
135
|
+
return this.currentThinking;
|
|
136
|
+
}
|
|
137
|
+
outputDir() {
|
|
138
|
+
if (this.options.outputDir !== undefined)
|
|
139
|
+
return this.options.outputDir;
|
|
140
|
+
const dir = this.manager.directory();
|
|
141
|
+
return dir === undefined ? undefined : join(dir, "outputs");
|
|
142
|
+
}
|
|
143
|
+
async resolveApiKey() {
|
|
144
|
+
try {
|
|
145
|
+
const { provider, channel } = this.currentModel;
|
|
146
|
+
return (await this.options.providers.resolveApiKey(provider, channel)).apiKey;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
emit(event) {
|
|
153
|
+
this.cache.onEvent(event);
|
|
154
|
+
for (const listener of this.listeners) {
|
|
155
|
+
try {
|
|
156
|
+
listener(event);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.log("warn", `session listener failed on ${event.type}: ${String(error)}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
appendEntry(input) {
|
|
164
|
+
const entry = this.manager.append(input);
|
|
165
|
+
this.emit({ type: "entry_appended", entry });
|
|
166
|
+
return entry;
|
|
167
|
+
}
|
|
168
|
+
reloadMessages() {
|
|
169
|
+
this.agent.messages = buildProjection(this.manager.branch()).messages;
|
|
170
|
+
}
|
|
171
|
+
activeTool(name) {
|
|
172
|
+
return this.activeNames.includes(name) ? this.allTools.get(name) : undefined;
|
|
173
|
+
}
|
|
174
|
+
tool(name) {
|
|
175
|
+
return this.allTools.get(name);
|
|
176
|
+
}
|
|
177
|
+
activeToolNames() {
|
|
178
|
+
return [...this.activeNames];
|
|
179
|
+
}
|
|
180
|
+
runHook(event, payload, signal) {
|
|
181
|
+
return runHookWithEvents(this, event, payload, signal);
|
|
182
|
+
}
|
|
183
|
+
requestStop(reason) {
|
|
184
|
+
this.stopFlag = true;
|
|
185
|
+
this.stopReason = reason;
|
|
186
|
+
}
|
|
187
|
+
log(level, message) {
|
|
188
|
+
this.options.log?.(level, message);
|
|
189
|
+
}
|
|
190
|
+
// -------------------------------------------------------------------------
|
|
191
|
+
// 事件与落盘
|
|
192
|
+
// -------------------------------------------------------------------------
|
|
193
|
+
onAgentEvent(event) {
|
|
194
|
+
if (event.type === "message_end")
|
|
195
|
+
persistMessage(this, event.message);
|
|
196
|
+
else if (event.type === "agent_start")
|
|
197
|
+
this.compaction.breaker.startRun();
|
|
198
|
+
this.emit(event);
|
|
199
|
+
if (event.type === "message_start" &&
|
|
200
|
+
event.message.role === "user" &&
|
|
201
|
+
this.queuedMessages.delete(event.message)) {
|
|
202
|
+
this.emitQueue();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
emitQueue() {
|
|
206
|
+
this.emit({
|
|
207
|
+
type: "queue_update",
|
|
208
|
+
steering: this.agent.steeringQueue.snapshot().map(queuedText),
|
|
209
|
+
followUp: this.agent.followUpQueue.snapshot().map(queuedText),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
syncSystem() {
|
|
213
|
+
syncSystemMessage(this, this.systemInput, this.getTools());
|
|
214
|
+
}
|
|
215
|
+
// -------------------------------------------------------------------------
|
|
216
|
+
// 周期
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
assertUsable() {
|
|
219
|
+
if (this.disposed)
|
|
220
|
+
throw new AmaError("session_closed", "session is disposed");
|
|
221
|
+
}
|
|
222
|
+
startCycle(work) {
|
|
223
|
+
const controller = new AbortController();
|
|
224
|
+
let done = () => { };
|
|
225
|
+
const promise = new Promise((resolve) => {
|
|
226
|
+
done = resolve;
|
|
227
|
+
});
|
|
228
|
+
this.cycle = { controller, promise };
|
|
229
|
+
this.turns = 0;
|
|
230
|
+
this.stopFlag = false;
|
|
231
|
+
this.stopReason = undefined;
|
|
232
|
+
return (async () => {
|
|
233
|
+
try {
|
|
234
|
+
return await work(controller.signal);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
this.cycle = undefined;
|
|
238
|
+
this.retrying = false;
|
|
239
|
+
done();
|
|
240
|
+
}
|
|
241
|
+
})();
|
|
242
|
+
}
|
|
243
|
+
runDeps() {
|
|
244
|
+
return {
|
|
245
|
+
core: this,
|
|
246
|
+
compaction: this.compaction,
|
|
247
|
+
retry: () => this.retrySettings,
|
|
248
|
+
syncSystem: () => this.syncSystem(),
|
|
249
|
+
stopRequested: () => this.stopFlag,
|
|
250
|
+
setRetrying: (value) => {
|
|
251
|
+
this.retrying = value;
|
|
252
|
+
},
|
|
253
|
+
lastAssistantText: () => this.getLastAssistantText(),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// -------------------------------------------------------------------------
|
|
257
|
+
// AgentSession
|
|
258
|
+
// -------------------------------------------------------------------------
|
|
259
|
+
async prompt(text, options = {}) {
|
|
260
|
+
this.assertUsable();
|
|
261
|
+
if (this.cycle !== undefined) {
|
|
262
|
+
const behavior = options.streamingBehavior;
|
|
263
|
+
if (behavior === undefined) {
|
|
264
|
+
throw new AmaError("busy", "a run is in progress; pass streamingBehavior or use steer()/followUp()");
|
|
265
|
+
}
|
|
266
|
+
this.enqueue(makeUserMessage(text, options.images, normalizeOrigin(options.origin ?? behavior)), behavior);
|
|
267
|
+
return "queued";
|
|
268
|
+
}
|
|
269
|
+
const origin = normalizeOrigin(options.origin);
|
|
270
|
+
return this.startCycle((signal) => runPrompt(this.runDeps(), text, options.images, origin, signal));
|
|
271
|
+
}
|
|
272
|
+
enqueue(message, queue) {
|
|
273
|
+
this.queuedMessages.add(message);
|
|
274
|
+
if (queue === "steer")
|
|
275
|
+
this.agent.steer(message);
|
|
276
|
+
else
|
|
277
|
+
this.agent.followUp(message);
|
|
278
|
+
this.emitQueue();
|
|
279
|
+
}
|
|
280
|
+
async enqueueOrRun(text, queue, options) {
|
|
281
|
+
this.assertUsable();
|
|
282
|
+
const origin = normalizeOrigin(options.origin ?? queue);
|
|
283
|
+
if (this.cycle === undefined) {
|
|
284
|
+
await this.startCycle((signal) => runPrompt(this.runDeps(), text, undefined, origin, signal));
|
|
285
|
+
return "handled";
|
|
286
|
+
}
|
|
287
|
+
this.enqueue(makeUserMessage(text, undefined, origin), queue);
|
|
288
|
+
return "queued";
|
|
289
|
+
}
|
|
290
|
+
steer(text, options = {}) {
|
|
291
|
+
return this.enqueueOrRun(text, "steer", options);
|
|
292
|
+
}
|
|
293
|
+
followUp(text, options = {}) {
|
|
294
|
+
return this.enqueueOrRun(text, "followUp", options);
|
|
295
|
+
}
|
|
296
|
+
async abort() {
|
|
297
|
+
const cycle = this.cycle;
|
|
298
|
+
if (cycle === undefined)
|
|
299
|
+
return;
|
|
300
|
+
cycle.controller.abort();
|
|
301
|
+
this.agent.abort();
|
|
302
|
+
await cycle.promise;
|
|
303
|
+
}
|
|
304
|
+
waitForIdle() {
|
|
305
|
+
return this.cycle?.promise ?? Promise.resolve();
|
|
306
|
+
}
|
|
307
|
+
clearQueue() {
|
|
308
|
+
const cleared = {
|
|
309
|
+
steering: this.agent.steeringQueue.clear().map(queuedText),
|
|
310
|
+
followUp: this.agent.followUpQueue.clear().map(queuedText),
|
|
311
|
+
};
|
|
312
|
+
this.emitQueue();
|
|
313
|
+
return cleared;
|
|
314
|
+
}
|
|
315
|
+
subscribe(listener) {
|
|
316
|
+
this.listeners.add(listener);
|
|
317
|
+
return () => this.listeners.delete(listener);
|
|
318
|
+
}
|
|
319
|
+
/** 手动压缩;运行中 → busy。 */
|
|
320
|
+
compact(instructions) {
|
|
321
|
+
this.assertUsable();
|
|
322
|
+
if (this.cycle !== undefined)
|
|
323
|
+
return Promise.reject(new AmaError("busy", "cannot compact while running"));
|
|
324
|
+
return this.startCycle((signal) => this.compaction.compactManual(instructions, signal));
|
|
325
|
+
}
|
|
326
|
+
async fork(entryId) {
|
|
327
|
+
this.assertUsable();
|
|
328
|
+
if (this.cycle !== undefined)
|
|
329
|
+
throw new AmaError("busy", "cannot fork while running");
|
|
330
|
+
return new AgentSessionImpl({
|
|
331
|
+
...this.options,
|
|
332
|
+
...this.childBase(),
|
|
333
|
+
sessionManager: this.manager.fork(entryId),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* `/tree`:同文件换叶子;`summarize` 时为离开的分支写 branch_summary(挂在新叶子下)。
|
|
338
|
+
*/
|
|
339
|
+
async navigate(targetId, options = {}) {
|
|
340
|
+
this.assertUsable();
|
|
341
|
+
if (this.cycle !== undefined)
|
|
342
|
+
throw new AmaError("busy", "cannot navigate while running");
|
|
343
|
+
let entry;
|
|
344
|
+
if (options.summarize === true) {
|
|
345
|
+
entry = await this.startCycle((signal) => this.compaction.leaveBranch(targetId, options.instructions, signal));
|
|
346
|
+
}
|
|
347
|
+
else
|
|
348
|
+
this.manager.setLeaf(targetId);
|
|
349
|
+
this.reloadMessages();
|
|
350
|
+
this.cache.onContextChanged();
|
|
351
|
+
return entry;
|
|
352
|
+
}
|
|
353
|
+
async setModel(ref) {
|
|
354
|
+
this.assertUsable();
|
|
355
|
+
const lookup = this.options.providers.findModel(ref);
|
|
356
|
+
if (!lookup.ok) {
|
|
357
|
+
throw new AmaError("model_not_found", `model ${ref} not found`, {
|
|
358
|
+
detail: lookup.candidates,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
this.currentModel = lookup.model;
|
|
362
|
+
this.compaction.refresh();
|
|
363
|
+
const next = modelRefOf(lookup.model);
|
|
364
|
+
this.appendEntry({
|
|
365
|
+
type: "model_change",
|
|
366
|
+
provider: next.provider,
|
|
367
|
+
modelId: next.id,
|
|
368
|
+
...(next.channel !== undefined ? { channel: next.channel } : {}),
|
|
369
|
+
});
|
|
370
|
+
this.emit({ type: "model_changed", model: next });
|
|
371
|
+
}
|
|
372
|
+
setThinkingLevel(level) {
|
|
373
|
+
this.currentThinking = level;
|
|
374
|
+
this.appendEntry({ type: "thinking_level_change", thinkingLevel: level });
|
|
375
|
+
this.emit({ type: "thinking_level_changed", level });
|
|
376
|
+
}
|
|
377
|
+
setPermissionMode(mode) {
|
|
378
|
+
this.options.permission?.setMode(mode);
|
|
379
|
+
this.emit({ type: "permission_mode_changed", mode });
|
|
380
|
+
}
|
|
381
|
+
setActiveTools(names) {
|
|
382
|
+
const unknown = names.filter((name) => !this.allTools.has(name));
|
|
383
|
+
if (unknown.length > 0)
|
|
384
|
+
throw new AmaError("tool_not_found", `unknown tools: ${unknown.join(", ")}`);
|
|
385
|
+
this.activeNames = [...new Set(names)];
|
|
386
|
+
}
|
|
387
|
+
/** 宿主 / SDK 在会话创建后追加工具(下次请求前以 system 补丁声明)。 */
|
|
388
|
+
addTool(tool, active = true) {
|
|
389
|
+
if (this.allTools.has(tool.name))
|
|
390
|
+
throw new AmaError("tool_exists", `tool ${tool.name} already exists`);
|
|
391
|
+
this.allTools.set(tool.name, tool);
|
|
392
|
+
if (active)
|
|
393
|
+
this.activeNames.push(tool.name);
|
|
394
|
+
}
|
|
395
|
+
/** 更新系统提示的静态部分(SessionStart Hook 的 hookContext、宿主 instructions 等)。 */
|
|
396
|
+
updateSystem(patch) {
|
|
397
|
+
this.systemInput = { ...this.systemInput, ...patch };
|
|
398
|
+
}
|
|
399
|
+
setAutoCompaction(enabled) {
|
|
400
|
+
this.compaction.setAuto(enabled);
|
|
401
|
+
}
|
|
402
|
+
setAutoRetry(enabled) {
|
|
403
|
+
this.retrySettings = { ...this.retrySettings, enabled };
|
|
404
|
+
}
|
|
405
|
+
getTools() {
|
|
406
|
+
return this.activeNames
|
|
407
|
+
.map((name) => this.allTools.get(name))
|
|
408
|
+
.filter((tool) => tool !== undefined)
|
|
409
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
410
|
+
}
|
|
411
|
+
/** 发 session_start(bootstrap / SDK 在会话就绪后调用一次)。 */
|
|
412
|
+
announceStart(reason) {
|
|
413
|
+
const event = {
|
|
414
|
+
type: "session_start",
|
|
415
|
+
sessionId: this.manager.id,
|
|
416
|
+
cwd: this.cwd,
|
|
417
|
+
reason,
|
|
418
|
+
};
|
|
419
|
+
const file = this.manager.file();
|
|
420
|
+
if (file !== undefined)
|
|
421
|
+
event.sessionFile = file;
|
|
422
|
+
this.emit(event);
|
|
423
|
+
}
|
|
424
|
+
get state() {
|
|
425
|
+
return buildSessionState({
|
|
426
|
+
agent: this.agent,
|
|
427
|
+
manager: this.manager,
|
|
428
|
+
model: this.currentModel,
|
|
429
|
+
thinkingLevel: this.currentThinking,
|
|
430
|
+
permissionMode: this.options.permission?.mode ?? "default",
|
|
431
|
+
isCompacting: this.compaction.isCompacting,
|
|
432
|
+
isRetrying: this.retrying,
|
|
433
|
+
autoCompaction: this.compaction.settings.enabled,
|
|
434
|
+
autoRetry: this.retrySettings.enabled,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
get messages() {
|
|
438
|
+
return this.agent.messages;
|
|
439
|
+
}
|
|
440
|
+
get entries() {
|
|
441
|
+
return this.manager.entries();
|
|
442
|
+
}
|
|
443
|
+
/** run 被 Hook 的 continue:false 结束时的说明。 */
|
|
444
|
+
get stopHookReason() {
|
|
445
|
+
return this.stopReason;
|
|
446
|
+
}
|
|
447
|
+
getLastAssistantText() {
|
|
448
|
+
return lastAssistantText(this.manager.branch());
|
|
449
|
+
}
|
|
450
|
+
getStats() {
|
|
451
|
+
const contextTokens = this.compaction.estimate().tokens;
|
|
452
|
+
const contextWindow = this.currentModel.contextWindow;
|
|
453
|
+
return computeStats({
|
|
454
|
+
sessionId: this.manager.id,
|
|
455
|
+
sessionFile: this.manager.file(),
|
|
456
|
+
branch: this.manager.branch(),
|
|
457
|
+
contextTokens,
|
|
458
|
+
contextWindow,
|
|
459
|
+
cache: this.cache.stats({ tokens: contextTokens, window: contextWindow }),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
async dispose() {
|
|
463
|
+
if (this.disposed)
|
|
464
|
+
return;
|
|
465
|
+
await this.abort();
|
|
466
|
+
this.cache.dispose();
|
|
467
|
+
this.disposed = true;
|
|
468
|
+
this.manager.close();
|
|
469
|
+
this.listeners.clear();
|
|
470
|
+
}
|
|
471
|
+
// -------------------------------------------------------------------------
|
|
472
|
+
// 子 Agent(ToolContext.spawnSubagent,供 B3 的 task 工具)
|
|
473
|
+
// -------------------------------------------------------------------------
|
|
474
|
+
childBase() {
|
|
475
|
+
return {
|
|
476
|
+
model: this.currentModel,
|
|
477
|
+
thinkingLevel: this.currentThinking,
|
|
478
|
+
activeTools: this.activeNames,
|
|
479
|
+
system: this.systemInput,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
spawnSubagent(request) {
|
|
483
|
+
return runSubagent(this, request, this.subagentPool, (options) => new AgentSessionImpl(options));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 系统提示的命名节装配与补丁 diff(设计 §1.2 system-prompt.ts、§9「缓存」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host
|
|
5
|
+
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add,最后)。
|
|
6
|
+
* 不含时间戳等易变内容,保证前缀缓存稳定。
|
|
7
|
+
*
|
|
8
|
+
* 首次请求前,整份节 + 工具表作为首条 `system` 消息落盘;之后节或工具表变化时只落补丁
|
|
9
|
+
* (节名级替换、`null` 删除;`toolsAdded` / `toolsRemoved`),请求时由协议层重装全量。
|
|
10
|
+
*/
|
|
11
|
+
import type { SystemMessage, ToolDecl } from "../ai/types.js";
|
|
12
|
+
import { type SystemState } from "../session/projection.js";
|
|
13
|
+
import type { AgentMessage } from "../session/types.js";
|
|
14
|
+
import type { ToolDefinition } from "../tools/types.js";
|
|
15
|
+
export declare const SECTION_ORDER: readonly ["preamble", "tools", "rules", "project_context", "skills", "hooks", "cwd", "host"];
|
|
16
|
+
export type SectionName = (typeof SECTION_ORDER)[number];
|
|
17
|
+
export declare const DEFAULT_PREAMBLE: string;
|
|
18
|
+
export interface SystemPromptInput {
|
|
19
|
+
preamble?: string;
|
|
20
|
+
tools: readonly ToolDefinition[];
|
|
21
|
+
extraRules?: readonly string[];
|
|
22
|
+
contextFiles?: readonly {
|
|
23
|
+
path: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}[];
|
|
26
|
+
/** 预渲染的 `<available_skills>` 索引(B3 的 index-prompt.ts);缺省时由 skills 列表生成最小索引。 */
|
|
27
|
+
skillsIndex?: string;
|
|
28
|
+
skills?: readonly {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
location: string;
|
|
32
|
+
}[];
|
|
33
|
+
/** SessionStart Hook 的 additionalContext。 */
|
|
34
|
+
hookContext?: string;
|
|
35
|
+
cwd: string;
|
|
36
|
+
/** 宿主 instructions(已读入的文本),按添加顺序。 */
|
|
37
|
+
hostInstructions?: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function toolDecl(tool: ToolDefinition): ToolDecl;
|
|
40
|
+
/** 按名排序的工具声明(与 ToolRegistry.active() 的顺序一致)。 */
|
|
41
|
+
export declare function toolDecls(tools: readonly ToolDefinition[]): ToolDecl[];
|
|
42
|
+
export declare function assembleSections(input: SystemPromptInput): Record<SectionName, string | undefined>;
|
|
43
|
+
/** 去掉 undefined 的有序节表。 */
|
|
44
|
+
export declare function definedSections(sections: Record<SectionName, string | undefined>): Record<string, string>;
|
|
45
|
+
export declare function currentSystemState(messages: Iterable<AgentMessage>): SystemState | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* 当前状态 → 目标状态的 system 消息:无当前状态 → 全量;有差异 → 补丁;无差异 → undefined。
|
|
48
|
+
*/
|
|
49
|
+
export declare function diffSystem(current: SystemState | undefined, sections: Record<string, string>, tools: readonly ToolDecl[], timestamp?: number): SystemMessage | undefined;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 系统提示的命名节装配与补丁 diff(设计 §1.2 system-prompt.ts、§9「缓存」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host
|
|
5
|
+
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add,最后)。
|
|
6
|
+
* 不含时间戳等易变内容,保证前缀缓存稳定。
|
|
7
|
+
*
|
|
8
|
+
* 首次请求前,整份节 + 工具表作为首条 `system` 消息落盘;之后节或工具表变化时只落补丁
|
|
9
|
+
* (节名级替换、`null` 删除;`toolsAdded` / `toolsRemoved`),请求时由协议层重装全量。
|
|
10
|
+
*/
|
|
11
|
+
import { replaySystem } from "../session/projection.js";
|
|
12
|
+
import { escapeXml } from "../skills/index-prompt.js";
|
|
13
|
+
export const SECTION_ORDER = [
|
|
14
|
+
"preamble",
|
|
15
|
+
"tools",
|
|
16
|
+
"rules",
|
|
17
|
+
"project_context",
|
|
18
|
+
"skills",
|
|
19
|
+
"hooks",
|
|
20
|
+
"cwd",
|
|
21
|
+
"host",
|
|
22
|
+
];
|
|
23
|
+
export const DEFAULT_PREAMBLE = "You are ama, a coding agent working in the user's project through tools. Read files before " +
|
|
24
|
+
"changing them, make focused changes, and verify your work. Be concise.";
|
|
25
|
+
function firstLine(text) {
|
|
26
|
+
return text.split("\n", 1)[0]?.trim() ?? "";
|
|
27
|
+
}
|
|
28
|
+
export function toolDecl(tool) {
|
|
29
|
+
return { name: tool.name, description: tool.description, parameters: tool.parameters };
|
|
30
|
+
}
|
|
31
|
+
/** 按名排序的工具声明(与 ToolRegistry.active() 的顺序一致)。 */
|
|
32
|
+
export function toolDecls(tools) {
|
|
33
|
+
return [...tools].sort((a, b) => a.name.localeCompare(b.name)).map(toolDecl);
|
|
34
|
+
}
|
|
35
|
+
export function assembleSections(input) {
|
|
36
|
+
const tools = [...input.tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
37
|
+
const toolLines = tools.map((tool) => {
|
|
38
|
+
const snippet = tool.promptSnippet ?? firstLine(tool.description);
|
|
39
|
+
// 内置工具的 promptSnippet 已以「name: 」开头(B3 约定),不再重复工具名。
|
|
40
|
+
return snippet.startsWith(`${tool.name}:`) ? `- ${snippet}` : `- ${tool.name}: ${snippet}`;
|
|
41
|
+
});
|
|
42
|
+
const rules = [
|
|
43
|
+
...tools.flatMap((tool) => tool.promptGuidelines ?? []),
|
|
44
|
+
...(input.extraRules ?? []),
|
|
45
|
+
];
|
|
46
|
+
const projectContext = (input.contextFiles ?? [])
|
|
47
|
+
.map((file) => `<file path="${escapeXml(file.path)}">\n${file.content.trim()}\n</file>`)
|
|
48
|
+
.join("\n\n");
|
|
49
|
+
let skills = input.skillsIndex;
|
|
50
|
+
if (skills === undefined && input.skills !== undefined && input.skills.length > 0) {
|
|
51
|
+
skills = [
|
|
52
|
+
"<available_skills>",
|
|
53
|
+
...input.skills.map((skill) => `<skill name="${escapeXml(skill.name)}" location="${escapeXml(skill.location)}">${escapeXml(skill.description)}</skill>`),
|
|
54
|
+
"</available_skills>",
|
|
55
|
+
].join("\n");
|
|
56
|
+
}
|
|
57
|
+
const host = (input.hostInstructions ?? []).map((text) => text.trim()).filter(Boolean);
|
|
58
|
+
const nonEmpty = (text) => text === undefined || text.trim() === "" ? undefined : text;
|
|
59
|
+
return {
|
|
60
|
+
preamble: input.preamble ?? DEFAULT_PREAMBLE,
|
|
61
|
+
tools: toolLines.length > 0 ? `Available tools:\n${toolLines.join("\n")}` : undefined,
|
|
62
|
+
rules: rules.length > 0 ? `Rules:\n${rules.map((rule) => `- ${rule}`).join("\n")}` : undefined,
|
|
63
|
+
project_context: nonEmpty(projectContext),
|
|
64
|
+
skills: nonEmpty(skills),
|
|
65
|
+
hooks: nonEmpty(input.hookContext),
|
|
66
|
+
cwd: `Current working directory: ${input.cwd}`,
|
|
67
|
+
host: host.length > 0 ? host.join("\n\n") : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** 去掉 undefined 的有序节表。 */
|
|
71
|
+
export function definedSections(sections) {
|
|
72
|
+
const out = {};
|
|
73
|
+
for (const name of SECTION_ORDER) {
|
|
74
|
+
const text = sections[name];
|
|
75
|
+
if (text !== undefined)
|
|
76
|
+
out[name] = text;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
export function currentSystemState(messages) {
|
|
81
|
+
return replaySystem(messages);
|
|
82
|
+
}
|
|
83
|
+
function sameTool(a, b) {
|
|
84
|
+
return (a.description === b.description && JSON.stringify(a.parameters) === JSON.stringify(b.parameters));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 当前状态 → 目标状态的 system 消息:无当前状态 → 全量;有差异 → 补丁;无差异 → undefined。
|
|
88
|
+
*/
|
|
89
|
+
export function diffSystem(current, sections, tools, timestamp = Date.now()) {
|
|
90
|
+
if (current === undefined) {
|
|
91
|
+
const full = { role: "system", sections: { ...sections }, timestamp };
|
|
92
|
+
if (tools.length > 0)
|
|
93
|
+
full.toolsAdded = tools.map((tool) => ({ ...tool }));
|
|
94
|
+
return full;
|
|
95
|
+
}
|
|
96
|
+
const patch = {};
|
|
97
|
+
for (const [name, text] of Object.entries(sections)) {
|
|
98
|
+
if (current.sections[name] !== text)
|
|
99
|
+
patch[name] = text;
|
|
100
|
+
}
|
|
101
|
+
for (const name of Object.keys(current.sections)) {
|
|
102
|
+
if (!(name in sections))
|
|
103
|
+
patch[name] = null;
|
|
104
|
+
}
|
|
105
|
+
const before = new Map(current.tools.map((tool) => [tool.name, tool]));
|
|
106
|
+
const after = new Map(tools.map((tool) => [tool.name, tool]));
|
|
107
|
+
const toolsAdded = tools.filter((tool) => {
|
|
108
|
+
const old = before.get(tool.name);
|
|
109
|
+
return old === undefined || !sameTool(old, tool);
|
|
110
|
+
});
|
|
111
|
+
const toolsRemoved = [...before.keys()].filter((name) => !after.has(name));
|
|
112
|
+
if (Object.keys(patch).length === 0 && toolsAdded.length === 0 && toolsRemoved.length === 0) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const message = { role: "system", sections: patch, timestamp };
|
|
116
|
+
if (toolsAdded.length > 0)
|
|
117
|
+
message.toolsAdded = toolsAdded.map((tool) => ({ ...tool }));
|
|
118
|
+
if (toolsRemoved.length > 0)
|
|
119
|
+
message.toolsRemoved = toolsRemoved;
|
|
120
|
+
return message;
|
|
121
|
+
}
|