@armadra/agent 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -1
- package/README.md +202 -83
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/dist/acp.d.ts +18 -0
- package/dist/acp.js +19 -0
- package/dist/agent/limits.d.ts +38 -0
- package/dist/agent/limits.js +127 -0
- package/dist/agent/loop-guard.d.ts +38 -0
- package/dist/agent/loop-guard.js +95 -0
- package/dist/agent/loop.d.ts +4 -0
- package/dist/agent/loop.js +7 -0
- package/dist/agent/prompt-rules.d.ts +12 -0
- package/dist/agent/prompt-rules.js +18 -0
- package/dist/agent/reminders.d.ts +59 -0
- package/dist/agent/reminders.js +327 -0
- package/dist/agent/session-cache.d.ts +5 -0
- package/dist/agent/session-cache.js +17 -1
- package/dist/agent/session-classifier.d.ts +1 -1
- package/dist/agent/session-classifier.js +3 -1
- package/dist/agent/session-compaction.d.ts +35 -6
- package/dist/agent/session-compaction.js +157 -31
- package/dist/agent/session-core.d.ts +27 -1
- package/dist/agent/session-extensions.d.ts +61 -0
- package/dist/agent/session-extensions.js +129 -0
- package/dist/agent/session-images.d.ts +23 -0
- package/dist/agent/session-images.js +97 -0
- package/dist/agent/session-plan.d.ts +21 -0
- package/dist/agent/session-plan.js +524 -0
- package/dist/agent/session-rewind.d.ts +81 -0
- package/dist/agent/session-rewind.js +354 -0
- package/dist/agent/session-run.d.ts +28 -1
- package/dist/agent/session-run.js +157 -6
- package/dist/agent/session-settings.d.ts +49 -0
- package/dist/agent/session-settings.js +95 -0
- package/dist/agent/session-subagent.d.ts +36 -24
- package/dist/agent/session-subagent.js +259 -129
- package/dist/agent/session-sync.d.ts +10 -3
- package/dist/agent/session-sync.js +37 -8
- package/dist/agent/session-telemetry.d.ts +39 -0
- package/dist/agent/session-telemetry.js +231 -0
- package/dist/agent/session-tools.js +40 -1
- package/dist/agent/session.d.ts +30 -29
- package/dist/agent/session.js +79 -123
- package/dist/agent/subagent-registry.d.ts +91 -0
- package/dist/agent/subagent-registry.js +494 -0
- package/dist/agent/system-prompt.d.ts +6 -3
- package/dist/agent/system-prompt.js +7 -2
- package/dist/agent/tool-runner.d.ts +8 -1
- package/dist/agent/tool-runner.js +57 -4
- package/dist/agent/types-w5.d.ts +192 -0
- package/dist/agent/types-w5.js +10 -0
- package/dist/agent/types.d.ts +47 -4
- package/dist/agent/types.js +2 -0
- package/dist/agent/worktree.d.ts +36 -0
- package/dist/agent/worktree.js +86 -0
- package/dist/agents/builtin.d.ts +14 -0
- package/dist/agents/builtin.js +39 -0
- package/dist/agents/catalog.d.ts +43 -0
- package/dist/agents/catalog.js +82 -0
- package/dist/agents/discover.d.ts +35 -0
- package/dist/agents/discover.js +83 -0
- package/dist/agents/external.d.ts +82 -0
- package/dist/agents/external.js +326 -0
- package/dist/agents/parse.d.ts +18 -0
- package/dist/agents/parse.js +116 -0
- package/dist/agents/result.d.ts +32 -0
- package/dist/agents/result.js +67 -0
- package/dist/agents/task-control.d.ts +17 -0
- package/dist/agents/task-control.js +17 -0
- package/dist/agents/task-record.d.ts +78 -0
- package/dist/agents/task-record.js +141 -0
- package/dist/agents/types.d.ts +46 -0
- package/dist/agents/types.js +8 -0
- package/dist/ai/apis/anthropic-compat.d.ts +36 -0
- package/dist/ai/apis/anthropic-compat.js +92 -0
- package/dist/ai/apis/anthropic-request.d.ts +7 -6
- package/dist/ai/apis/anthropic-request.js +10 -28
- package/dist/ai/apis/cache-params.d.ts +11 -2
- package/dist/ai/apis/cache-params.js +25 -6
- package/dist/ai/apis/openai-compat.d.ts +4 -1
- package/dist/ai/apis/openai-compat.js +8 -1
- package/dist/ai/image-limits.d.ts +45 -0
- package/dist/ai/image-limits.js +68 -0
- package/dist/ai/providers/builtin.d.ts +18 -6
- package/dist/ai/providers/builtin.js +135 -6
- package/dist/ai/providers/catalog-data.js +17 -13
- package/dist/ai/providers/catalog.d.ts +60 -7
- package/dist/ai/providers/catalog.js +197 -38
- package/dist/ai/providers/channels.d.ts +33 -3
- package/dist/ai/providers/channels.js +108 -7
- package/dist/ai/providers/enrich.d.ts +6 -3
- package/dist/ai/providers/enrich.js +19 -4
- package/dist/ai/providers/models-dev-cache.d.ts +28 -24
- package/dist/ai/providers/models-dev-cache.js +92 -78
- package/dist/ai/providers/models-dev-data.d.ts +7 -0
- package/dist/ai/providers/models-dev-data.js +32 -0
- package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
- package/dist/ai/providers/models-dev-snapshot.js +137 -0
- package/dist/ai/providers/models-dev.d.ts +51 -10
- package/dist/ai/providers/models-dev.js +132 -39
- package/dist/ai/providers/registry.d.ts +20 -10
- package/dist/ai/providers/registry.js +82 -43
- package/dist/ai/types.d.ts +28 -2
- package/dist/ai/types.js +5 -0
- package/dist/bundle/ama.cjs +37605 -21619
- package/dist/checkpoints/backend.d.ts +61 -0
- package/dist/checkpoints/backend.js +135 -0
- package/dist/checkpoints/blobs.d.ts +60 -0
- package/dist/checkpoints/blobs.js +203 -0
- package/dist/checkpoints/gc.d.ts +47 -0
- package/dist/checkpoints/gc.js +144 -0
- package/dist/checkpoints/git-head.d.ts +15 -0
- package/dist/checkpoints/git-head.js +89 -0
- package/dist/checkpoints/index.d.ts +17 -0
- package/dist/checkpoints/index.js +17 -0
- package/dist/checkpoints/replay.d.ts +48 -0
- package/dist/checkpoints/replay.js +154 -0
- package/dist/checkpoints/restore.d.ts +86 -0
- package/dist/checkpoints/restore.js +263 -0
- package/dist/checkpoints/settings.d.ts +14 -0
- package/dist/checkpoints/settings.js +23 -0
- package/dist/checkpoints/shadow-git.d.ts +108 -0
- package/dist/checkpoints/shadow-git.js +491 -0
- package/dist/checkpoints/shadow-restore.d.ts +25 -0
- package/dist/checkpoints/shadow-restore.js +128 -0
- package/dist/checkpoints/tracker.d.ts +64 -0
- package/dist/checkpoints/tracker.js +192 -0
- package/dist/checkpoints/types.d.ts +99 -0
- package/dist/checkpoints/types.js +12 -0
- package/dist/cli/args.d.ts +8 -4
- package/dist/cli/args.js +17 -97
- package/dist/cli/bootstrap.js +13 -1
- package/dist/cli/choice-prompt.d.ts +58 -0
- package/dist/cli/choice-prompt.js +140 -0
- package/dist/cli/codemode-notice.d.ts +3 -2
- package/dist/cli/codemode-notice.js +3 -2
- package/dist/cli/compose-agents.d.ts +27 -0
- package/dist/cli/compose-agents.js +111 -0
- package/dist/cli/compose-extensions.d.ts +29 -0
- package/dist/cli/compose-extensions.js +44 -0
- package/dist/cli/compose-session.d.ts +3 -0
- package/dist/cli/compose-session.js +47 -4
- package/dist/cli/compose.d.ts +3 -0
- package/dist/cli/compose.js +33 -6
- package/dist/cli/deps.d.ts +6 -0
- package/dist/cli/exit-codes.d.ts +10 -0
- package/dist/cli/exit-codes.js +12 -0
- package/dist/cli/help-text.d.ts +4 -0
- package/dist/cli/help-text.js +102 -0
- package/dist/cli/startup-screen.js +5 -3
- package/dist/cli/startup-steps.js +7 -1
- package/dist/cli/subcommands/config.d.ts +4 -1
- package/dist/cli/subcommands/config.js +17 -6
- package/dist/cli/subcommands/doctor.js +21 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
- package/dist/cli/subcommands/models-cache-probe.js +3 -12
- package/dist/cli/subcommands/models-discover.js +5 -6
- package/dist/cli/subcommands/models.d.ts +2 -0
- package/dist/cli/subcommands/models.js +33 -14
- package/dist/cli/subcommands/providers.d.ts +1 -1
- package/dist/cli/subcommands/providers.js +8 -15
- package/dist/cli/subcommands/sessions.d.ts +3 -1
- package/dist/cli/subcommands/sessions.js +36 -2
- package/dist/codemode/capability.d.ts +24 -7
- package/dist/codemode/capability.js +38 -10
- package/dist/codemode/host-side.d.ts +14 -0
- package/dist/codemode/host-side.js +24 -1
- package/dist/codemode/tool.d.ts +1 -1
- package/dist/codemode/tool.js +6 -4
- package/dist/compaction/breaker.d.ts +25 -9
- package/dist/compaction/breaker.js +47 -17
- package/dist/compaction/estimate.d.ts +11 -3
- package/dist/compaction/estimate.js +64 -22
- package/dist/compaction/image-budget.d.ts +45 -0
- package/dist/compaction/image-budget.js +115 -0
- package/dist/compaction/post-compact.d.ts +38 -0
- package/dist/compaction/post-compact.js +152 -0
- package/dist/compaction/protect.d.ts +27 -0
- package/dist/compaction/protect.js +56 -0
- package/dist/compaction/prune-tier.d.ts +58 -12
- package/dist/compaction/prune-tier.js +120 -54
- package/dist/compaction/summarize-tier.d.ts +23 -4
- package/dist/compaction/summarize-tier.js +100 -29
- package/dist/config/checker.d.ts +28 -0
- package/dist/config/checker.js +97 -0
- package/dist/config/json-schema.js +63 -2
- package/dist/config/key-docs.js +72 -3
- package/dist/config/merge.d.ts +5 -3
- package/dist/config/merge.js +81 -7
- package/dist/config/profile.d.ts +2 -0
- package/dist/config/profile.js +2 -0
- package/dist/config/schema-w5.d.ts +18 -0
- package/dist/config/schema-w5.js +109 -0
- package/dist/config/schema.d.ts +5 -9
- package/dist/config/schema.js +68 -114
- package/dist/config/types-w5.d.ts +118 -0
- package/dist/config/types-w5.js +20 -0
- package/dist/config/types.d.ts +50 -1
- package/dist/config/types.js +12 -0
- package/dist/drivers/acp/client.d.ts +57 -0
- package/dist/drivers/acp/client.js +160 -0
- package/dist/drivers/acp/driver.d.ts +31 -0
- package/dist/drivers/acp/driver.js +274 -0
- package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
- package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
- package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
- package/dist/drivers/acp/testing/fake-agent.js +211 -0
- package/dist/drivers/acp/types.d.ts +285 -0
- package/dist/drivers/acp/types.js +36 -0
- package/dist/drivers/agents.d.ts +58 -0
- package/dist/drivers/agents.js +142 -0
- package/dist/drivers/base.d.ts +35 -0
- package/dist/drivers/base.js +59 -0
- package/dist/drivers/catalog.d.ts +46 -0
- package/dist/drivers/catalog.js +167 -0
- package/dist/drivers/env.d.ts +20 -0
- package/dist/drivers/env.js +46 -0
- package/dist/drivers/host-runners.d.ts +18 -0
- package/dist/drivers/host-runners.js +45 -0
- package/dist/drivers/jsonrpc.d.ts +58 -0
- package/dist/drivers/jsonrpc.js +180 -0
- package/dist/drivers/native/claude-normalize.d.ts +46 -0
- package/dist/drivers/native/claude-normalize.js +106 -0
- package/dist/drivers/native/claude-stream.d.ts +29 -0
- package/dist/drivers/native/claude-stream.js +456 -0
- package/dist/drivers/native/codex-app-server.d.ts +26 -0
- package/dist/drivers/native/codex-app-server.js +407 -0
- package/dist/drivers/native/codex-normalize.d.ts +38 -0
- package/dist/drivers/native/codex-normalize.js +124 -0
- package/dist/drivers/native/oneshot.d.ts +28 -0
- package/dist/drivers/native/oneshot.js +312 -0
- package/dist/drivers/permissions.d.ts +40 -0
- package/dist/drivers/permissions.js +92 -0
- package/dist/drivers/pids.d.ts +32 -0
- package/dist/drivers/pids.js +93 -0
- package/dist/drivers/pool.d.ts +28 -0
- package/dist/drivers/pool.js +96 -0
- package/dist/drivers/probe.d.ts +47 -0
- package/dist/drivers/probe.js +172 -0
- package/dist/drivers/process.d.ts +48 -0
- package/dist/drivers/process.js +100 -0
- package/dist/drivers/runner.d.ts +61 -0
- package/dist/drivers/runner.js +388 -0
- package/dist/drivers/store.d.ts +49 -0
- package/dist/drivers/store.js +91 -0
- package/dist/drivers/turn.d.ts +41 -0
- package/dist/drivers/turn.js +99 -0
- package/dist/drivers/types.d.ts +122 -0
- package/dist/drivers/types.js +11 -0
- package/dist/git/info.d.ts +75 -0
- package/dist/git/info.js +191 -0
- package/dist/hooks/protocol.js +6 -1
- package/dist/hooks/types.d.ts +10 -1
- package/dist/hooks/types.js +2 -0
- package/dist/host/api-impl.d.ts +7 -0
- package/dist/host/api-impl.js +15 -0
- package/dist/host/types.d.ts +24 -2
- package/dist/host/types.js +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/modes/acp/acp-events.d.ts +46 -0
- package/dist/modes/acp/acp-events.js +233 -0
- package/dist/modes/acp/acp-mode.d.ts +17 -0
- package/dist/modes/acp/acp-mode.js +47 -0
- package/dist/modes/acp/acp-server.d.ts +79 -0
- package/dist/modes/acp/acp-server.js +315 -0
- package/dist/modes/commands-core.d.ts +13 -1
- package/dist/modes/commands-core.js +64 -0
- package/dist/modes/image-input.d.ts +12 -4
- package/dist/modes/image-input.js +22 -5
- package/dist/modes/interactive/agent-panels.d.ts +14 -0
- package/dist/modes/interactive/agent-panels.js +103 -0
- package/dist/modes/interactive/agent-ui.d.ts +73 -0
- package/dist/modes/interactive/agent-ui.js +234 -0
- package/dist/modes/interactive/approval-dialog.d.ts +21 -4
- package/dist/modes/interactive/approval-dialog.js +98 -20
- package/dist/modes/interactive/approval-merge.d.ts +41 -0
- package/dist/modes/interactive/approval-merge.js +89 -0
- package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
- package/dist/modes/interactive/clipboard-paste.js +28 -0
- package/dist/modes/interactive/commands.d.ts +12 -1
- package/dist/modes/interactive/commands.js +31 -1
- package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
- package/dist/modes/interactive/confirm-dialog.js +121 -0
- package/dist/modes/interactive/double-esc.d.ts +20 -0
- package/dist/modes/interactive/double-esc.js +34 -0
- package/dist/modes/interactive/event-notices.d.ts +32 -0
- package/dist/modes/interactive/event-notices.js +79 -0
- package/dist/modes/interactive/external-editor.d.ts +16 -0
- package/dist/modes/interactive/external-editor.js +39 -0
- package/dist/modes/interactive/interactive-mode.d.ts +14 -1
- package/dist/modes/interactive/interactive-mode.js +140 -136
- package/dist/modes/interactive/key-dispatch.d.ts +22 -2
- package/dist/modes/interactive/key-dispatch.js +66 -7
- package/dist/modes/interactive/line/line-mode.d.ts +4 -1
- package/dist/modes/interactive/line/line-mode.js +31 -5
- package/dist/modes/interactive/line/line-render.d.ts +2 -1
- package/dist/modes/interactive/line/line-render.js +40 -4
- package/dist/modes/interactive/message-view.d.ts +1 -1
- package/dist/modes/interactive/message-view.js +24 -2
- package/dist/modes/interactive/panels.d.ts +18 -1
- package/dist/modes/interactive/panels.js +19 -5
- package/dist/modes/interactive/plan-command.d.ts +40 -0
- package/dist/modes/interactive/plan-command.js +129 -0
- package/dist/modes/interactive/plan-dialog.d.ts +98 -0
- package/dist/modes/interactive/plan-dialog.js +290 -0
- package/dist/modes/interactive/plan-flow.d.ts +34 -0
- package/dist/modes/interactive/plan-flow.js +98 -0
- package/dist/modes/interactive/rewind-command.d.ts +31 -0
- package/dist/modes/interactive/rewind-command.js +106 -0
- package/dist/modes/interactive/rewind-flow.d.ts +37 -0
- package/dist/modes/interactive/rewind-flow.js +151 -0
- package/dist/modes/interactive/rewind-list.d.ts +36 -0
- package/dist/modes/interactive/rewind-list.js +123 -0
- package/dist/modes/interactive/rewind-panel.d.ts +77 -0
- package/dist/modes/interactive/rewind-panel.js +309 -0
- package/dist/modes/interactive/rewind-text.d.ts +40 -0
- package/dist/modes/interactive/rewind-text.js +139 -0
- package/dist/modes/interactive/session-events.d.ts +24 -0
- package/dist/modes/interactive/session-events.js +96 -0
- package/dist/modes/interactive/startup-ui.js +5 -1
- package/dist/modes/interactive/status-area.d.ts +73 -0
- package/dist/modes/interactive/status-area.js +190 -0
- package/dist/modes/interactive/status-bar.d.ts +99 -11
- package/dist/modes/interactive/status-bar.js +273 -90
- package/dist/modes/interactive/status-line.d.ts +33 -0
- package/dist/modes/interactive/status-line.js +113 -0
- package/dist/modes/interactive/subagent-view.d.ts +56 -0
- package/dist/modes/interactive/subagent-view.js +154 -0
- package/dist/modes/interactive/tasks-report.d.ts +27 -0
- package/dist/modes/interactive/tasks-report.js +114 -0
- package/dist/modes/interactive/tool-view.d.ts +12 -2
- package/dist/modes/interactive/tool-view.js +34 -4
- package/dist/modes/print/print-mode.d.ts +13 -4
- package/dist/modes/print/print-mode.js +56 -12
- package/dist/modes/rpc/commands.d.ts +9 -0
- package/dist/modes/rpc/commands.js +44 -0
- package/dist/modes/rpc/rpc-mode.js +7 -1
- package/dist/modes/session-report.d.ts +11 -0
- package/dist/modes/session-report.js +48 -1
- package/dist/permissions/auto-safe.d.ts +5 -0
- package/dist/permissions/auto-safe.js +18 -0
- package/dist/permissions/bypass.d.ts +32 -0
- package/dist/permissions/bypass.js +36 -0
- package/dist/permissions/classifier.d.ts +4 -0
- package/dist/permissions/classifier.js +2 -0
- package/dist/permissions/modes.js +1 -1
- package/dist/permissions/pipeline.d.ts +43 -4
- package/dist/permissions/pipeline.js +114 -9
- package/dist/permissions/readonly-bash.d.ts +30 -0
- package/dist/permissions/readonly-bash.js +118 -0
- package/dist/permissions/types.d.ts +42 -0
- package/dist/plan/compose.d.ts +9 -0
- package/dist/plan/compose.js +19 -0
- package/dist/plan/controller.d.ts +71 -0
- package/dist/plan/controller.js +27 -0
- package/dist/plan/done-markers.d.ts +17 -0
- package/dist/plan/done-markers.js +51 -0
- package/dist/plan/extract.d.ts +29 -0
- package/dist/plan/extract.js +160 -0
- package/dist/plan/prompts.d.ts +36 -0
- package/dist/plan/prompts.js +89 -0
- package/dist/plan/store.d.ts +37 -0
- package/dist/plan/store.js +90 -0
- package/dist/rpc.d.ts +58 -2
- package/dist/rpc.js +7 -0
- package/dist/sandbox/bash.d.ts +74 -0
- package/dist/sandbox/bash.js +126 -0
- package/dist/sandbox/detect.d.ts +48 -0
- package/dist/sandbox/detect.js +149 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/profile.d.ts +33 -0
- package/dist/sandbox/profile.js +76 -0
- package/dist/sandbox/wrap.d.ts +29 -0
- package/dist/sandbox/wrap.js +117 -0
- package/dist/sdk.d.ts +33 -2
- package/dist/sdk.js +24 -2
- package/dist/session/types.d.ts +3 -1
- package/dist/session/types.js +1 -0
- package/dist/skills/builtin.d.ts +31 -0
- package/dist/skills/builtin.js +109 -0
- package/dist/skills/discover.d.ts +2 -1
- package/dist/skills/index-prompt.js +5 -8
- package/dist/tools/background-jobs.d.ts +71 -0
- package/dist/tools/background-jobs.js +198 -0
- package/dist/tools/bash.d.ts +53 -1
- package/dist/tools/bash.js +158 -7
- package/dist/tools/clipboard-image.d.ts +44 -0
- package/dist/tools/clipboard-image.js +148 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.js +18 -3
- package/dist/tools/image-file.d.ts +38 -3
- package/dist/tools/image-file.js +81 -10
- package/dist/tools/image-resize.d.ts +58 -0
- package/dist/tools/image-resize.js +97 -0
- package/dist/tools/presets.d.ts +7 -2
- package/dist/tools/presets.js +11 -4
- package/dist/tools/read.d.ts +5 -2
- package/dist/tools/read.js +13 -10
- package/dist/tools/registry.d.ts +7 -1
- package/dist/tools/registry.js +27 -4
- package/dist/tools/task-ctl.d.ts +22 -0
- package/dist/tools/task-ctl.js +116 -0
- package/dist/tools/task.d.ts +22 -10
- package/dist/tools/task.js +66 -26
- package/dist/tools/todo.d.ts +22 -2
- package/dist/tools/todo.js +58 -14
- package/dist/tools/truncate.d.ts +15 -0
- package/dist/tools/truncate.js +25 -0
- package/dist/tools/types.d.ts +113 -0
- package/dist/tools/types.js +5 -0
- package/dist/tools/write.js +3 -0
- package/dist/tui/glyphs.d.ts +4 -0
- package/dist/tui/glyphs.js +4 -0
- package/dist/tui/keybindings.d.ts +6 -1
- package/dist/tui/keybindings.js +6 -1
- package/docs/acp.md +78 -0
- package/docs/agents.md +250 -0
- package/docs/codemode.md +12 -8
- package/docs/hooks.md +26 -22
- package/docs/permissions.md +78 -13
- package/docs/plan.md +107 -0
- package/docs/providers.md +228 -52
- package/docs/rewind-plan.md +181 -0
- package/docs/rpc.md +92 -5
- package/docs/sandbox.md +215 -0
- package/docs/session-format.md +41 -21
- package/docs/sessions.md +41 -1
- package/docs/tui-design.md +751 -0
- package/docs/tui.md +179 -21
- package/package.json +13 -2
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 压缩熔断(设计 §9
|
|
2
|
+
* 压缩熔断(设计 §9「熔断」,wave5 §8.2 C5)。[B2][W5-H1]
|
|
3
3
|
*
|
|
4
4
|
* - 无 `contextWindow` 或配置关闭 → 自动压缩关闭(手动 /compact 仍可用)。
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
5
|
+
* - 不再限制「每 run 档二 ≤ 1 次」(单提示长任务需要多次摘要);改为两种跳闸:
|
|
6
|
+
* - 连续 3 次摘要失败;
|
|
7
|
+
* - 快速回填:连续 3 次摘要都在上一次摘要后不到 3 回合就又需要摘要(压缩不动、越压越满)。
|
|
8
|
+
* 跳闸后关闭自动压缩直到 `reset()`(例如用户手动压缩成功)。
|
|
9
|
+
* - 固定前缀(system + 工具表)已超过预算:摘要无济于事,直接告警、不再尝试(`prefix_overflow`,
|
|
10
|
+
* 由调用方每次检查时更新)。
|
|
11
|
+
* - 「nothing to compact」不计失败(调用方不调 `recordSummary`)。
|
|
7
12
|
* - 溢出恢复:压缩后估算仍 > 0.8 × window → 不重试。
|
|
8
13
|
*/
|
|
9
|
-
export const
|
|
10
|
-
|
|
14
|
+
export const MAX_CONSECUTIVE_FAILURES = 3;
|
|
15
|
+
/** 上一次摘要后不到这么多回合又需要摘要,记一次快速回填。 */
|
|
16
|
+
export const RAPID_REFILL_TURNS = 3;
|
|
17
|
+
export const MAX_RAPID_REFILLS = 3;
|
|
11
18
|
export const RETRY_AFTER_COMPACTION_RATIO = 0.8;
|
|
12
19
|
export class CompactionBreaker {
|
|
13
|
-
summariesThisRun = 0;
|
|
14
20
|
consecutiveFailures = 0;
|
|
21
|
+
rapidRefills = 0;
|
|
22
|
+
turn = 0;
|
|
23
|
+
lastSummaryTurn;
|
|
24
|
+
prefixOverflow = false;
|
|
15
25
|
enabled;
|
|
16
26
|
contextWindow;
|
|
17
27
|
constructor(enabled, contextWindow) {
|
|
@@ -22,15 +32,23 @@ export class CompactionBreaker {
|
|
|
22
32
|
this.enabled = enabled;
|
|
23
33
|
this.contextWindow = contextWindow;
|
|
24
34
|
}
|
|
35
|
+
/** 连续失败或快速回填跳闸(溢出恢复也不再尝试)。 */
|
|
25
36
|
get tripped() {
|
|
26
|
-
return this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES;
|
|
37
|
+
return (this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES || this.rapidRefills >= MAX_RAPID_REFILLS);
|
|
27
38
|
}
|
|
28
|
-
/**
|
|
39
|
+
/** 自动压缩整体是否开启。 */
|
|
29
40
|
get autoEnabled() {
|
|
30
41
|
return this.enabled && this.contextWindow !== undefined && !this.tripped;
|
|
31
42
|
}
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
/** 新 run 开始(会话在 agent_start 时调用);不再重置任何计数,保留以兼容调用点。 */
|
|
44
|
+
startRun() { }
|
|
45
|
+
/** 每次请求前的阈值检查调用一次:回合计数(快速回填按它判断)。 */
|
|
46
|
+
tick() {
|
|
47
|
+
this.turn++;
|
|
48
|
+
}
|
|
49
|
+
/** 固定前缀是否已超预算(调用方每次准备摘要前更新)。 */
|
|
50
|
+
setPrefixOverflow(overflow) {
|
|
51
|
+
this.prefixOverflow = overflow;
|
|
34
52
|
}
|
|
35
53
|
/** 自动档二能否执行;不能则给出原因。 */
|
|
36
54
|
blockReason() {
|
|
@@ -38,23 +56,35 @@ export class CompactionBreaker {
|
|
|
38
56
|
return "disabled";
|
|
39
57
|
if (this.contextWindow === undefined)
|
|
40
58
|
return "no_window";
|
|
41
|
-
if (this.
|
|
59
|
+
if (this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES)
|
|
42
60
|
return "tripped";
|
|
43
|
-
if (this.
|
|
44
|
-
return "
|
|
61
|
+
if (this.rapidRefills >= MAX_RAPID_REFILLS)
|
|
62
|
+
return "rapid_refill";
|
|
63
|
+
if (this.prefixOverflow)
|
|
64
|
+
return "prefix_overflow";
|
|
45
65
|
return undefined;
|
|
46
66
|
}
|
|
47
67
|
canSummarize() {
|
|
48
68
|
return this.blockReason() === undefined;
|
|
49
69
|
}
|
|
50
70
|
recordSummary(success) {
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
if (!success) {
|
|
72
|
+
this.consecutiveFailures++;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.consecutiveFailures = 0;
|
|
76
|
+
const last = this.lastSummaryTurn;
|
|
77
|
+
if (last !== undefined && this.turn - last < RAPID_REFILL_TURNS)
|
|
78
|
+
this.rapidRefills++;
|
|
79
|
+
else
|
|
80
|
+
this.rapidRefills = 0;
|
|
81
|
+
this.lastSummaryTurn = this.turn;
|
|
53
82
|
}
|
|
54
|
-
/**
|
|
83
|
+
/** 手动压缩成功等场景:清零全部计数。 */
|
|
55
84
|
reset() {
|
|
56
85
|
this.consecutiveFailures = 0;
|
|
57
|
-
this.
|
|
86
|
+
this.rapidRefills = 0;
|
|
87
|
+
this.lastSummaryTurn = undefined;
|
|
58
88
|
}
|
|
59
89
|
/** 溢出恢复后是否值得重试。 */
|
|
60
90
|
shouldRetryAfterCompaction(tokensAfter) {
|
|
@@ -2,15 +2,23 @@
|
|
|
2
2
|
* 上下文 token 估算(设计 §9「估算」)。[B2]
|
|
3
3
|
*
|
|
4
4
|
* `contextTokens` = 最后一条非 error / aborted 助手消息的 `totalTokens`(缺则
|
|
5
|
-
* `input + output + cacheRead + cacheWrite`,**含 output**)+
|
|
5
|
+
* `input + output + cacheRead + cacheWrite`,**含 output**)+ 其后消息的估算(图片 1 600)。
|
|
6
6
|
* 若该 usage 之后活动分支上出现过 `context_edit` 或 `compaction`,usage 不再可信,按投影全量重估。
|
|
7
|
+
*
|
|
8
|
+
* [W5-H1] 文本估算按脚本区分(wave5 D28 / C10):CJK 统一表意文字(含扩展区与兼容区)、假名、
|
|
9
|
+
* 谚文、CJK 符号与全角形式每字 1 token,其余字符合计 / 4 向上取整。「字符 / 4」对中文低估 3–4 倍,
|
|
10
|
+
* 中文会话会晚触发直至溢出;所有阈值(档一、档二、保留区)都用这个口径。
|
|
7
11
|
*/
|
|
8
|
-
import type { Usage } from "../ai/types.js";
|
|
12
|
+
import type { ContentBlock, Usage } from "../ai/types.js";
|
|
9
13
|
import type { ContextItem } from "../session/projection.js";
|
|
10
14
|
import type { AgentMessage, SessionEntry } from "../session/types.js";
|
|
11
15
|
export declare const IMAGE_TOKENS = 1600;
|
|
16
|
+
/** 文本的 token 估算:CJK / 假名 / 谚文每字 1,其余字符 / 4(向上取整)。 */
|
|
17
|
+
export declare function estimateTextTokens(text: string): number;
|
|
12
18
|
export declare function calculateContextTokens(usage: Usage): number;
|
|
13
|
-
/**
|
|
19
|
+
/** 内容的估算(文本按脚本,图片每张 1 600)。 */
|
|
20
|
+
export declare function estimateContentTokens(content: string | readonly ContentBlock[]): number;
|
|
21
|
+
/** 单条消息的估算(文本按脚本区分,图片每张 1 600)。 */
|
|
14
22
|
export declare function estimateMessageTokens(message: AgentMessage): number;
|
|
15
23
|
export declare function estimateMessagesTokens(messages: readonly AgentMessage[]): number;
|
|
16
24
|
export interface ContextEstimate {
|
|
@@ -2,64 +2,106 @@
|
|
|
2
2
|
* 上下文 token 估算(设计 §9「估算」)。[B2]
|
|
3
3
|
*
|
|
4
4
|
* `contextTokens` = 最后一条非 error / aborted 助手消息的 `totalTokens`(缺则
|
|
5
|
-
* `input + output + cacheRead + cacheWrite`,**含 output**)+
|
|
5
|
+
* `input + output + cacheRead + cacheWrite`,**含 output**)+ 其后消息的估算(图片 1 600)。
|
|
6
6
|
* 若该 usage 之后活动分支上出现过 `context_edit` 或 `compaction`,usage 不再可信,按投影全量重估。
|
|
7
|
+
*
|
|
8
|
+
* [W5-H1] 文本估算按脚本区分(wave5 D28 / C10):CJK 统一表意文字(含扩展区与兼容区)、假名、
|
|
9
|
+
* 谚文、CJK 符号与全角形式每字 1 token,其余字符合计 / 4 向上取整。「字符 / 4」对中文低估 3–4 倍,
|
|
10
|
+
* 中文会话会晚触发直至溢出;所有阈值(档一、档二、保留区)都用这个口径。
|
|
7
11
|
*/
|
|
8
12
|
export const IMAGE_TOKENS = 1600;
|
|
9
13
|
const CHARS_PER_TOKEN = 4;
|
|
14
|
+
/** 按 1 token / 字计的码位(BMP 内)。 */
|
|
15
|
+
function isWideCode(code) {
|
|
16
|
+
return ((code >= 0x4e00 && code <= 0x9fff) || // CJK 统一表意文字
|
|
17
|
+
(code >= 0x3400 && code <= 0x4dbf) || // 扩展 A
|
|
18
|
+
(code >= 0xf900 && code <= 0xfaff) || // 兼容表意文字
|
|
19
|
+
(code >= 0x3040 && code <= 0x30ff) || // 平假名、片假名
|
|
20
|
+
(code >= 0x31f0 && code <= 0x31ff) || // 片假名音标扩展
|
|
21
|
+
(code >= 0xac00 && code <= 0xd7af) || // 谚文音节
|
|
22
|
+
(code >= 0x1100 && code <= 0x11ff) || // 谚文字母
|
|
23
|
+
(code >= 0x3130 && code <= 0x318f) || // 谚文兼容字母
|
|
24
|
+
(code >= 0x3000 && code <= 0x303f) || // CJK 符号与标点
|
|
25
|
+
(code >= 0xff00 && code <= 0xffef) // 半角与全角形式
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
/** 文本的 token 估算:CJK / 假名 / 谚文每字 1,其余字符 / 4(向上取整)。 */
|
|
29
|
+
export function estimateTextTokens(text) {
|
|
30
|
+
return tokensOf(countText(text, { wide: 0, narrow: 0 }));
|
|
31
|
+
}
|
|
32
|
+
function countText(text, into) {
|
|
33
|
+
let wide = 0;
|
|
34
|
+
let narrow = 0;
|
|
35
|
+
for (let i = 0; i < text.length; i++) {
|
|
36
|
+
const code = text.charCodeAt(i);
|
|
37
|
+
if (code < 0x1100)
|
|
38
|
+
narrow++;
|
|
39
|
+
else if (code >= 0xd840 && code <= 0xd87f) {
|
|
40
|
+
// 补充平面 CJK 扩展 B–F(U+20000–U+2FFFF)的代理对:整对算 1 个字
|
|
41
|
+
wide++;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else if (isWideCode(code))
|
|
45
|
+
wide++;
|
|
46
|
+
else
|
|
47
|
+
narrow++;
|
|
48
|
+
}
|
|
49
|
+
into.wide += wide;
|
|
50
|
+
into.narrow += narrow;
|
|
51
|
+
return into;
|
|
52
|
+
}
|
|
53
|
+
function tokensOf(count) {
|
|
54
|
+
return count.wide + Math.ceil(count.narrow / CHARS_PER_TOKEN);
|
|
55
|
+
}
|
|
10
56
|
export function calculateContextTokens(usage) {
|
|
11
57
|
return usage.totalTokens > 0
|
|
12
58
|
? usage.totalTokens
|
|
13
59
|
: usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
14
60
|
}
|
|
15
|
-
|
|
61
|
+
/** 内容的估算(文本按脚本,图片每张 1 600)。 */
|
|
62
|
+
export function estimateContentTokens(content) {
|
|
16
63
|
if (typeof content === "string")
|
|
17
|
-
return
|
|
18
|
-
|
|
64
|
+
return estimateTextTokens(content);
|
|
65
|
+
const count = { wide: 0, narrow: 0 };
|
|
19
66
|
let images = 0;
|
|
20
67
|
for (const block of content) {
|
|
21
68
|
if (block.type === "text")
|
|
22
|
-
|
|
69
|
+
countText(block.text, count);
|
|
23
70
|
else
|
|
24
71
|
images++;
|
|
25
72
|
}
|
|
26
|
-
return
|
|
73
|
+
return tokensOf(count) + images * IMAGE_TOKENS;
|
|
27
74
|
}
|
|
28
|
-
/**
|
|
75
|
+
/** 单条消息的估算(文本按脚本区分,图片每张 1 600)。 */
|
|
29
76
|
export function estimateMessageTokens(message) {
|
|
30
|
-
|
|
31
|
-
let images = 0;
|
|
77
|
+
const count = { wide: 0, narrow: 0 };
|
|
32
78
|
switch (message.role) {
|
|
33
79
|
case "system":
|
|
34
80
|
for (const text of Object.values(message.sections))
|
|
35
|
-
|
|
81
|
+
countText(text ?? "", count);
|
|
36
82
|
if (message.toolsAdded !== undefined)
|
|
37
|
-
|
|
83
|
+
countText(JSON.stringify(message.toolsAdded), count);
|
|
38
84
|
break;
|
|
39
85
|
case "user":
|
|
40
86
|
case "toolResult":
|
|
41
|
-
case "custom":
|
|
42
|
-
|
|
43
|
-
chars = counted.chars;
|
|
44
|
-
images = counted.images;
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
87
|
+
case "custom":
|
|
88
|
+
return estimateContentTokens(message.content);
|
|
47
89
|
case "assistant":
|
|
48
90
|
for (const block of message.content) {
|
|
49
91
|
if (block.type === "text")
|
|
50
|
-
|
|
92
|
+
countText(block.text, count);
|
|
51
93
|
else if (block.type === "thinking")
|
|
52
|
-
|
|
94
|
+
countText(block.thinking, count);
|
|
53
95
|
else
|
|
54
|
-
|
|
96
|
+
countText(block.name + JSON.stringify(block.arguments), count);
|
|
55
97
|
}
|
|
56
98
|
break;
|
|
57
99
|
case "compactionSummary":
|
|
58
100
|
case "branchSummary":
|
|
59
|
-
|
|
101
|
+
countText(message.summary, count);
|
|
60
102
|
break;
|
|
61
103
|
}
|
|
62
|
-
return
|
|
104
|
+
return tokensOf(count);
|
|
63
105
|
}
|
|
64
106
|
export function estimateMessagesTokens(messages) {
|
|
65
107
|
let total = 0;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 单请求图片总量预算(docs/wave5-plan.md §4、D11)。[W5-I] 纯函数,无模型调用。
|
|
3
|
+
*
|
|
4
|
+
* base64 图片在历史里每轮重发,几张截图就可能撞上 Anthropic 32 MB / Gemini 20 MB 的请求上限。
|
|
5
|
+
* `planImageBudget` 给出要降级的消息:调用方把每条写成 `context_edit{reason:"image_budget"}`,
|
|
6
|
+
* 之后前缀稳定(不是请求时临时处理),未命中归因把它当重置点。
|
|
7
|
+
*
|
|
8
|
+
* 规则(按投影顺序,最旧在前):
|
|
9
|
+
* 1. 单张超过 `perImageBytes`(换了更小上限的模型 / 中转)的消息必降——发出去必被拒;
|
|
10
|
+
* 2. 总量(含本回合新提示里的图 `reserve`)> 预算时,从最旧的开始降,直到 ≤ 预算 × 60%
|
|
11
|
+
* (一次多降一些,减少反复失效);
|
|
12
|
+
* 3. 图片数 > 20 且还有边长 > 2000 px 的图时,继续从最旧的开始降,直到不超过 20 张或没有大图;
|
|
13
|
+
* 规则 2、3 不降最新一条带图的消息(刚读的图要让模型看到)。降级以消息为单位:文本块原样保留,
|
|
14
|
+
* 每个图片块换成占位文本。
|
|
15
|
+
*/
|
|
16
|
+
import type { ContentBlock, ImageBlock } from "../ai/types.js";
|
|
17
|
+
import type { ContextItem } from "../session/projection.js";
|
|
18
|
+
import { type ImageSize } from "../tools/image-file.js";
|
|
19
|
+
export declare const IMAGE_BUDGET_TARGET_RATIO = 0.6;
|
|
20
|
+
export declare const IMAGE_OMITTED_FOR_BUDGET = "[earlier image omitted to fit request size]";
|
|
21
|
+
export declare const IMAGE_OMITTED_TOO_LARGE = "[image omitted: larger than this endpoint's per-image limit]";
|
|
22
|
+
export interface ImageBudgetOptions {
|
|
23
|
+
/** 单张上限(base64 后);超过的必降。缺省不检查。 */
|
|
24
|
+
perImageBytes?: number;
|
|
25
|
+
/** 本回合即将发送、不能降级的图片(新提示里的附件):计入总量与张数。 */
|
|
26
|
+
reserve?: readonly ImageBlock[];
|
|
27
|
+
/** 降到预算的这个比例以下;缺省 0.6。 */
|
|
28
|
+
targetRatio?: number;
|
|
29
|
+
/** 测试注入:图片尺寸。缺省解 base64 读文件头。 */
|
|
30
|
+
sizeOf?(block: ImageBlock): ImageSize | undefined;
|
|
31
|
+
}
|
|
32
|
+
export interface ImageBudgetEdit {
|
|
33
|
+
targetId: string;
|
|
34
|
+
replacement: string;
|
|
35
|
+
/** `per_image`:单张超限;`budget`:总量;`many_images`:> 20 张时的边长。 */
|
|
36
|
+
cause: "per_image" | "budget" | "many_images";
|
|
37
|
+
/** 本条消息里被省略的图片数与 base64 字节。 */
|
|
38
|
+
images: number;
|
|
39
|
+
bytes: number;
|
|
40
|
+
}
|
|
41
|
+
/** 降级后的文本:文本块原样,图片块换成 `placeholder`,块间换行。 */
|
|
42
|
+
export declare function budgetReplacement(content: readonly ContentBlock[], placeholder?: string): string;
|
|
43
|
+
/** 解 base64 读文件头取尺寸(先解前 384 KB;JPEG 的 SOF 更靠后时再解全量);按块缓存。 */
|
|
44
|
+
export declare function base64ImageSize(block: ImageBlock): ImageSize | undefined;
|
|
45
|
+
export declare function planImageBudget(items: readonly ContextItem[], budgetBytes: number, options?: ImageBudgetOptions): ImageBudgetEdit[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 单请求图片总量预算(docs/wave5-plan.md §4、D11)。[W5-I] 纯函数,无模型调用。
|
|
3
|
+
*
|
|
4
|
+
* base64 图片在历史里每轮重发,几张截图就可能撞上 Anthropic 32 MB / Gemini 20 MB 的请求上限。
|
|
5
|
+
* `planImageBudget` 给出要降级的消息:调用方把每条写成 `context_edit{reason:"image_budget"}`,
|
|
6
|
+
* 之后前缀稳定(不是请求时临时处理),未命中归因把它当重置点。
|
|
7
|
+
*
|
|
8
|
+
* 规则(按投影顺序,最旧在前):
|
|
9
|
+
* 1. 单张超过 `perImageBytes`(换了更小上限的模型 / 中转)的消息必降——发出去必被拒;
|
|
10
|
+
* 2. 总量(含本回合新提示里的图 `reserve`)> 预算时,从最旧的开始降,直到 ≤ 预算 × 60%
|
|
11
|
+
* (一次多降一些,减少反复失效);
|
|
12
|
+
* 3. 图片数 > 20 且还有边长 > 2000 px 的图时,继续从最旧的开始降,直到不超过 20 张或没有大图;
|
|
13
|
+
* 规则 2、3 不降最新一条带图的消息(刚读的图要让模型看到)。降级以消息为单位:文本块原样保留,
|
|
14
|
+
* 每个图片块换成占位文本。
|
|
15
|
+
*/
|
|
16
|
+
import { MANY_IMAGES_COUNT, MANY_IMAGES_EDGE } from "../ai/image-limits.js";
|
|
17
|
+
import { imageSize } from "../tools/image-file.js";
|
|
18
|
+
export const IMAGE_BUDGET_TARGET_RATIO = 0.6;
|
|
19
|
+
export const IMAGE_OMITTED_FOR_BUDGET = "[earlier image omitted to fit request size]";
|
|
20
|
+
export const IMAGE_OMITTED_TOO_LARGE = "[image omitted: larger than this endpoint's per-image limit]";
|
|
21
|
+
function imagesOf(content) {
|
|
22
|
+
return content.filter((block) => block.type === "image");
|
|
23
|
+
}
|
|
24
|
+
function candidates(items) {
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const { entry, message } of items) {
|
|
27
|
+
if (entry.type !== "message" && entry.type !== "custom_message")
|
|
28
|
+
continue;
|
|
29
|
+
if (message.role !== "user" && message.role !== "toolResult" && message.role !== "custom")
|
|
30
|
+
continue;
|
|
31
|
+
if (typeof message.content === "string")
|
|
32
|
+
continue;
|
|
33
|
+
const images = imagesOf(message.content);
|
|
34
|
+
if (images.length === 0)
|
|
35
|
+
continue;
|
|
36
|
+
const bytes = images.reduce((sum, image) => sum + image.data.length, 0);
|
|
37
|
+
out.push({ targetId: entry.id, content: message.content, images, bytes });
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/** 降级后的文本:文本块原样,图片块换成 `placeholder`,块间换行。 */
|
|
42
|
+
export function budgetReplacement(content, placeholder = IMAGE_OMITTED_FOR_BUDGET) {
|
|
43
|
+
return content
|
|
44
|
+
.map((block) => (block.type === "text" ? block.text : placeholder))
|
|
45
|
+
.filter((text) => text !== "")
|
|
46
|
+
.join("\n");
|
|
47
|
+
}
|
|
48
|
+
const HEAD_CHARS = 512 * 1024;
|
|
49
|
+
const sizeCache = new WeakMap();
|
|
50
|
+
/** 解 base64 读文件头取尺寸(先解前 384 KB;JPEG 的 SOF 更靠后时再解全量);按块缓存。 */
|
|
51
|
+
export function base64ImageSize(block) {
|
|
52
|
+
const cached = sizeCache.get(block);
|
|
53
|
+
if (cached !== undefined)
|
|
54
|
+
return cached ?? undefined;
|
|
55
|
+
let size = imageSize(Buffer.from(block.data.slice(0, HEAD_CHARS), "base64"), block.mimeType);
|
|
56
|
+
if (size === undefined && block.data.length > HEAD_CHARS)
|
|
57
|
+
size = imageSize(Buffer.from(block.data, "base64"), block.mimeType);
|
|
58
|
+
sizeCache.set(block, size ?? null);
|
|
59
|
+
return size;
|
|
60
|
+
}
|
|
61
|
+
export function planImageBudget(items, budgetBytes, options = {}) {
|
|
62
|
+
const all = candidates(items);
|
|
63
|
+
const plan = [];
|
|
64
|
+
const drop = (c, cause) => {
|
|
65
|
+
const placeholder = cause === "per_image" ? IMAGE_OMITTED_TOO_LARGE : IMAGE_OMITTED_FOR_BUDGET;
|
|
66
|
+
plan.push({
|
|
67
|
+
targetId: c.targetId,
|
|
68
|
+
replacement: budgetReplacement(c.content, placeholder),
|
|
69
|
+
cause,
|
|
70
|
+
images: c.images.length,
|
|
71
|
+
bytes: c.bytes,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
// 1. 单张超限
|
|
75
|
+
const perImage = options.perImageBytes;
|
|
76
|
+
const kept = [];
|
|
77
|
+
for (const c of all) {
|
|
78
|
+
if (perImage !== undefined && c.images.some((image) => image.data.length > perImage))
|
|
79
|
+
drop(c, "per_image");
|
|
80
|
+
else
|
|
81
|
+
kept.push(c);
|
|
82
|
+
}
|
|
83
|
+
const reserve = options.reserve ?? [];
|
|
84
|
+
let total = kept.reduce((sum, c) => sum + c.bytes, 0) +
|
|
85
|
+
reserve.reduce((sum, image) => sum + image.data.length, 0);
|
|
86
|
+
let count = kept.reduce((sum, c) => sum + c.images.length, 0) + reserve.length;
|
|
87
|
+
// 最新一条带图的消息不降
|
|
88
|
+
const droppable = kept.slice(0, -1);
|
|
89
|
+
let next = 0;
|
|
90
|
+
// 2. 总量
|
|
91
|
+
if (total > budgetBytes) {
|
|
92
|
+
const target = budgetBytes * (options.targetRatio ?? IMAGE_BUDGET_TARGET_RATIO);
|
|
93
|
+
while (total > target && next < droppable.length) {
|
|
94
|
+
const c = droppable[next++];
|
|
95
|
+
drop(c, "budget");
|
|
96
|
+
total -= c.bytes;
|
|
97
|
+
count -= c.images.length;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 3. > 20 张时的边长
|
|
101
|
+
if (count > MANY_IMAGES_COUNT) {
|
|
102
|
+
const sizeOf = options.sizeOf ?? base64ImageSize;
|
|
103
|
+
const tooWide = (image) => {
|
|
104
|
+
const size = sizeOf(image);
|
|
105
|
+
return size !== undefined && Math.max(size.width, size.height) > MANY_IMAGES_EDGE;
|
|
106
|
+
};
|
|
107
|
+
const remaining = () => reserve.some(tooWide) || kept.slice(next).some((c) => c.images.some(tooWide));
|
|
108
|
+
while (count > MANY_IMAGES_COUNT && next < droppable.length && remaining()) {
|
|
109
|
+
const c = droppable[next++];
|
|
110
|
+
drop(c, "many_images");
|
|
111
|
+
count -= c.images.length;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return plan;
|
|
115
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 压缩后回注(wave5 §8.2 C6,D26)。[W5-H1]
|
|
3
|
+
*
|
|
4
|
+
* 把压缩可能丢掉的关键状态以**清单与指针**放回上下文(不含文件正文):
|
|
5
|
+
* - todo 快照(分支上最近一条 `ama.todo`);
|
|
6
|
+
* - 当前计划(最近一条未被拒绝 / 取代的 `ama.plan`:编号、状态、文件路径与步骤标题);
|
|
7
|
+
* - 已加载的 Skill(用 `read` 读过的索引路径,或 `skill` 工具的调用);
|
|
8
|
+
* - 最近修改 / 读取的文件路径(按最近使用排序,各至多 20 个);
|
|
9
|
+
* - 完整转录与 `outputs/` 路径(模型需要细节时自己 `read`);
|
|
10
|
+
* - 续接说明。
|
|
11
|
+
*
|
|
12
|
+
* 放在哪:作为 `<post-compact-state>` 块接在 compaction 条目的 `summary` 末尾(与已有的
|
|
13
|
+
* `<read-files>` / `<modified-files>` 同一做法),模型看到的顺序就是「摘要 → 回注 → 保留区」。
|
|
14
|
+
* 不另写 `custom_message`:投影把 compaction 之后的条目放在保留区之后,溢出恢复时保留区以用户刚发
|
|
15
|
+
* 的提示结尾,追加在末尾的回注会排在那条提示后面、让模型先回应回注。压缩后本来就是新前缀,
|
|
16
|
+
* 回注不额外打断缓存。下一次增量摘要前先剥掉这个块(`stripPostCompact`),每次重新生成。
|
|
17
|
+
*/
|
|
18
|
+
import type { SessionEntry } from "../session/types.js";
|
|
19
|
+
export declare const POST_COMPACT_TAG = "post-compact-state";
|
|
20
|
+
export declare const PLAN_CUSTOM_TYPE = "ama.plan";
|
|
21
|
+
export declare const MAX_RECENT_FILES = 20;
|
|
22
|
+
export interface PostCompactInput {
|
|
23
|
+
/** 活动分支(含刚追加的 compaction)。 */
|
|
24
|
+
branch: readonly SessionEntry[];
|
|
25
|
+
cwd: string;
|
|
26
|
+
/** Skill 索引里的文件路径(`skillLocations`)。 */
|
|
27
|
+
skillPaths?: readonly string[];
|
|
28
|
+
/** 会话转录文件;内存会话 undefined。 */
|
|
29
|
+
transcriptPath?: string | undefined;
|
|
30
|
+
/** 裁剪 / 截断全文目录。 */
|
|
31
|
+
outputDir?: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** 去掉摘要里的回注块(上一份摘要作增量合并的输入前、模型照抄进新摘要时)。 */
|
|
34
|
+
export declare function stripPostCompact(summary: string): string;
|
|
35
|
+
/** 回注块(`<post-compact-state>…</post-compact-state>`);无任何可回注的状态时仍给出续接说明。 */
|
|
36
|
+
export declare function buildPostCompactBlock(input: PostCompactInput): string;
|
|
37
|
+
/** 回注正文。 */
|
|
38
|
+
export declare function buildPostCompactContent(input: PostCompactInput): string;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 压缩后回注(wave5 §8.2 C6,D26)。[W5-H1]
|
|
3
|
+
*
|
|
4
|
+
* 把压缩可能丢掉的关键状态以**清单与指针**放回上下文(不含文件正文):
|
|
5
|
+
* - todo 快照(分支上最近一条 `ama.todo`);
|
|
6
|
+
* - 当前计划(最近一条未被拒绝 / 取代的 `ama.plan`:编号、状态、文件路径与步骤标题);
|
|
7
|
+
* - 已加载的 Skill(用 `read` 读过的索引路径,或 `skill` 工具的调用);
|
|
8
|
+
* - 最近修改 / 读取的文件路径(按最近使用排序,各至多 20 个);
|
|
9
|
+
* - 完整转录与 `outputs/` 路径(模型需要细节时自己 `read`);
|
|
10
|
+
* - 续接说明。
|
|
11
|
+
*
|
|
12
|
+
* 放在哪:作为 `<post-compact-state>` 块接在 compaction 条目的 `summary` 末尾(与已有的
|
|
13
|
+
* `<read-files>` / `<modified-files>` 同一做法),模型看到的顺序就是「摘要 → 回注 → 保留区」。
|
|
14
|
+
* 不另写 `custom_message`:投影把 compaction 之后的条目放在保留区之后,溢出恢复时保留区以用户刚发
|
|
15
|
+
* 的提示结尾,追加在末尾的回注会排在那条提示后面、让模型先回应回注。压缩后本来就是新前缀,
|
|
16
|
+
* 回注不额外打断缓存。下一次增量摘要前先剥掉这个块(`stripPostCompact`),每次重新生成。
|
|
17
|
+
*/
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
import { TODO_CUSTOM_TYPE } from "../tools/todo.js";
|
|
20
|
+
export const POST_COMPACT_TAG = "post-compact-state";
|
|
21
|
+
export const PLAN_CUSTOM_TYPE = "ama.plan";
|
|
22
|
+
export const MAX_RECENT_FILES = 20;
|
|
23
|
+
const MAX_PLAN_STEPS = 20;
|
|
24
|
+
const MAX_LINE = 160;
|
|
25
|
+
const READ_TOOLS = new Set(["read"]);
|
|
26
|
+
const WRITE_TOOLS = new Set(["write", "edit"]);
|
|
27
|
+
function clip(text) {
|
|
28
|
+
const line = text.replace(/\s+/g, " ").trim();
|
|
29
|
+
return line.length > MAX_LINE ? `${line.slice(0, MAX_LINE - 1)}…` : line;
|
|
30
|
+
}
|
|
31
|
+
function lastCustom(branch, type, accept) {
|
|
32
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
33
|
+
const entry = branch[i];
|
|
34
|
+
if (entry?.type === "custom" && entry.customType === type) {
|
|
35
|
+
if (accept === undefined || accept(entry.data))
|
|
36
|
+
return entry.data;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const TODO_MARK = { pending: "[ ]", in_progress: "[~]", done: "[x]" };
|
|
42
|
+
function todoLines(branch) {
|
|
43
|
+
const data = lastCustom(branch, TODO_CUSTOM_TYPE);
|
|
44
|
+
const items = Array.isArray(data?.items) ? data.items : [];
|
|
45
|
+
const lines = [];
|
|
46
|
+
for (const item of items) {
|
|
47
|
+
if (typeof item?.["text"] !== "string")
|
|
48
|
+
continue;
|
|
49
|
+
const mark = TODO_MARK[String(item["status"])] ?? "[ ]";
|
|
50
|
+
lines.push(`${mark} ${String(item["id"] ?? "")}. ${clip(item["text"])}`);
|
|
51
|
+
}
|
|
52
|
+
return lines;
|
|
53
|
+
}
|
|
54
|
+
function planBlock(branch) {
|
|
55
|
+
const plan = lastCustom(branch, PLAN_CUSTOM_TYPE);
|
|
56
|
+
if (plan === undefined || typeof plan !== "object" || plan === null)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (plan.status === "rejected" || plan.status === "superseded")
|
|
59
|
+
return undefined;
|
|
60
|
+
const attrs = [`id="${String(plan.id ?? "")}"`];
|
|
61
|
+
if (plan.version !== undefined)
|
|
62
|
+
attrs.push(`version="${String(plan.version)}"`);
|
|
63
|
+
if (plan.status !== undefined)
|
|
64
|
+
attrs.push(`status="${String(plan.status)}"`);
|
|
65
|
+
if (typeof plan.filePath === "string")
|
|
66
|
+
attrs.push(`file="${plan.filePath}"`);
|
|
67
|
+
const steps = Array.isArray(plan.steps) ? plan.steps : [];
|
|
68
|
+
const lines = steps
|
|
69
|
+
.slice(0, MAX_PLAN_STEPS)
|
|
70
|
+
.map((step) => `${String(step?.["id"] ?? "")} ${clip(String(step?.["text"] ?? ""))}`);
|
|
71
|
+
if (steps.length > MAX_PLAN_STEPS)
|
|
72
|
+
lines.push(`(+${steps.length - MAX_PLAN_STEPS} more steps)`);
|
|
73
|
+
return [`<plan ${attrs.join(" ")}>`, ...lines, "</plan>"].join("\n");
|
|
74
|
+
}
|
|
75
|
+
/** 按最近使用排序(最近的在前)的文件与 Skill。 */
|
|
76
|
+
function fileActivity(input) {
|
|
77
|
+
const skillSet = new Set((input.skillPaths ?? []).map((path) => resolve(input.cwd, path)));
|
|
78
|
+
const read = new Map();
|
|
79
|
+
const modified = new Map();
|
|
80
|
+
const skills = new Map();
|
|
81
|
+
let order = 0;
|
|
82
|
+
for (const entry of input.branch) {
|
|
83
|
+
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
84
|
+
continue;
|
|
85
|
+
for (const block of entry.message.content) {
|
|
86
|
+
if (block.type !== "toolCall")
|
|
87
|
+
continue;
|
|
88
|
+
order++;
|
|
89
|
+
if (block.name === "skill") {
|
|
90
|
+
const name = block.arguments["name"];
|
|
91
|
+
if (typeof name === "string")
|
|
92
|
+
skills.set(name, order);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const path = block.arguments["path"];
|
|
96
|
+
if (typeof path !== "string" || path === "")
|
|
97
|
+
continue;
|
|
98
|
+
const abs = resolve(input.cwd, path);
|
|
99
|
+
if (WRITE_TOOLS.has(block.name))
|
|
100
|
+
modified.set(path, order);
|
|
101
|
+
else if (READ_TOOLS.has(block.name)) {
|
|
102
|
+
if (skillSet.has(abs))
|
|
103
|
+
skills.set(abs, order);
|
|
104
|
+
else
|
|
105
|
+
read.set(path, order);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const path of modified.keys())
|
|
110
|
+
read.delete(path);
|
|
111
|
+
const recent = (map) => [...map.entries()].sort((a, b) => b[1] - a[1]).map(([key]) => key);
|
|
112
|
+
return { read: recent(read), modified: recent(modified), skills: recent(skills) };
|
|
113
|
+
}
|
|
114
|
+
function capped(paths) {
|
|
115
|
+
const shown = paths.slice(0, MAX_RECENT_FILES);
|
|
116
|
+
const more = paths.length - shown.length;
|
|
117
|
+
return more > 0 ? `${shown.join("\n")}\n(+${more} more)` : shown.join("\n");
|
|
118
|
+
}
|
|
119
|
+
const BLOCK_PATTERN = new RegExp(`\\s*<${POST_COMPACT_TAG}>[\\s\\S]*?</${POST_COMPACT_TAG}>`, "g");
|
|
120
|
+
/** 去掉摘要里的回注块(上一份摘要作增量合并的输入前、模型照抄进新摘要时)。 */
|
|
121
|
+
export function stripPostCompact(summary) {
|
|
122
|
+
return summary.replace(BLOCK_PATTERN, "").trimEnd();
|
|
123
|
+
}
|
|
124
|
+
/** 回注块(`<post-compact-state>…</post-compact-state>`);无任何可回注的状态时仍给出续接说明。 */
|
|
125
|
+
export function buildPostCompactBlock(input) {
|
|
126
|
+
return `<${POST_COMPACT_TAG}>\n${buildPostCompactContent(input)}\n</${POST_COMPACT_TAG}>`;
|
|
127
|
+
}
|
|
128
|
+
/** 回注正文。 */
|
|
129
|
+
export function buildPostCompactContent(input) {
|
|
130
|
+
const parts = [
|
|
131
|
+
"State to continue from after compaction (pointers only; read a file again only when you need its content):",
|
|
132
|
+
];
|
|
133
|
+
const todo = todoLines(input.branch);
|
|
134
|
+
if (todo.length > 0)
|
|
135
|
+
parts.push(`<todo>\n${todo.join("\n")}\n</todo>`);
|
|
136
|
+
const plan = planBlock(input.branch);
|
|
137
|
+
if (plan !== undefined)
|
|
138
|
+
parts.push(plan);
|
|
139
|
+
const files = fileActivity(input);
|
|
140
|
+
if (files.skills.length > 0)
|
|
141
|
+
parts.push(`<loaded-skills>\n${capped(files.skills)}\n</loaded-skills>`);
|
|
142
|
+
if (files.modified.length > 0)
|
|
143
|
+
parts.push(`<recently-modified-files>\n${capped(files.modified)}\n</recently-modified-files>`);
|
|
144
|
+
if (files.read.length > 0)
|
|
145
|
+
parts.push(`<recently-read-files>\n${capped(files.read)}\n</recently-read-files>`);
|
|
146
|
+
if (input.transcriptPath !== undefined)
|
|
147
|
+
parts.push(`<transcript>${input.transcriptPath}</transcript> (full history before compaction, JSONL; read it only if the summary lacks a detail you need)`);
|
|
148
|
+
if (input.outputDir !== undefined)
|
|
149
|
+
parts.push(`<outputs>${input.outputDir}</outputs> (full text of pruned and truncated tool results)`);
|
|
150
|
+
parts.push("Continue from the summary's Next Steps and the todo list; do not redo finished work.");
|
|
151
|
+
return parts.join("\n\n");
|
|
152
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档一保护集(wave5 §8.2 C4)。[W5-H1]
|
|
3
|
+
*
|
|
4
|
+
* 不被档一换成占位的工具结果:
|
|
5
|
+
* - `todo`、`skill` 工具的结果(任务清单与 Skill 正文是长任务的骨架);
|
|
6
|
+
* - `read` 读入的 Skill 文件(系统提示 skills 节 `<location>` 里列出的路径)与 `AGENTS.md` / `CLAUDE.md`;
|
|
7
|
+
* - 声明了 `annotations.keepInContext` 的工具(宿主工具可用);
|
|
8
|
+
* - `compaction.pruneExclude` 列出的工具名。
|
|
9
|
+
*/
|
|
10
|
+
import type { ContextItem } from "../session/projection.js";
|
|
11
|
+
import type { PruneCandidate } from "./prune-tier.js";
|
|
12
|
+
export declare const PROTECTED_TOOLS: readonly string[];
|
|
13
|
+
export declare const PROTECTED_FILE_NAMES: readonly string[];
|
|
14
|
+
export interface ProtectionInput {
|
|
15
|
+
cwd: string;
|
|
16
|
+
/** Skill 文件的绝对路径(见 `skillLocations`)。 */
|
|
17
|
+
skillPaths?: readonly string[];
|
|
18
|
+
/** `compaction.pruneExclude`。 */
|
|
19
|
+
exclude?: readonly string[];
|
|
20
|
+
/** 工具是否声明了 `annotations.keepInContext`。 */
|
|
21
|
+
keepInContext?(toolName: string): boolean;
|
|
22
|
+
}
|
|
23
|
+
/** 投影里最近一次给出 skills 节的 system 消息中的 `<location>`(索引里列出的 Skill 文件)。 */
|
|
24
|
+
export declare function skillLocations(items: readonly ContextItem[]): string[];
|
|
25
|
+
/** `read` 调用读的文件(绝对路径);不是 read 或缺 path 时 undefined。 */
|
|
26
|
+
export declare function readPathOf(candidate: Pick<PruneCandidate, "toolName" | "args">, cwd: string): string | undefined;
|
|
27
|
+
export declare function createProtection(input: ProtectionInput): (candidate: PruneCandidate) => boolean;
|