@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,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档二切点(设计 §9「档二」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 在投影条目上从新往旧累计估算 token,到 `keepRecentTokens` 为止,切在其后最近的合法切点:
|
|
5
|
+
* user / assistant / custom_message / branch_summary(**不切 toolResult**;切在带工具调用的
|
|
6
|
+
* assistant 上时,它的结果随之保留)。system 消息不参与(它们折进 system 检查点,永不被摘要掉)。
|
|
7
|
+
* 切点不是回合起点(user / custom / branch_summary)时为 split turn:被摘要段分成「历史」与
|
|
8
|
+
* 「本回合前缀」两部分,各写一份摘要再合并。
|
|
9
|
+
*/
|
|
10
|
+
import type { ContextItem } from "../session/projection.js";
|
|
11
|
+
import type { AgentMessage } from "../session/types.js";
|
|
12
|
+
export interface CutPointResult {
|
|
13
|
+
/** items 中保留区的起点。等于 startIndex 表示没有可摘要的内容。 */
|
|
14
|
+
firstKeptIndex: number;
|
|
15
|
+
/** split turn 时本回合起点;否则 -1。 */
|
|
16
|
+
turnStartIndex: number;
|
|
17
|
+
isSplitTurn: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function isCutPoint(message: AgentMessage): boolean;
|
|
20
|
+
export declare function isTurnStart(message: AgentMessage): boolean;
|
|
21
|
+
/** 摘要区的起点:跳过开头的 system 检查点与上一份压缩摘要。 */
|
|
22
|
+
export declare function summarizableStart(items: readonly ContextItem[]): number;
|
|
23
|
+
export declare function findCutPoint(items: readonly ContextItem[], startIndex: number, keepRecentTokens: number): CutPointResult;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档二切点(设计 §9「档二」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 在投影条目上从新往旧累计估算 token,到 `keepRecentTokens` 为止,切在其后最近的合法切点:
|
|
5
|
+
* user / assistant / custom_message / branch_summary(**不切 toolResult**;切在带工具调用的
|
|
6
|
+
* assistant 上时,它的结果随之保留)。system 消息不参与(它们折进 system 检查点,永不被摘要掉)。
|
|
7
|
+
* 切点不是回合起点(user / custom / branch_summary)时为 split turn:被摘要段分成「历史」与
|
|
8
|
+
* 「本回合前缀」两部分,各写一份摘要再合并。
|
|
9
|
+
*/
|
|
10
|
+
import { estimateMessageTokens } from "./estimate.js";
|
|
11
|
+
export function isCutPoint(message) {
|
|
12
|
+
return (message.role === "user" ||
|
|
13
|
+
message.role === "assistant" ||
|
|
14
|
+
message.role === "custom" ||
|
|
15
|
+
message.role === "branchSummary");
|
|
16
|
+
}
|
|
17
|
+
export function isTurnStart(message) {
|
|
18
|
+
return message.role === "user" || message.role === "custom" || message.role === "branchSummary";
|
|
19
|
+
}
|
|
20
|
+
/** 摘要区的起点:跳过开头的 system 检查点与上一份压缩摘要。 */
|
|
21
|
+
export function summarizableStart(items) {
|
|
22
|
+
let i = 0;
|
|
23
|
+
while (i < items.length) {
|
|
24
|
+
const role = items[i]?.message.role;
|
|
25
|
+
if (role !== "system" && role !== "compactionSummary")
|
|
26
|
+
break;
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
return i;
|
|
30
|
+
}
|
|
31
|
+
export function findCutPoint(items, startIndex, keepRecentTokens) {
|
|
32
|
+
const cutPoints = [];
|
|
33
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
34
|
+
const message = items[i]?.message;
|
|
35
|
+
if (message !== undefined && isCutPoint(message))
|
|
36
|
+
cutPoints.push(i);
|
|
37
|
+
}
|
|
38
|
+
const none = {
|
|
39
|
+
firstKeptIndex: startIndex,
|
|
40
|
+
turnStartIndex: -1,
|
|
41
|
+
isSplitTurn: false,
|
|
42
|
+
};
|
|
43
|
+
if (cutPoints.length === 0)
|
|
44
|
+
return none;
|
|
45
|
+
let accumulated = 0;
|
|
46
|
+
let cutIndex = startIndex;
|
|
47
|
+
for (let i = items.length - 1; i >= startIndex; i--) {
|
|
48
|
+
const message = items[i]?.message;
|
|
49
|
+
if (message === undefined || message.role === "system")
|
|
50
|
+
continue;
|
|
51
|
+
accumulated += estimateMessageTokens(message);
|
|
52
|
+
if (accumulated >= keepRecentTokens) {
|
|
53
|
+
cutIndex = cutPoints.find((candidate) => candidate >= i) ?? cutPoints.at(-1);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (cutIndex <= startIndex)
|
|
58
|
+
return none;
|
|
59
|
+
const cutMessage = items[cutIndex]?.message;
|
|
60
|
+
if (cutMessage === undefined || isTurnStart(cutMessage)) {
|
|
61
|
+
return { firstKeptIndex: cutIndex, turnStartIndex: -1, isSplitTurn: false };
|
|
62
|
+
}
|
|
63
|
+
for (let i = cutIndex - 1; i >= startIndex; i--) {
|
|
64
|
+
const message = items[i]?.message;
|
|
65
|
+
if (message !== undefined && isTurnStart(message)) {
|
|
66
|
+
return { firstKeptIndex: cutIndex, turnStartIndex: i, isSplitTurn: true };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { firstKeptIndex: cutIndex, turnStartIndex: -1, isSplitTurn: false };
|
|
70
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文 token 估算(设计 §9「估算」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* `contextTokens` = 最后一条非 error / aborted 助手消息的 `totalTokens`(缺则
|
|
5
|
+
* `input + output + cacheRead + cacheWrite`,**含 output**)+ 其后消息的估算(字符 / 4,图片 1 600)。
|
|
6
|
+
* 若该 usage 之后活动分支上出现过 `context_edit` 或 `compaction`,usage 不再可信,按投影全量重估。
|
|
7
|
+
*/
|
|
8
|
+
import type { Usage } from "../ai/types.js";
|
|
9
|
+
import type { ContextItem } from "../session/projection.js";
|
|
10
|
+
import type { AgentMessage, SessionEntry } from "../session/types.js";
|
|
11
|
+
export declare const IMAGE_TOKENS = 1600;
|
|
12
|
+
export declare function calculateContextTokens(usage: Usage): number;
|
|
13
|
+
/** 单条消息的估算(字符 / 4 向上取整,图片每张 1 600)。 */
|
|
14
|
+
export declare function estimateMessageTokens(message: AgentMessage): number;
|
|
15
|
+
export declare function estimateMessagesTokens(messages: readonly AgentMessage[]): number;
|
|
16
|
+
export interface ContextEstimate {
|
|
17
|
+
tokens: number;
|
|
18
|
+
/** 来自 usage 的部分(0 = 全量估算)。 */
|
|
19
|
+
usageTokens: number;
|
|
20
|
+
trailingTokens: number;
|
|
21
|
+
/** 被采信的 usage 所在消息下标。 */
|
|
22
|
+
lastUsageIndex: number | null;
|
|
23
|
+
}
|
|
24
|
+
/** 只看消息:最后一条可用 usage + 其后估算;无 usage 则全量估算。 */
|
|
25
|
+
export declare function estimateContextTokens(messages: readonly AgentMessage[]): ContextEstimate;
|
|
26
|
+
/**
|
|
27
|
+
* 投影感知的估算:usage 所在条目之后若有 context_edit / compaction,按投影全量重估。
|
|
28
|
+
* `items` 是 buildProjection(branch).items,`branch` 是同一活动分支。
|
|
29
|
+
*/
|
|
30
|
+
export declare function estimateProjectedTokens(items: readonly ContextItem[], branch: readonly SessionEntry[]): ContextEstimate;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文 token 估算(设计 §9「估算」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* `contextTokens` = 最后一条非 error / aborted 助手消息的 `totalTokens`(缺则
|
|
5
|
+
* `input + output + cacheRead + cacheWrite`,**含 output**)+ 其后消息的估算(字符 / 4,图片 1 600)。
|
|
6
|
+
* 若该 usage 之后活动分支上出现过 `context_edit` 或 `compaction`,usage 不再可信,按投影全量重估。
|
|
7
|
+
*/
|
|
8
|
+
export const IMAGE_TOKENS = 1600;
|
|
9
|
+
const CHARS_PER_TOKEN = 4;
|
|
10
|
+
export function calculateContextTokens(usage) {
|
|
11
|
+
return usage.totalTokens > 0
|
|
12
|
+
? usage.totalTokens
|
|
13
|
+
: usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
14
|
+
}
|
|
15
|
+
function contentChars(content) {
|
|
16
|
+
if (typeof content === "string")
|
|
17
|
+
return { chars: content.length, images: 0 };
|
|
18
|
+
let chars = 0;
|
|
19
|
+
let images = 0;
|
|
20
|
+
for (const block of content) {
|
|
21
|
+
if (block.type === "text")
|
|
22
|
+
chars += block.text.length;
|
|
23
|
+
else
|
|
24
|
+
images++;
|
|
25
|
+
}
|
|
26
|
+
return { chars, images };
|
|
27
|
+
}
|
|
28
|
+
/** 单条消息的估算(字符 / 4 向上取整,图片每张 1 600)。 */
|
|
29
|
+
export function estimateMessageTokens(message) {
|
|
30
|
+
let chars = 0;
|
|
31
|
+
let images = 0;
|
|
32
|
+
switch (message.role) {
|
|
33
|
+
case "system":
|
|
34
|
+
for (const text of Object.values(message.sections))
|
|
35
|
+
chars += text?.length ?? 0;
|
|
36
|
+
if (message.toolsAdded !== undefined)
|
|
37
|
+
chars += JSON.stringify(message.toolsAdded).length;
|
|
38
|
+
break;
|
|
39
|
+
case "user":
|
|
40
|
+
case "toolResult":
|
|
41
|
+
case "custom": {
|
|
42
|
+
const counted = contentChars(message.content);
|
|
43
|
+
chars = counted.chars;
|
|
44
|
+
images = counted.images;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "assistant":
|
|
48
|
+
for (const block of message.content) {
|
|
49
|
+
if (block.type === "text")
|
|
50
|
+
chars += block.text.length;
|
|
51
|
+
else if (block.type === "thinking")
|
|
52
|
+
chars += block.thinking.length;
|
|
53
|
+
else
|
|
54
|
+
chars += block.name.length + JSON.stringify(block.arguments).length;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case "compactionSummary":
|
|
58
|
+
case "branchSummary":
|
|
59
|
+
chars = message.summary.length;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return Math.ceil(chars / CHARS_PER_TOKEN) + images * IMAGE_TOKENS;
|
|
63
|
+
}
|
|
64
|
+
export function estimateMessagesTokens(messages) {
|
|
65
|
+
let total = 0;
|
|
66
|
+
for (const message of messages)
|
|
67
|
+
total += estimateMessageTokens(message);
|
|
68
|
+
return total;
|
|
69
|
+
}
|
|
70
|
+
function usableUsage(message) {
|
|
71
|
+
if (message.role !== "assistant")
|
|
72
|
+
return undefined;
|
|
73
|
+
if (message.stopReason === "error" || message.stopReason === "aborted")
|
|
74
|
+
return undefined;
|
|
75
|
+
return calculateContextTokens(message.usage) > 0 ? message.usage : undefined;
|
|
76
|
+
}
|
|
77
|
+
/** 只看消息:最后一条可用 usage + 其后估算;无 usage 则全量估算。 */
|
|
78
|
+
export function estimateContextTokens(messages) {
|
|
79
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
80
|
+
const message = messages[i];
|
|
81
|
+
const usage = message === undefined ? undefined : usableUsage(message);
|
|
82
|
+
if (usage === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
const usageTokens = calculateContextTokens(usage);
|
|
85
|
+
const trailingTokens = estimateMessagesTokens(messages.slice(i + 1));
|
|
86
|
+
return { tokens: usageTokens + trailingTokens, usageTokens, trailingTokens, lastUsageIndex: i };
|
|
87
|
+
}
|
|
88
|
+
const tokens = estimateMessagesTokens(messages);
|
|
89
|
+
return { tokens, usageTokens: 0, trailingTokens: tokens, lastUsageIndex: null };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 投影感知的估算:usage 所在条目之后若有 context_edit / compaction,按投影全量重估。
|
|
93
|
+
* `items` 是 buildProjection(branch).items,`branch` 是同一活动分支。
|
|
94
|
+
*/
|
|
95
|
+
export function estimateProjectedTokens(items, branch) {
|
|
96
|
+
const messages = items.map((item) => item.message);
|
|
97
|
+
const estimate = estimateContextTokens(messages);
|
|
98
|
+
if (estimate.lastUsageIndex !== null) {
|
|
99
|
+
const usageEntryId = items[estimate.lastUsageIndex]?.entry.id;
|
|
100
|
+
const usageAt = branch.findIndex((entry) => entry.id === usageEntryId);
|
|
101
|
+
let invalidatedAt = -1;
|
|
102
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
103
|
+
const type = branch[i]?.type;
|
|
104
|
+
if (type === "context_edit" || type === "compaction") {
|
|
105
|
+
invalidatedAt = i;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (usageAt > invalidatedAt)
|
|
110
|
+
return estimate;
|
|
111
|
+
}
|
|
112
|
+
const tokens = estimateMessagesTokens(messages);
|
|
113
|
+
return { tokens, usageTokens: 0, trailingTokens: tokens, lastUsageIndex: null };
|
|
114
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档一裁剪(设计 §9「档一」):无模型调用。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 触发:`contextTokens > 0.7 × (contextWindow − reserveTokens)`。
|
|
5
|
+
* 范围:最近两个用户回合之前、内容 > 2 KiB 的 toolResult。
|
|
6
|
+
* 动作:返回 `context_edit{reason:"prune", replacement:"[已裁剪 …全文 path]"}` 计划;有 outputDir 时
|
|
7
|
+
* 把全文写到 `<outputDir>/<toolCallId>.txt`(已存在则不覆盖),模型可用 read 取回。
|
|
8
|
+
*/
|
|
9
|
+
import type { ContentBlock } from "../ai/types.js";
|
|
10
|
+
import type { ContextItem } from "../session/projection.js";
|
|
11
|
+
export declare const PRUNE_THRESHOLD_RATIO = 0.7;
|
|
12
|
+
export declare const PRUNE_MIN_BYTES = 2048;
|
|
13
|
+
export declare const PRUNE_KEEP_USER_TURNS = 2;
|
|
14
|
+
export interface PrunePlanItem {
|
|
15
|
+
targetId: string;
|
|
16
|
+
replacement: string;
|
|
17
|
+
toolCallId: string;
|
|
18
|
+
originalBytes: number;
|
|
19
|
+
fullTextPath?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PruneOptions {
|
|
22
|
+
keepUserTurns?: number;
|
|
23
|
+
minBytes?: number;
|
|
24
|
+
/** 全文落盘目录;undefined 时不落盘(内存会话)。 */
|
|
25
|
+
outputDir?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
export declare function pruneThreshold(contextWindow: number, reserveTokens: number): number;
|
|
28
|
+
export declare function shouldPrune(tokens: number, contextWindow: number, reserveTokens: number): boolean;
|
|
29
|
+
export declare function contentToText(content: string | readonly ContentBlock[]): string;
|
|
30
|
+
/** 计算裁剪计划(并按需落盘全文);返回空数组表示无可裁剪项。 */
|
|
31
|
+
export declare function planPrune(items: readonly ContextItem[], options?: PruneOptions): PrunePlanItem[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档一裁剪(设计 §9「档一」):无模型调用。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 触发:`contextTokens > 0.7 × (contextWindow − reserveTokens)`。
|
|
5
|
+
* 范围:最近两个用户回合之前、内容 > 2 KiB 的 toolResult。
|
|
6
|
+
* 动作:返回 `context_edit{reason:"prune", replacement:"[已裁剪 …全文 path]"}` 计划;有 outputDir 时
|
|
7
|
+
* 把全文写到 `<outputDir>/<toolCallId>.txt`(已存在则不覆盖),模型可用 read 取回。
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
export const PRUNE_THRESHOLD_RATIO = 0.7;
|
|
12
|
+
export const PRUNE_MIN_BYTES = 2048;
|
|
13
|
+
export const PRUNE_KEEP_USER_TURNS = 2;
|
|
14
|
+
export function pruneThreshold(contextWindow, reserveTokens) {
|
|
15
|
+
return PRUNE_THRESHOLD_RATIO * Math.max(0, contextWindow - reserveTokens);
|
|
16
|
+
}
|
|
17
|
+
export function shouldPrune(tokens, contextWindow, reserveTokens) {
|
|
18
|
+
return tokens > pruneThreshold(contextWindow, reserveTokens);
|
|
19
|
+
}
|
|
20
|
+
export function contentToText(content) {
|
|
21
|
+
if (typeof content === "string")
|
|
22
|
+
return content;
|
|
23
|
+
return content
|
|
24
|
+
.map((block) => (block.type === "text" ? block.text : `[image ${block.mimeType}]`))
|
|
25
|
+
.join("\n");
|
|
26
|
+
}
|
|
27
|
+
function safeFileStem(id) {
|
|
28
|
+
return id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 120) || "tool";
|
|
29
|
+
}
|
|
30
|
+
/** 计算裁剪计划(并按需落盘全文);返回空数组表示无可裁剪项。 */
|
|
31
|
+
export function planPrune(items, options = {}) {
|
|
32
|
+
const keepTurns = options.keepUserTurns ?? PRUNE_KEEP_USER_TURNS;
|
|
33
|
+
const minBytes = options.minBytes ?? PRUNE_MIN_BYTES;
|
|
34
|
+
let boundary = items.length;
|
|
35
|
+
let seen = 0;
|
|
36
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
37
|
+
if (items[i]?.message.role !== "user")
|
|
38
|
+
continue;
|
|
39
|
+
seen++;
|
|
40
|
+
if (seen >= keepTurns) {
|
|
41
|
+
boundary = i;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (seen < keepTurns)
|
|
46
|
+
return [];
|
|
47
|
+
const plan = [];
|
|
48
|
+
for (const item of items.slice(0, boundary)) {
|
|
49
|
+
const { message, entry } = item;
|
|
50
|
+
if (message.role !== "toolResult" || entry.type !== "message")
|
|
51
|
+
continue;
|
|
52
|
+
const text = contentToText(message.content);
|
|
53
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
54
|
+
if (bytes <= minBytes)
|
|
55
|
+
continue;
|
|
56
|
+
let fullTextPath;
|
|
57
|
+
if (options.outputDir !== undefined) {
|
|
58
|
+
fullTextPath = join(options.outputDir, `${safeFileStem(message.toolCallId)}.txt`);
|
|
59
|
+
try {
|
|
60
|
+
if (!existsSync(fullTextPath)) {
|
|
61
|
+
mkdirSync(options.outputDir, { recursive: true });
|
|
62
|
+
writeFileSync(fullTextPath, text, "utf8");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
fullTextPath = undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const replacement = fullTextPath === undefined
|
|
70
|
+
? `[已裁剪:${message.toolName} 的结果原有 ${bytes} 字节,全文不可用]`
|
|
71
|
+
: `[已裁剪:${message.toolName} 的结果原有 ${bytes} 字节,全文 ${fullTextPath}]`;
|
|
72
|
+
const planned = {
|
|
73
|
+
targetId: entry.id,
|
|
74
|
+
replacement,
|
|
75
|
+
toolCallId: message.toolCallId,
|
|
76
|
+
originalBytes: bytes,
|
|
77
|
+
};
|
|
78
|
+
if (fullTextPath !== undefined)
|
|
79
|
+
planned.fullTextPath = fullTextPath;
|
|
80
|
+
plan.push(planned);
|
|
81
|
+
}
|
|
82
|
+
return plan;
|
|
83
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 被摘要段的文本序列化(设计 §9「模板」)与文件操作累计。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 形状:`[User]: … / [Assistant thinking]: … / [Assistant]: … / [Assistant tool calls]: read(path="…") /
|
|
5
|
+
* [Tool result]: …`——写成记录而非对话,避免摘要模型把它当成要续写的对话。工具结果截 2 000 字符。
|
|
6
|
+
*/
|
|
7
|
+
import type { FileOpsDetails } from "../session/types.js";
|
|
8
|
+
import type { AgentMessage } from "../session/types.js";
|
|
9
|
+
export declare const TOOL_RESULT_SERIALIZE_LIMIT = 2000;
|
|
10
|
+
export declare function serializeMessage(message: AgentMessage): string;
|
|
11
|
+
export declare function serializeConversation(messages: readonly AgentMessage[]): string;
|
|
12
|
+
export interface FileOps {
|
|
13
|
+
read: Set<string>;
|
|
14
|
+
modified: Set<string>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createFileOps(previous?: FileOpsDetails): FileOps;
|
|
17
|
+
export declare function collectFileOps(messages: readonly AgentMessage[], ops: FileOps): FileOps;
|
|
18
|
+
/** 只读过、没改过的进 readFiles;改过的进 modifiedFiles。 */
|
|
19
|
+
export declare function fileOpsDetails(ops: FileOps): FileOpsDetails;
|
|
20
|
+
export declare function formatFileOps(details: FileOpsDetails): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 被摘要段的文本序列化(设计 §9「模板」)与文件操作累计。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 形状:`[User]: … / [Assistant thinking]: … / [Assistant]: … / [Assistant tool calls]: read(path="…") /
|
|
5
|
+
* [Tool result]: …`——写成记录而非对话,避免摘要模型把它当成要续写的对话。工具结果截 2 000 字符。
|
|
6
|
+
*/
|
|
7
|
+
import { contentToText } from "./prune-tier.js";
|
|
8
|
+
export const TOOL_RESULT_SERIALIZE_LIMIT = 2000;
|
|
9
|
+
function truncate(text, limit) {
|
|
10
|
+
if (text.length <= limit)
|
|
11
|
+
return text;
|
|
12
|
+
return `${text.slice(0, limit)}… [截断,原 ${text.length} 字符]`;
|
|
13
|
+
}
|
|
14
|
+
function formatArgs(args) {
|
|
15
|
+
return Object.entries(args)
|
|
16
|
+
.map(([key, value]) => {
|
|
17
|
+
const text = typeof value === "string" ? JSON.stringify(value) : (JSON.stringify(value) ?? "");
|
|
18
|
+
return `${key}=${truncate(text, 300)}`;
|
|
19
|
+
})
|
|
20
|
+
.join(", ");
|
|
21
|
+
}
|
|
22
|
+
export function serializeMessage(message) {
|
|
23
|
+
switch (message.role) {
|
|
24
|
+
case "system":
|
|
25
|
+
return "";
|
|
26
|
+
case "user":
|
|
27
|
+
return `[User]: ${contentToText(message.content)}`;
|
|
28
|
+
case "custom":
|
|
29
|
+
return `[Context ${message.customType}]: ${contentToText(message.content)}`;
|
|
30
|
+
case "compactionSummary":
|
|
31
|
+
return `[Earlier summary]: ${message.summary}`;
|
|
32
|
+
case "branchSummary":
|
|
33
|
+
return `[Branch summary]: ${message.summary}`;
|
|
34
|
+
case "toolResult":
|
|
35
|
+
return `[Tool result${message.isError ? " (error)" : ""} ${message.toolName}]: ${truncate(contentToText(message.content), TOOL_RESULT_SERIALIZE_LIMIT)}`;
|
|
36
|
+
case "assistant": {
|
|
37
|
+
const parts = [];
|
|
38
|
+
const thinking = message.content
|
|
39
|
+
.filter((block) => block.type === "thinking")
|
|
40
|
+
.map((block) => block.thinking)
|
|
41
|
+
.join("\n")
|
|
42
|
+
.trim();
|
|
43
|
+
if (thinking !== "")
|
|
44
|
+
parts.push(`[Assistant thinking]: ${thinking}`);
|
|
45
|
+
const text = message.content
|
|
46
|
+
.filter((block) => block.type === "text")
|
|
47
|
+
.map((block) => block.text)
|
|
48
|
+
.join("\n")
|
|
49
|
+
.trim();
|
|
50
|
+
if (text !== "")
|
|
51
|
+
parts.push(`[Assistant]: ${text}`);
|
|
52
|
+
const calls = message.content
|
|
53
|
+
.filter((block) => block.type === "toolCall")
|
|
54
|
+
.map((block) => `${block.name}(${formatArgs(block.arguments)})`);
|
|
55
|
+
if (calls.length > 0)
|
|
56
|
+
parts.push(`[Assistant tool calls]: ${calls.join("; ")}`);
|
|
57
|
+
if (message.stopReason === "error" && message.errorMessage !== undefined) {
|
|
58
|
+
parts.push(`[Assistant error]: ${message.errorMessage}`);
|
|
59
|
+
}
|
|
60
|
+
return parts.join("\n");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function serializeConversation(messages) {
|
|
65
|
+
return messages
|
|
66
|
+
.map(serializeMessage)
|
|
67
|
+
.filter((text) => text !== "")
|
|
68
|
+
.join("\n\n");
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// 文件操作
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
const READ_TOOLS = new Set(["read"]);
|
|
74
|
+
const WRITE_TOOLS = new Set(["write", "edit"]);
|
|
75
|
+
export function createFileOps(previous) {
|
|
76
|
+
return {
|
|
77
|
+
read: new Set(previous?.readFiles ?? []),
|
|
78
|
+
modified: new Set(previous?.modifiedFiles ?? []),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function collectFileOps(messages, ops) {
|
|
82
|
+
for (const message of messages) {
|
|
83
|
+
if (message.role !== "assistant")
|
|
84
|
+
continue;
|
|
85
|
+
for (const block of message.content) {
|
|
86
|
+
if (block.type !== "toolCall")
|
|
87
|
+
continue;
|
|
88
|
+
const path = block.arguments["path"];
|
|
89
|
+
if (typeof path !== "string" || path === "")
|
|
90
|
+
continue;
|
|
91
|
+
if (READ_TOOLS.has(block.name))
|
|
92
|
+
ops.read.add(path);
|
|
93
|
+
else if (WRITE_TOOLS.has(block.name))
|
|
94
|
+
ops.modified.add(path);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return ops;
|
|
98
|
+
}
|
|
99
|
+
/** 只读过、没改过的进 readFiles;改过的进 modifiedFiles。 */
|
|
100
|
+
export function fileOpsDetails(ops) {
|
|
101
|
+
return {
|
|
102
|
+
readFiles: [...ops.read].filter((path) => !ops.modified.has(path)).sort(),
|
|
103
|
+
modifiedFiles: [...ops.modified].sort(),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function formatFileOps(details) {
|
|
107
|
+
const parts = [];
|
|
108
|
+
if (details.readFiles.length > 0) {
|
|
109
|
+
parts.push(`<read-files>\n${details.readFiles.join("\n")}\n</read-files>`);
|
|
110
|
+
}
|
|
111
|
+
if (details.modifiedFiles.length > 0) {
|
|
112
|
+
parts.push(`<modified-files>\n${details.modifiedFiles.join("\n")}\n</modified-files>`);
|
|
113
|
+
}
|
|
114
|
+
return parts.join("\n\n");
|
|
115
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档二摘要(设计 §9「档二」「模板」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* `prepareCompaction()` 在投影上找切点,得到「要摘要的消息」与 `firstKeptEntryId`;
|
|
5
|
+
* `runCompaction()` 请模型按模板写摘要,split turn 时历史与回合前缀各一份再合并;
|
|
6
|
+
* `<read-files>` / `<modified-files>` 与上一份 compaction 的 details 累计。失败抛
|
|
7
|
+
* `AmaError{code:"compaction_failed"}`,abort 抛 `aborted`。
|
|
8
|
+
*
|
|
9
|
+
* [W3-C1b] 会话前缀续写(第三波 §1.8):调用方给出 `continuation.prefix`(与上一次真实请求
|
|
10
|
+
* 逐字节同前缀的完整转录)时,请求 = 前缀 + 一条摘要指令(「只摘要第 1–N 条,第 N+1 条起
|
|
11
|
+
* 保留原文」+ 首行引文),`cacheRetention: "short"`、`purpose: "summary"`,按读价计费。
|
|
12
|
+
* **不发 `toolChoice`**:实测(docs/benchmarks/cache-2026-10-02.md E3)中转与 Kimi 在
|
|
13
|
+
* `tool_choice: "none"` 时渲染的提示不带工具定义,前缀在工具段断开、cacheRead 为 0;Anthropic
|
|
14
|
+
* 文档也写明改动 tool_choice 会让消息缓存失效。所以 system + tools + 消息前缀与上一次真实请求
|
|
15
|
+
* 逐字节相同,「不调用工具、只输出摘要」只写在末尾指令里;响应为空 / 被截断 / 含工具调用 / 请求出错 → 回落现行独立请求(序列化转录、
|
|
16
|
+
* `cacheRetention: "none"`)并经 `onFallback` 记 warning。
|
|
17
|
+
*/
|
|
18
|
+
import type { ApiImplementation, Model, StreamOptions, TranscriptContext, Usage } from "../ai/types.js";
|
|
19
|
+
import type { Projection } from "../session/projection.js";
|
|
20
|
+
import type { AgentMessage, FileOpsDetails } from "../session/types.js";
|
|
21
|
+
import { type CutPointResult } from "./cut-point.js";
|
|
22
|
+
export type StreamFn = ApiImplementation["stream"];
|
|
23
|
+
export declare const SUMMARY_MAX_TOKENS = 4096;
|
|
24
|
+
export declare const SUMMARIZATION_SYSTEM_PROMPT: string;
|
|
25
|
+
export declare const SUMMARY_TEMPLATE = "Write the checkpoint summary using EXACTLY this format:\n\n## Goal\n[What the user is trying to accomplish; several items if the session covers several tasks.]\n\n## Constraints & Preferences\n- [Constraints, preferences, or requirements stated by the user, or \"(none)\"]\n\n## Progress\n### Done\n- [x] [Completed tasks and changes]\n\n### In Progress\n- [ ] [Current work]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Short rationale]\n\n## Next Steps\n1. [Ordered list of what should happen next]\n\n## Critical Context\n- [Data, file paths, identifiers, error messages, or references needed to continue, or \"(none)\"]\n\nKeep every section concise. Preserve exact file paths, function names, and error messages.";
|
|
26
|
+
/** 续写指令的首句(测试据此识别摘要请求)。 */
|
|
27
|
+
export declare const SUMMARY_CONTINUATION_PREAMBLE = "Stop here: do not continue the conversation, do not answer questions from it and do not call any tools.";
|
|
28
|
+
/** 续写指令的末句:工具仍在请求里(为了缓存前缀),只靠指令禁止调用。 */
|
|
29
|
+
export declare const SUMMARY_CONTINUATION_TAIL = "Tools are still listed above, but you must not call any of them now. Reply with the summary text only.";
|
|
30
|
+
export interface CompactionPlan {
|
|
31
|
+
/** 要摘要的消息(不含 system 与上一份摘要)。 */
|
|
32
|
+
toSummarize: AgentMessage[];
|
|
33
|
+
/** split turn 时:回合前缀(同时也在 toSummarize 的末尾之后,不重叠)。 */
|
|
34
|
+
turnPrefix: AgentMessage[];
|
|
35
|
+
firstKeptEntryId: string;
|
|
36
|
+
cut: CutPointResult;
|
|
37
|
+
previousSummary: string | undefined;
|
|
38
|
+
previousDetails: FileOpsDetails | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* [W3-C1b] 续写用的位置:转录里(不计 system)历史段结束前、保留区开始前各有多少条
|
|
41
|
+
* 消息,以及紧随其后那条消息的首行引文。
|
|
42
|
+
*/
|
|
43
|
+
anchors: {
|
|
44
|
+
historyEnd: number;
|
|
45
|
+
keptStart: number;
|
|
46
|
+
historyExcerpt: string;
|
|
47
|
+
keptExcerpt: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface CompactionDraft {
|
|
51
|
+
summary: string;
|
|
52
|
+
firstKeptEntryId: string;
|
|
53
|
+
usage: Usage | undefined;
|
|
54
|
+
details: FileOpsDetails;
|
|
55
|
+
}
|
|
56
|
+
export interface SummarizerOptions {
|
|
57
|
+
stream: StreamFn;
|
|
58
|
+
model: Model;
|
|
59
|
+
apiKey?: string | undefined;
|
|
60
|
+
signal: AbortSignal;
|
|
61
|
+
customInstructions?: string | undefined;
|
|
62
|
+
maxTokens?: number;
|
|
63
|
+
/** [W3-C1b] 会话前缀续写的前缀与流选项(缺省 / 回落时走独立请求)。 */
|
|
64
|
+
continuation?: SummaryContinuation | undefined;
|
|
65
|
+
/** 续写失败回落独立请求时调用(记 warning)。 */
|
|
66
|
+
onFallback?(reason: string): void;
|
|
67
|
+
}
|
|
68
|
+
export interface SummaryContinuation {
|
|
69
|
+
/** 与上一次真实请求逐字节同前缀的完整转录(system 补丁 + 全部消息)。 */
|
|
70
|
+
prefix: TranscriptContext;
|
|
71
|
+
/** 沿用上一次真实请求的选项(apiKey、sessionId、thinkingLevel 等)。 */
|
|
72
|
+
streamOptions?: Partial<Omit<StreamOptions, "signal">>;
|
|
73
|
+
}
|
|
74
|
+
export interface ContinuationInput {
|
|
75
|
+
/** 与上一次真实请求逐字节相同的转录(system 补丁 + 全部消息,直到最后一条真实请求为止)。 */
|
|
76
|
+
prefix: TranscriptContext;
|
|
77
|
+
/** 摘要到第 index 条为止(不计 system);excerpt 是第 index + 1 条的首行引文。 */
|
|
78
|
+
keepFrom: {
|
|
79
|
+
index: number;
|
|
80
|
+
excerpt: string;
|
|
81
|
+
};
|
|
82
|
+
/** 只摘要第 from + 1 条起(split turn 的回合前缀、分支摘要);缺省从头。 */
|
|
83
|
+
from?: number;
|
|
84
|
+
/** SUMMARY_TEMPLATE / 回合前缀提示 / 分支摘要模板。 */
|
|
85
|
+
instruction: string;
|
|
86
|
+
}
|
|
87
|
+
/** 转录里(不计 system)实际发给模型的消息数:失败 / 中断的助手消息连同其工具结果不回放。 */
|
|
88
|
+
export declare function countLlmMessages(messages: readonly AgentMessage[]): number;
|
|
89
|
+
/** 消息首行引文(≤ 80 字符)。 */
|
|
90
|
+
export declare function excerptOf(message: AgentMessage | undefined): string;
|
|
91
|
+
/** 无可摘要内容时返回 undefined。 */
|
|
92
|
+
export declare function prepareCompaction(projection: Projection, keepRecentTokens: number): CompactionPlan | undefined;
|
|
93
|
+
/** 一次无工具的文本补全;错误 / 取消 / 空输出抛错。 */
|
|
94
|
+
export declare function completeText(options: SummarizerOptions, userText: string): Promise<{
|
|
95
|
+
text: string;
|
|
96
|
+
usage: Usage;
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* 会话前缀续写:前缀 + 一条摘要指令,不发 `toolChoice`(保住含工具段的缓存前缀)。空 / 截断 /
|
|
100
|
+
* 含工具调用 / 出错抛 `compaction_failed`(调用方回落),abort 抛 `aborted`。
|
|
101
|
+
*/
|
|
102
|
+
export declare function completeByContinuation(options: SummarizerOptions, input: ContinuationInput): Promise<{
|
|
103
|
+
text: string;
|
|
104
|
+
usage: Usage;
|
|
105
|
+
}>;
|
|
106
|
+
/** 先试续写,失败(非 abort)回落独立请求。 */
|
|
107
|
+
export declare function summarizeWithFallback(options: SummarizerOptions, continuation: Omit<ContinuationInput, "prefix"> | undefined, independentPrompt: () => string): Promise<{
|
|
108
|
+
text: string;
|
|
109
|
+
usage: Usage;
|
|
110
|
+
}>;
|
|
111
|
+
export declare function runCompaction(plan: CompactionPlan, options: SummarizerOptions): Promise<CompactionDraft>;
|