@cjhyy/code-shell-core 0.5.0-rc.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/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/agent/coordinator.d.ts +49 -0
- package/dist/agent/coordinator.js +77 -0
- package/dist/arena/arena.d.ts +43 -0
- package/dist/arena/arena.js +334 -0
- package/dist/arena/context/context-tools.d.ts +16 -0
- package/dist/arena/context/context-tools.js +231 -0
- package/dist/arena/detect-mode.d.ts +20 -0
- package/dist/arena/detect-mode.js +78 -0
- package/dist/arena/digest-builder.d.ts +25 -0
- package/dist/arena/digest-builder.js +120 -0
- package/dist/arena/index.d.ts +29 -0
- package/dist/arena/index.js +29 -0
- package/dist/arena/iterate/convergence.d.ts +25 -0
- package/dist/arena/iterate/convergence.js +103 -0
- package/dist/arena/iterate/formats/index.d.ts +22 -0
- package/dist/arena/iterate/formats/index.js +283 -0
- package/dist/arena/iterate/index.d.ts +11 -0
- package/dist/arena/iterate/index.js +9 -0
- package/dist/arena/iterate/iterative-arena.d.ts +23 -0
- package/dist/arena/iterate/iterative-arena.js +237 -0
- package/dist/arena/iterate/parse.d.ts +42 -0
- package/dist/arena/iterate/parse.js +123 -0
- package/dist/arena/iterate/phases/argue.d.ts +22 -0
- package/dist/arena/iterate/phases/argue.js +159 -0
- package/dist/arena/iterate/phases/revise.d.ts +16 -0
- package/dist/arena/iterate/phases/revise.js +62 -0
- package/dist/arena/iterate/phases/tournament.d.ts +34 -0
- package/dist/arena/iterate/phases/tournament.js +113 -0
- package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
- package/dist/arena/iterate/tools/web-tools.js +54 -0
- package/dist/arena/iterate/types.d.ts +152 -0
- package/dist/arena/iterate/types.js +8 -0
- package/dist/arena/ledger.d.ts +47 -0
- package/dist/arena/ledger.js +151 -0
- package/dist/arena/lenses/architecture.d.ts +5 -0
- package/dist/arena/lenses/architecture.js +22 -0
- package/dist/arena/lenses/engineering.d.ts +5 -0
- package/dist/arena/lenses/engineering.js +22 -0
- package/dist/arena/lenses/general.d.ts +5 -0
- package/dist/arena/lenses/general.js +20 -0
- package/dist/arena/lenses/index.d.ts +16 -0
- package/dist/arena/lenses/index.js +47 -0
- package/dist/arena/lenses/product.d.ts +5 -0
- package/dist/arena/lenses/product.js +22 -0
- package/dist/arena/model-presets.d.ts +23 -0
- package/dist/arena/model-presets.js +44 -0
- package/dist/arena/phases/adjudication.d.ts +24 -0
- package/dist/arena/phases/adjudication.js +144 -0
- package/dist/arena/phases/build-consensus.d.ts +29 -0
- package/dist/arena/phases/build-consensus.js +86 -0
- package/dist/arena/phases/claim-registry.d.ts +26 -0
- package/dist/arena/phases/claim-registry.js +60 -0
- package/dist/arena/phases/cross-review.d.ts +45 -0
- package/dist/arena/phases/cross-review.js +224 -0
- package/dist/arena/phases/debate-rounds.d.ts +27 -0
- package/dist/arena/phases/debate-rounds.js +162 -0
- package/dist/arena/phases/participant-research.d.ts +38 -0
- package/dist/arena/phases/participant-research.js +317 -0
- package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
- package/dist/arena/phases/planning-detail-expansion.js +121 -0
- package/dist/arena/planner.d.ts +27 -0
- package/dist/arena/planner.js +312 -0
- package/dist/arena/providers/docs.d.ts +6 -0
- package/dist/arena/providers/docs.js +108 -0
- package/dist/arena/providers/git.d.ts +8 -0
- package/dist/arena/providers/git.js +174 -0
- package/dist/arena/providers/index.d.ts +32 -0
- package/dist/arena/providers/index.js +132 -0
- package/dist/arena/providers/none.d.ts +7 -0
- package/dist/arena/providers/none.js +11 -0
- package/dist/arena/providers/repo.d.ts +6 -0
- package/dist/arena/providers/repo.js +255 -0
- package/dist/arena/render/session.d.ts +17 -0
- package/dist/arena/render/session.js +190 -0
- package/dist/arena/render/terminal.d.ts +34 -0
- package/dist/arena/render/terminal.js +286 -0
- package/dist/arena/strategies/discussion.d.ts +25 -0
- package/dist/arena/strategies/discussion.js +143 -0
- package/dist/arena/strategies/index.d.ts +15 -0
- package/dist/arena/strategies/index.js +28 -0
- package/dist/arena/strategies/language-wrapper.d.ts +17 -0
- package/dist/arena/strategies/language-wrapper.js +102 -0
- package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
- package/dist/arena/strategies/lens-wrapper.js +236 -0
- package/dist/arena/strategies/planning.d.ts +30 -0
- package/dist/arena/strategies/planning.js +225 -0
- package/dist/arena/strategies/review.d.ts +26 -0
- package/dist/arena/strategies/review.js +168 -0
- package/dist/arena/strategies/utils.d.ts +39 -0
- package/dist/arena/strategies/utils.js +647 -0
- package/dist/arena/tools/selector.d.ts +17 -0
- package/dist/arena/tools/selector.js +61 -0
- package/dist/arena/transitions.d.ts +48 -0
- package/dist/arena/transitions.js +92 -0
- package/dist/arena/types.d.ts +508 -0
- package/dist/arena/types.js +27 -0
- package/dist/cli/agent-server-stdio.d.ts +32 -0
- package/dist/cli/agent-server-stdio.js +116 -0
- package/dist/colorizer.d.ts +23 -0
- package/dist/colorizer.js +12 -0
- package/dist/context/compaction.d.ts +116 -0
- package/dist/context/compaction.js +534 -0
- package/dist/context/manager.d.ts +145 -0
- package/dist/context/manager.js +423 -0
- package/dist/context/token-counter.d.ts +27 -0
- package/dist/context/token-counter.js +91 -0
- package/dist/context/tool-result-storage.d.ts +96 -0
- package/dist/context/tool-result-storage.js +296 -0
- package/dist/cost-tracker.d.ts +64 -0
- package/dist/cost-tracker.js +314 -0
- package/dist/cron/scheduler.d.ts +34 -0
- package/dist/cron/scheduler.js +115 -0
- package/dist/data/deepseek-models.json +46 -0
- package/dist/data/gemini-models.json +67 -0
- package/dist/data/openai-models.json +175 -0
- package/dist/data/openrouter-models.d.ts +35 -0
- package/dist/data/openrouter-models.js +36 -0
- package/dist/data/openrouter-models.json +4626 -0
- package/dist/data/openrouter-sync.d.ts +16 -0
- package/dist/data/openrouter-sync.js +53 -0
- package/dist/data/static-catalogs.d.ts +25 -0
- package/dist/data/static-catalogs.js +26 -0
- package/dist/data/zai-models.json +56 -0
- package/dist/engine/cost-store.d.ts +22 -0
- package/dist/engine/cost-store.js +15 -0
- package/dist/engine/engine.d.ts +295 -0
- package/dist/engine/engine.js +1467 -0
- package/dist/engine/model-facade.d.ts +24 -0
- package/dist/engine/model-facade.js +194 -0
- package/dist/engine/parse-task.d.ts +59 -0
- package/dist/engine/parse-task.js +135 -0
- package/dist/engine/patch-orphaned-tools.d.ts +52 -0
- package/dist/engine/patch-orphaned-tools.js +95 -0
- package/dist/engine/query.d.ts +50 -0
- package/dist/engine/query.js +107 -0
- package/dist/engine/runtime.d.ts +50 -0
- package/dist/engine/runtime.js +59 -0
- package/dist/engine/streaming-tool-queue.d.ts +34 -0
- package/dist/engine/streaming-tool-queue.js +61 -0
- package/dist/engine/token-budget.d.ts +22 -0
- package/dist/engine/token-budget.js +40 -0
- package/dist/engine/tool-summary.d.ts +13 -0
- package/dist/engine/tool-summary.js +35 -0
- package/dist/engine/turn-loop.d.ts +122 -0
- package/dist/engine/turn-loop.js +602 -0
- package/dist/engine/turn-state.d.ts +20 -0
- package/dist/engine/turn-state.js +17 -0
- package/dist/exceptions.d.ts +61 -0
- package/dist/exceptions.js +116 -0
- package/dist/git/utils.d.ts +35 -0
- package/dist/git/utils.js +98 -0
- package/dist/git/worktree.d.ts +36 -0
- package/dist/git/worktree.js +139 -0
- package/dist/hooks/events.d.ts +114 -0
- package/dist/hooks/events.js +4 -0
- package/dist/hooks/inject.d.ts +19 -0
- package/dist/hooks/inject.js +29 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +84 -0
- package/dist/hooks/shell-runner.d.ts +45 -0
- package/dist/hooks/shell-runner.js +173 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +145 -0
- package/dist/llm/api-key-sanitize.d.ts +22 -0
- package/dist/llm/api-key-sanitize.js +81 -0
- package/dist/llm/capabilities/index.d.ts +20 -0
- package/dist/llm/capabilities/index.js +39 -0
- package/dist/llm/capabilities/rules.d.ts +26 -0
- package/dist/llm/capabilities/rules.js +215 -0
- package/dist/llm/capabilities/types.d.ts +114 -0
- package/dist/llm/capabilities/types.js +24 -0
- package/dist/llm/client-base.d.ts +29 -0
- package/dist/llm/client-base.js +137 -0
- package/dist/llm/client-factory.d.ts +10 -0
- package/dist/llm/client-factory.js +29 -0
- package/dist/llm/model-cache.d.ts +21 -0
- package/dist/llm/model-cache.js +46 -0
- package/dist/llm/model-fetcher.d.ts +29 -0
- package/dist/llm/model-fetcher.js +282 -0
- package/dist/llm/model-pool.d.ts +95 -0
- package/dist/llm/model-pool.js +246 -0
- package/dist/llm/provider-catalog.d.ts +32 -0
- package/dist/llm/provider-catalog.js +50 -0
- package/dist/llm/provider-kinds.d.ts +21 -0
- package/dist/llm/provider-kinds.js +132 -0
- package/dist/llm/providers/anthropic.d.ts +19 -0
- package/dist/llm/providers/anthropic.js +267 -0
- package/dist/llm/providers/openai.d.ts +64 -0
- package/dist/llm/providers/openai.js +606 -0
- package/dist/llm/retry.d.ts +12 -0
- package/dist/llm/retry.js +17 -0
- package/dist/llm/stream-watchdog.d.ts +42 -0
- package/dist/llm/stream-watchdog.js +70 -0
- package/dist/llm/token-counter.d.ts +17 -0
- package/dist/llm/token-counter.js +36 -0
- package/dist/llm/types.d.ts +37 -0
- package/dist/llm/types.js +4 -0
- package/dist/logging/logger.d.ts +109 -0
- package/dist/logging/logger.js +390 -0
- package/dist/logging/sanitize-messages.d.ts +42 -0
- package/dist/logging/sanitize-messages.js +150 -0
- package/dist/logging/session-recorder.d.ts +90 -0
- package/dist/logging/session-recorder.js +304 -0
- package/dist/lsp/client.d.ts +41 -0
- package/dist/lsp/client.js +166 -0
- package/dist/lsp/manager.d.ts +40 -0
- package/dist/lsp/manager.js +124 -0
- package/dist/lsp/servers.d.ts +16 -0
- package/dist/lsp/servers.js +60 -0
- package/dist/migrate-models.d.ts +45 -0
- package/dist/migrate-models.js +179 -0
- package/dist/onboarding.d.ts +189 -0
- package/dist/onboarding.js +625 -0
- package/dist/plugins/gitOps.d.ts +32 -0
- package/dist/plugins/gitOps.js +62 -0
- package/dist/plugins/installedPlugins.d.ts +15 -0
- package/dist/plugins/installedPlugins.js +56 -0
- package/dist/plugins/knownMarketplaces.d.ts +10 -0
- package/dist/plugins/knownMarketplaces.js +46 -0
- package/dist/plugins/loadPluginHooks.d.ts +52 -0
- package/dist/plugins/loadPluginHooks.js +162 -0
- package/dist/plugins/marketplaceManager.d.ts +49 -0
- package/dist/plugins/marketplaceManager.js +142 -0
- package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
- package/dist/plugins/parseMarketplaceInput.js +73 -0
- package/dist/plugins/pluginCommandHook.d.ts +54 -0
- package/dist/plugins/pluginCommandHook.js +180 -0
- package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
- package/dist/plugins/pluginCommandsLoader.js +114 -0
- package/dist/plugins/pluginInstaller.d.ts +36 -0
- package/dist/plugins/pluginInstaller.js +228 -0
- package/dist/plugins/schemas.d.ts +9 -0
- package/dist/plugins/schemas.js +136 -0
- package/dist/plugins/types.d.ts +75 -0
- package/dist/plugins/types.js +7 -0
- package/dist/plugins/varRewrite.d.ts +42 -0
- package/dist/plugins/varRewrite.js +129 -0
- package/dist/preset/index.d.ts +60 -0
- package/dist/preset/index.js +159 -0
- package/dist/product/define.d.ts +60 -0
- package/dist/product/define.js +123 -0
- package/dist/product/index.d.ts +5 -0
- package/dist/product/index.js +4 -0
- package/dist/product/types.d.ts +111 -0
- package/dist/product/types.js +21 -0
- package/dist/prompt/composer.d.ts +49 -0
- package/dist/prompt/composer.js +168 -0
- package/dist/prompt/instruction-scanner.d.ts +54 -0
- package/dist/prompt/instruction-scanner.js +187 -0
- package/dist/prompt/section-cache.d.ts +15 -0
- package/dist/prompt/section-cache.js +31 -0
- package/dist/prompt/section-loader.d.ts +26 -0
- package/dist/prompt/section-loader.js +53 -0
- package/dist/prompt/sections/base.md +35 -0
- package/dist/prompt/sections/coding.md +31 -0
- package/dist/prompt/sections/orchestration.md +16 -0
- package/dist/prompt/sections/tone.md +9 -0
- package/dist/protocol/chat-session-manager.d.ts +33 -0
- package/dist/protocol/chat-session-manager.js +65 -0
- package/dist/protocol/chat-session.d.ts +49 -0
- package/dist/protocol/chat-session.js +86 -0
- package/dist/protocol/client.d.ts +104 -0
- package/dist/protocol/client.js +260 -0
- package/dist/protocol/factories.d.ts +93 -0
- package/dist/protocol/factories.js +64 -0
- package/dist/protocol/helpers.d.ts +42 -0
- package/dist/protocol/helpers.js +55 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/index.js +4 -0
- package/dist/protocol/server.d.ts +80 -0
- package/dist/protocol/server.js +855 -0
- package/dist/protocol/transport.d.ts +37 -0
- package/dist/protocol/transport.js +85 -0
- package/dist/protocol/types.d.ts +221 -0
- package/dist/protocol/types.js +61 -0
- package/dist/remote/bridge.d.ts +37 -0
- package/dist/remote/bridge.js +103 -0
- package/dist/run/ArtifactTracker.d.ts +38 -0
- package/dist/run/ArtifactTracker.js +132 -0
- package/dist/run/CheckpointWriter.d.ts +53 -0
- package/dist/run/CheckpointWriter.js +135 -0
- package/dist/run/EngineRunner.d.ts +72 -0
- package/dist/run/EngineRunner.js +119 -0
- package/dist/run/Evaluator.d.ts +56 -0
- package/dist/run/Evaluator.js +62 -0
- package/dist/run/FileRunStore.d.ts +39 -0
- package/dist/run/FileRunStore.js +176 -0
- package/dist/run/Heartbeat.d.ts +54 -0
- package/dist/run/Heartbeat.js +123 -0
- package/dist/run/RunApprovalBackend.d.ts +53 -0
- package/dist/run/RunApprovalBackend.js +85 -0
- package/dist/run/RunLock.d.ts +40 -0
- package/dist/run/RunLock.js +98 -0
- package/dist/run/RunManager.d.ts +82 -0
- package/dist/run/RunManager.js +587 -0
- package/dist/run/RunQueue.d.ts +27 -0
- package/dist/run/RunQueue.js +73 -0
- package/dist/run/RunStore.d.ts +25 -0
- package/dist/run/RunStore.js +9 -0
- package/dist/run/factory.d.ts +63 -0
- package/dist/run/factory.js +51 -0
- package/dist/run/index.d.ts +17 -0
- package/dist/run/index.js +23 -0
- package/dist/run/types.d.ts +136 -0
- package/dist/run/types.js +28 -0
- package/dist/runtime/safe-spawn.d.ts +118 -0
- package/dist/runtime/safe-spawn.js +268 -0
- package/dist/services/analytics.d.ts +29 -0
- package/dist/services/analytics.js +84 -0
- package/dist/services/auto-dream.d.ts +49 -0
- package/dist/services/auto-dream.js +124 -0
- package/dist/services/diagnostics.d.ts +41 -0
- package/dist/services/diagnostics.js +99 -0
- package/dist/services/extract-memories.d.ts +31 -0
- package/dist/services/extract-memories.js +71 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.js +17 -0
- package/dist/services/memory-orchestrator.d.ts +60 -0
- package/dist/services/memory-orchestrator.js +133 -0
- package/dist/services/notifier.d.ts +25 -0
- package/dist/services/notifier.js +69 -0
- package/dist/services/oauth.d.ts +27 -0
- package/dist/services/oauth.js +153 -0
- package/dist/services/session-memory.d.ts +37 -0
- package/dist/services/session-memory.js +81 -0
- package/dist/session/file-history.d.ts +41 -0
- package/dist/session/file-history.js +115 -0
- package/dist/session/memory.d.ts +104 -0
- package/dist/session/memory.js +310 -0
- package/dist/session/session-manager.d.ts +41 -0
- package/dist/session/session-manager.js +261 -0
- package/dist/session/transcript.d.ts +39 -0
- package/dist/session/transcript.js +186 -0
- package/dist/settings/manager.d.ts +36 -0
- package/dist/settings/manager.js +187 -0
- package/dist/settings/schema.d.ts +1219 -0
- package/dist/settings/schema.js +246 -0
- package/dist/skills/frontmatter.d.ts +13 -0
- package/dist/skills/frontmatter.js +70 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/scanner.d.ts +31 -0
- package/dist/skills/scanner.js +171 -0
- package/dist/state.d.ts +160 -0
- package/dist/state.js +267 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
- package/dist/tool-system/builtin/agent-notifications.js +197 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
- package/dist/tool-system/builtin/agent-registry.js +126 -0
- package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
- package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
- package/dist/tool-system/builtin/agent.d.ts +16 -0
- package/dist/tool-system/builtin/agent.js +364 -0
- package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
- package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
- package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
- package/dist/tool-system/builtin/apply-patch/index.js +101 -0
- package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
- package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
- package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
- package/dist/tool-system/builtin/apply-patch/types.js +13 -0
- package/dist/tool-system/builtin/arena.d.ts +31 -0
- package/dist/tool-system/builtin/arena.js +416 -0
- package/dist/tool-system/builtin/ask-user.d.ts +20 -0
- package/dist/tool-system/builtin/ask-user.js +99 -0
- package/dist/tool-system/builtin/bash.d.ts +21 -0
- package/dist/tool-system/builtin/bash.js +149 -0
- package/dist/tool-system/builtin/brief.d.ts +6 -0
- package/dist/tool-system/builtin/brief.js +41 -0
- package/dist/tool-system/builtin/config.d.ts +6 -0
- package/dist/tool-system/builtin/config.js +67 -0
- package/dist/tool-system/builtin/cron.d.ts +10 -0
- package/dist/tool-system/builtin/cron.js +61 -0
- package/dist/tool-system/builtin/edit.d.ts +6 -0
- package/dist/tool-system/builtin/edit.js +100 -0
- package/dist/tool-system/builtin/file-cache.d.ts +11 -0
- package/dist/tool-system/builtin/file-cache.js +39 -0
- package/dist/tool-system/builtin/glob.d.ts +7 -0
- package/dist/tool-system/builtin/glob.js +76 -0
- package/dist/tool-system/builtin/grep.d.ts +7 -0
- package/dist/tool-system/builtin/grep.js +150 -0
- package/dist/tool-system/builtin/index.d.ts +20 -0
- package/dist/tool-system/builtin/index.js +449 -0
- package/dist/tool-system/builtin/lsp.d.ts +6 -0
- package/dist/tool-system/builtin/lsp.js +141 -0
- package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
- package/dist/tool-system/builtin/mcp-tools.js +102 -0
- package/dist/tool-system/builtin/memory.d.ts +26 -0
- package/dist/tool-system/builtin/memory.js +226 -0
- package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
- package/dist/tool-system/builtin/notebook-edit.js +124 -0
- package/dist/tool-system/builtin/plan.d.ts +15 -0
- package/dist/tool-system/builtin/plan.js +55 -0
- package/dist/tool-system/builtin/powershell.d.ts +11 -0
- package/dist/tool-system/builtin/powershell.js +64 -0
- package/dist/tool-system/builtin/read.d.ts +6 -0
- package/dist/tool-system/builtin/read.js +70 -0
- package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
- package/dist/tool-system/builtin/remote-trigger.js +54 -0
- package/dist/tool-system/builtin/repl.d.ts +11 -0
- package/dist/tool-system/builtin/repl.js +82 -0
- package/dist/tool-system/builtin/send-message.d.ts +6 -0
- package/dist/tool-system/builtin/send-message.js +47 -0
- package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
- package/dist/tool-system/builtin/skill-prompt.js +52 -0
- package/dist/tool-system/builtin/skill.d.ts +9 -0
- package/dist/tool-system/builtin/skill.js +55 -0
- package/dist/tool-system/builtin/sleep.d.ts +6 -0
- package/dist/tool-system/builtin/sleep.js +33 -0
- package/dist/tool-system/builtin/task.d.ts +48 -0
- package/dist/tool-system/builtin/task.js +139 -0
- package/dist/tool-system/builtin/tool-search.d.ts +11 -0
- package/dist/tool-system/builtin/tool-search.js +90 -0
- package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
- package/dist/tool-system/builtin/web-fetch.js +328 -0
- package/dist/tool-system/builtin/web-search.d.ts +25 -0
- package/dist/tool-system/builtin/web-search.js +174 -0
- package/dist/tool-system/builtin/worktree.d.ts +10 -0
- package/dist/tool-system/builtin/worktree.js +95 -0
- package/dist/tool-system/builtin/write.d.ts +6 -0
- package/dist/tool-system/builtin/write.js +36 -0
- package/dist/tool-system/context.d.ts +165 -0
- package/dist/tool-system/context.js +18 -0
- package/dist/tool-system/executor.d.ts +49 -0
- package/dist/tool-system/executor.js +495 -0
- package/dist/tool-system/investigation-guard.d.ts +46 -0
- package/dist/tool-system/investigation-guard.js +155 -0
- package/dist/tool-system/mcp-manager.d.ts +67 -0
- package/dist/tool-system/mcp-manager.js +249 -0
- package/dist/tool-system/permission.d.ts +111 -0
- package/dist/tool-system/permission.js +774 -0
- package/dist/tool-system/registry.d.ts +32 -0
- package/dist/tool-system/registry.js +131 -0
- package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
- package/dist/tool-system/sandbox/bwrap.js +63 -0
- package/dist/tool-system/sandbox/index.d.ts +78 -0
- package/dist/tool-system/sandbox/index.js +222 -0
- package/dist/tool-system/sandbox/off.d.ts +2 -0
- package/dist/tool-system/sandbox/off.js +8 -0
- package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
- package/dist/tool-system/sandbox/seatbelt.js +109 -0
- package/dist/tool-system/task-guard.d.ts +29 -0
- package/dist/tool-system/task-guard.js +70 -0
- package/dist/tool-system/validation.d.ts +5 -0
- package/dist/tool-system/validation.js +39 -0
- package/dist/types.d.ts +376 -0
- package/dist/types.js +4 -0
- package/dist/updater.d.ts +65 -0
- package/dist/updater.js +405 -0
- package/dist/utils/debug.d.ts +41 -0
- package/dist/utils/debug.js +110 -0
- package/dist/utils/earlyInput.d.ts +43 -0
- package/dist/utils/earlyInput.js +166 -0
- package/dist/utils/env.d.ts +24 -0
- package/dist/utils/env.js +104 -0
- package/dist/utils/envUtils.d.ts +51 -0
- package/dist/utils/envUtils.js +135 -0
- package/dist/utils/execFileNoThrow.d.ts +39 -0
- package/dist/utils/execFileNoThrow.js +74 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/format.js +236 -0
- package/dist/utils/intl.d.ts +22 -0
- package/dist/utils/intl.js +83 -0
- package/dist/utils/lockfile.d.ts +15 -0
- package/dist/utils/lockfile.js +30 -0
- package/dist/utils/memoize.d.ts +19 -0
- package/dist/utils/memoize.js +14 -0
- package/dist/utils/semver.d.ts +6 -0
- package/dist/utils/semver.js +13 -0
- package/dist/utils/sliceAnsi.d.ts +12 -0
- package/dist/utils/sliceAnsi.js +62 -0
- package/dist/utils/systemTheme.d.ts +40 -0
- package/dist/utils/systemTheme.js +108 -0
- package/dist/utils/task-sanitizer.d.ts +6 -0
- package/dist/utils/task-sanitizer.js +24 -0
- package/dist/utils/theme.d.ts +93 -0
- package/dist/utils/theme.js +544 -0
- package/dist/utils/toolDisplay.d.ts +31 -0
- package/dist/utils/toolDisplay.js +188 -0
- package/package.json +72 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context manager — three-tier context management.
|
|
3
|
+
*
|
|
4
|
+
* Tier 1: microcompact (sync, zero-cost) — clear old tool_result content
|
|
5
|
+
* Tier 2: LLM summary (async) — generate summary of older messages via model call
|
|
6
|
+
* Tier 3: window compact (sync, emergency) — aggressive truncation fallback
|
|
7
|
+
*/
|
|
8
|
+
import type { Message } from "../types.js";
|
|
9
|
+
export interface ContextManagerConfig {
|
|
10
|
+
maxTokens: number;
|
|
11
|
+
compactAtRatio: number;
|
|
12
|
+
summarizeAtRatio: number;
|
|
13
|
+
maxToolResultChars: number;
|
|
14
|
+
/**
|
|
15
|
+
* Lower bound, as a ratio of maxTokens, below which microcompact will not
|
|
16
|
+
* run. CC's external (non-cache-edit) path is "autocompact handles context
|
|
17
|
+
* pressure"; mirror that by leaving early-turn context alone so a model
|
|
18
|
+
* with a 1M window doesn't see its first-3-rounds Read results wiped at
|
|
19
|
+
* 23k tokens. Raising the floor also keeps prompt-cache prefixes warm in
|
|
20
|
+
* short sessions.
|
|
21
|
+
*/
|
|
22
|
+
microcompactFloorRatio: number;
|
|
23
|
+
/**
|
|
24
|
+
* Most-recent compactable rounds microcompact keeps untouched. Auto-derived
|
|
25
|
+
* from maxTokens when not set explicitly: bigger window → keep more.
|
|
26
|
+
*/
|
|
27
|
+
microcompactKeepRecent?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Async function type for LLM summarization calls.
|
|
31
|
+
* Injected by the Engine so the ContextManager doesn't depend on LLM directly.
|
|
32
|
+
*/
|
|
33
|
+
export type SummarizeFn = (prompt: string) => Promise<string>;
|
|
34
|
+
export type CompactStrategy = "micro" | "summary" | "window" | "snip" | "emergency";
|
|
35
|
+
export type OnCompactFn = (info: {
|
|
36
|
+
strategy: CompactStrategy;
|
|
37
|
+
before: number;
|
|
38
|
+
after: number;
|
|
39
|
+
}) => void;
|
|
40
|
+
export declare class ContextManager {
|
|
41
|
+
private config;
|
|
42
|
+
private toolCallHashes;
|
|
43
|
+
private summarizeFn;
|
|
44
|
+
private consecutiveSummaryFailures;
|
|
45
|
+
private lastSummary;
|
|
46
|
+
/** Last known actual token count from API usage data. */
|
|
47
|
+
private lastActualTokens;
|
|
48
|
+
/** Message count at the time lastActualTokens was recorded. */
|
|
49
|
+
private lastActualAtMessageCount;
|
|
50
|
+
/** Path to session transcript — passed to summary compaction for on-demand access. */
|
|
51
|
+
private transcriptPath;
|
|
52
|
+
/** Notified whenever any compaction tier fires, including microcompact. */
|
|
53
|
+
private onCompact;
|
|
54
|
+
/**
|
|
55
|
+
* Tool-result persistence state. Created lazily once we know where to
|
|
56
|
+
* write files (i.e. once setTranscriptPath has been called).
|
|
57
|
+
* `null` = persistence has been explicitly disabled.
|
|
58
|
+
*/
|
|
59
|
+
private replacementState;
|
|
60
|
+
private toolResultsDir;
|
|
61
|
+
constructor(config?: Partial<ContextManagerConfig>);
|
|
62
|
+
setOnCompact(fn: OnCompactFn): void;
|
|
63
|
+
/**
|
|
64
|
+
* Record actual token usage from API response.
|
|
65
|
+
* Used for hybrid estimation: actual + estimate for new messages.
|
|
66
|
+
*/
|
|
67
|
+
recordActualUsage(inputTokens: number, messageCount: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Best-effort token estimate: uses actual API usage as base if available,
|
|
70
|
+
* plus estimation for messages added since the last API call.
|
|
71
|
+
*/
|
|
72
|
+
private estimateTokensHybrid;
|
|
73
|
+
/**
|
|
74
|
+
* Set the summarize function (injected by Engine).
|
|
75
|
+
*/
|
|
76
|
+
setSummarizeFn(fn: SummarizeFn): void;
|
|
77
|
+
/**
|
|
78
|
+
* Set the transcript path so compaction can reference it. The
|
|
79
|
+
* tool-results directory lives alongside the transcript file:
|
|
80
|
+
* <transcriptDir>/tool-results/<toolUseId>.txt
|
|
81
|
+
*/
|
|
82
|
+
setTranscriptPath(path: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Initialize tool-result-persistence state from already-loaded messages.
|
|
85
|
+
* Call this once per session, after setTranscriptPath, when resuming so
|
|
86
|
+
* the manager re-enters the same "frozen decision" state it had on the
|
|
87
|
+
* previous run. Without this, a resumed session would re-evaluate
|
|
88
|
+
* every result against the current threshold and might flip choices.
|
|
89
|
+
*/
|
|
90
|
+
initReplacementStateFromMessages(messages: Message[]): void;
|
|
91
|
+
/**
|
|
92
|
+
* Apply progressive context management (sync path).
|
|
93
|
+
* For Tier 2 (LLM summary), call manageAsync instead.
|
|
94
|
+
*/
|
|
95
|
+
manage(messages: Message[]): Message[];
|
|
96
|
+
/**
|
|
97
|
+
* Async context management — attempts LLM summarization before falling back.
|
|
98
|
+
* Call this when you have access to the LLM (between turns).
|
|
99
|
+
*/
|
|
100
|
+
manageAsync(messages: Message[]): Promise<Message[]>;
|
|
101
|
+
/**
|
|
102
|
+
* Persist large tool_result blocks to disk and replace them with a
|
|
103
|
+
* preview + filepath. No-op when transcript path hasn't been set
|
|
104
|
+
* (e.g. unit tests that exercise compaction directly without a session).
|
|
105
|
+
*/
|
|
106
|
+
private persistLargeToolResults;
|
|
107
|
+
/**
|
|
108
|
+
* Truncate oversized tool results in messages.
|
|
109
|
+
*/
|
|
110
|
+
private truncateToolResults;
|
|
111
|
+
/**
|
|
112
|
+
* Reactive compaction check — call during streaming to detect if
|
|
113
|
+
* token usage is approaching the limit mid-turn.
|
|
114
|
+
* Returns true if emergency compaction should be triggered.
|
|
115
|
+
*/
|
|
116
|
+
shouldReactiveCompact(messages: Message[], currentResponseTokens: number): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Check if context is approaching limits.
|
|
119
|
+
*/
|
|
120
|
+
checkLimits(messages: Message[]): {
|
|
121
|
+
tokens: number;
|
|
122
|
+
ratio: number;
|
|
123
|
+
needsCompact: boolean;
|
|
124
|
+
needsEmergency: boolean;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Deduplicate tool calls by hashing arguments.
|
|
128
|
+
*/
|
|
129
|
+
deduplicateToolCalls(calls: Array<{
|
|
130
|
+
toolName: string;
|
|
131
|
+
args: Record<string, unknown>;
|
|
132
|
+
}>): {
|
|
133
|
+
toExecute: typeof calls;
|
|
134
|
+
cached: Array<{
|
|
135
|
+
toolName: string;
|
|
136
|
+
args: Record<string, unknown>;
|
|
137
|
+
result: string;
|
|
138
|
+
}>;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Record a tool call result for dedup tracking.
|
|
142
|
+
*/
|
|
143
|
+
recordToolResult(toolName: string, args: Record<string, unknown>, result: string): void;
|
|
144
|
+
private hashCall;
|
|
145
|
+
}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context manager — three-tier context management.
|
|
3
|
+
*
|
|
4
|
+
* Tier 1: microcompact (sync, zero-cost) — clear old tool_result content
|
|
5
|
+
* Tier 2: LLM summary (async) — generate summary of older messages via model call
|
|
6
|
+
* Tier 3: window compact (sync, emergency) — aggressive truncation fallback
|
|
7
|
+
*/
|
|
8
|
+
import { estimateTokens, microcompact, snipCompact, windowCompact, truncateToolResult, buildSummarizationPrompt, applySummaryCompaction, applyToolResultBudget, extractAnchoredSummary, } from "./compaction.js";
|
|
9
|
+
import { applyToolResultPersistence, createContentReplacementState, reconstructContentReplacementState, resolveToolResultsDir, } from "./tool-result-storage.js";
|
|
10
|
+
import { logger } from "../logging/logger.js";
|
|
11
|
+
const DEFAULT_CONFIG = {
|
|
12
|
+
// 0.85 ≈ effectiveContextWindow − reserved-output buffer (CC's
|
|
13
|
+
// autoCompactThreshold pattern: window − 13k buffer − 20k output budget
|
|
14
|
+
// ≈ 0.83 on a 200k window, 0.97 on 1M). 0.6 was wasting 40% of any model
|
|
15
|
+
// with a window over ~150k.
|
|
16
|
+
maxTokens: 200_000,
|
|
17
|
+
compactAtRatio: 0.85,
|
|
18
|
+
summarizeAtRatio: 0.92,
|
|
19
|
+
maxToolResultChars: 30_000,
|
|
20
|
+
// 0.7 leaves micro idle until the prompt is ~70% full. Below this we
|
|
21
|
+
// let tool_results stay intact: under-pressure context doesn't need
|
|
22
|
+
// micro and wiping early Read/Bash output just forces the model to
|
|
23
|
+
// re-fetch the same files later. CC's external path triggers around
|
|
24
|
+
// 70-75% of the window; this matches it. Was 0.5 — on a 200k model
|
|
25
|
+
// that fired at 100k where a long-but-not-overloaded conversation
|
|
26
|
+
// (~130k tokens) tripped micro every single turn, with the cleared
|
|
27
|
+
// 8 rounds promptly replaced by 5 fresh ones so the next turn was
|
|
28
|
+
// back at 130k and the cycle repeated. 0.7 → 140k floor, leaving
|
|
29
|
+
// realistic headroom before we start churning.
|
|
30
|
+
microcompactFloorRatio: 0.7,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Default keepRecent for microcompact, scaled to the model's context window.
|
|
34
|
+
* Mirrors CC's "keep recent 5" baseline but lets a 1M-window model retain
|
|
35
|
+
* proportionally more tool_result detail before clearing kicks in.
|
|
36
|
+
*/
|
|
37
|
+
function defaultKeepRecent(maxTokens) {
|
|
38
|
+
return Math.max(5, Math.floor(maxTokens / 100_000));
|
|
39
|
+
}
|
|
40
|
+
export class ContextManager {
|
|
41
|
+
config;
|
|
42
|
+
toolCallHashes = new Map();
|
|
43
|
+
summarizeFn;
|
|
44
|
+
consecutiveSummaryFailures = 0;
|
|
45
|
+
lastSummary;
|
|
46
|
+
/** Last known actual token count from API usage data. */
|
|
47
|
+
lastActualTokens;
|
|
48
|
+
/** Message count at the time lastActualTokens was recorded. */
|
|
49
|
+
lastActualAtMessageCount;
|
|
50
|
+
/** Path to session transcript — passed to summary compaction for on-demand access. */
|
|
51
|
+
transcriptPath;
|
|
52
|
+
/** Notified whenever any compaction tier fires, including microcompact. */
|
|
53
|
+
onCompact;
|
|
54
|
+
/**
|
|
55
|
+
* Tool-result persistence state. Created lazily once we know where to
|
|
56
|
+
* write files (i.e. once setTranscriptPath has been called).
|
|
57
|
+
* `null` = persistence has been explicitly disabled.
|
|
58
|
+
*/
|
|
59
|
+
replacementState;
|
|
60
|
+
toolResultsDir;
|
|
61
|
+
constructor(config) {
|
|
62
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
63
|
+
}
|
|
64
|
+
setOnCompact(fn) {
|
|
65
|
+
this.onCompact = fn;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Record actual token usage from API response.
|
|
69
|
+
* Used for hybrid estimation: actual + estimate for new messages.
|
|
70
|
+
*/
|
|
71
|
+
recordActualUsage(inputTokens, messageCount) {
|
|
72
|
+
this.lastActualTokens = inputTokens;
|
|
73
|
+
this.lastActualAtMessageCount = messageCount;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Best-effort token estimate: uses actual API usage as base if available,
|
|
77
|
+
* plus estimation for messages added since the last API call.
|
|
78
|
+
*/
|
|
79
|
+
estimateTokensHybrid(messages) {
|
|
80
|
+
if (this.lastActualTokens !== undefined &&
|
|
81
|
+
this.lastActualAtMessageCount !== undefined &&
|
|
82
|
+
this.lastActualAtMessageCount < messages.length) {
|
|
83
|
+
const newMessages = messages.slice(this.lastActualAtMessageCount);
|
|
84
|
+
const newTokens = estimateTokens(newMessages);
|
|
85
|
+
return this.lastActualTokens + newTokens;
|
|
86
|
+
}
|
|
87
|
+
return estimateTokens(messages);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Set the summarize function (injected by Engine).
|
|
91
|
+
*/
|
|
92
|
+
setSummarizeFn(fn) {
|
|
93
|
+
this.summarizeFn = fn;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Set the transcript path so compaction can reference it. The
|
|
97
|
+
* tool-results directory lives alongside the transcript file:
|
|
98
|
+
* <transcriptDir>/tool-results/<toolUseId>.txt
|
|
99
|
+
*/
|
|
100
|
+
setTranscriptPath(path) {
|
|
101
|
+
this.transcriptPath = path;
|
|
102
|
+
this.toolResultsDir = resolveToolResultsDir(path);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Initialize tool-result-persistence state from already-loaded messages.
|
|
106
|
+
* Call this once per session, after setTranscriptPath, when resuming so
|
|
107
|
+
* the manager re-enters the same "frozen decision" state it had on the
|
|
108
|
+
* previous run. Without this, a resumed session would re-evaluate
|
|
109
|
+
* every result against the current threshold and might flip choices.
|
|
110
|
+
*/
|
|
111
|
+
initReplacementStateFromMessages(messages) {
|
|
112
|
+
this.replacementState = reconstructContentReplacementState(messages);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Apply progressive context management (sync path).
|
|
116
|
+
* For Tier 2 (LLM summary), call manageAsync instead.
|
|
117
|
+
*/
|
|
118
|
+
manage(messages) {
|
|
119
|
+
let result = messages;
|
|
120
|
+
// Tier 0a: Persist large tool_results to disk + replace with preview.
|
|
121
|
+
// Runs before any in-context truncation so the model gets a real
|
|
122
|
+
// filepath it can Read back, not a silent head/tail truncation.
|
|
123
|
+
result = this.persistLargeToolResults(result);
|
|
124
|
+
// Tier 0b: Hard truncate any oversized tool_result that survived
|
|
125
|
+
// persistence (e.g. persistence disabled, FS write failed, or a
|
|
126
|
+
// block was already frozen "don't persist" on a prior pass).
|
|
127
|
+
result = this.truncateToolResults(result);
|
|
128
|
+
// Tier 0c: Aggregate tool result budget (per-message) — char-level
|
|
129
|
+
// backstop for messages still over the limit after persistence.
|
|
130
|
+
result = applyToolResultBudget(result);
|
|
131
|
+
// Tier 1: microcompact — fingerprint old whitelisted tool_results.
|
|
132
|
+
// Only runs above the floor ratio: under-pressure context keeps full
|
|
133
|
+
// detail so the model isn't forced to re-Read files it just looked at.
|
|
134
|
+
const preTier1Tokens = this.estimateTokensHybrid(result);
|
|
135
|
+
if (preTier1Tokens > this.config.maxTokens * this.config.microcompactFloorRatio) {
|
|
136
|
+
const keepRecentN = this.config.microcompactKeepRecent ?? defaultKeepRecent(this.config.maxTokens);
|
|
137
|
+
// Capture rounds/tools synchronously from microcompact's onClear, but
|
|
138
|
+
// defer the token re-estimate until AFTER `result` has been reassigned
|
|
139
|
+
// to the compacted array — onClear fires before microcompact returns,
|
|
140
|
+
// so reading `result` inside it would see the pre-compact reference.
|
|
141
|
+
let clearedInfo = null;
|
|
142
|
+
result = microcompact(result, {
|
|
143
|
+
keepRecentN,
|
|
144
|
+
onClear: (info) => {
|
|
145
|
+
clearedInfo = info;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
if (clearedInfo) {
|
|
149
|
+
const after = this.estimateTokensHybrid(result);
|
|
150
|
+
logger.info("context.microcompact", {
|
|
151
|
+
before: preTier1Tokens,
|
|
152
|
+
after,
|
|
153
|
+
keepRecentN,
|
|
154
|
+
clearedRounds: clearedInfo.clearedRounds,
|
|
155
|
+
toolNames: clearedInfo.toolNames,
|
|
156
|
+
});
|
|
157
|
+
this.onCompact?.({ strategy: "micro", before: preTier1Tokens, after });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Hybrid token estimation walks every message; we cache it across the
|
|
161
|
+
// tier checks below and only recompute after a compaction shrinks `result`.
|
|
162
|
+
let tokens = this.estimateTokensHybrid(result);
|
|
163
|
+
// Tiers run in increasing severity. Each tier short-circuits its own
|
|
164
|
+
// arm but the next tier still runs if we're still above the next gate
|
|
165
|
+
// — a single pass can escalate micro → summary-replay → snip → window
|
|
166
|
+
// → emergency when the model just dumped a flood of tool_results.
|
|
167
|
+
//
|
|
168
|
+
// Severity gates (fractions of maxTokens):
|
|
169
|
+
// compactAtRatio — start non-micro compaction (try summary first,
|
|
170
|
+
// else snip; snip is the cheapest sync option)
|
|
171
|
+
// compactAtRatio + 0.05 — snip wasn't enough, do a window compact
|
|
172
|
+
// summarizeAtRatio — emergency window (smallest keep-tail)
|
|
173
|
+
const snipGate = this.config.maxTokens * this.config.compactAtRatio;
|
|
174
|
+
const windowGate = this.config.maxTokens * (this.config.compactAtRatio + 0.05);
|
|
175
|
+
const emergencyGate = this.config.maxTokens * this.config.summarizeAtRatio;
|
|
176
|
+
// Tier 2: prefer replaying a cached summary; otherwise snip
|
|
177
|
+
if (tokens > snipGate) {
|
|
178
|
+
const before = tokens;
|
|
179
|
+
if (this.lastSummary) {
|
|
180
|
+
const keepN = Math.max(8, Math.floor(result.length * 0.3));
|
|
181
|
+
result = applySummaryCompaction(result, this.lastSummary, keepN, this.transcriptPath);
|
|
182
|
+
this.lastSummary = undefined;
|
|
183
|
+
tokens = this.estimateTokensHybrid(result);
|
|
184
|
+
this.onCompact?.({ strategy: "summary", before, after: tokens });
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
result = snipCompact(result, 3, 8);
|
|
188
|
+
tokens = this.estimateTokensHybrid(result);
|
|
189
|
+
this.onCompact?.({ strategy: "snip", before, after: tokens });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Tier 2b: snip didn't free enough — fall back to window compact
|
|
193
|
+
if (tokens > windowGate) {
|
|
194
|
+
const before = tokens;
|
|
195
|
+
const keepN = Math.max(10, Math.floor(result.length * 0.4));
|
|
196
|
+
result = windowCompact(result, keepN);
|
|
197
|
+
tokens = this.estimateTokensHybrid(result);
|
|
198
|
+
this.onCompact?.({ strategy: "window", before, after: tokens });
|
|
199
|
+
}
|
|
200
|
+
// Tier 3: emergency window with a tiny tail — last resort before the
|
|
201
|
+
// request would otherwise blow the model's context.
|
|
202
|
+
if (tokens > emergencyGate) {
|
|
203
|
+
const before = tokens;
|
|
204
|
+
result = windowCompact(result, 6);
|
|
205
|
+
tokens = this.estimateTokensHybrid(result);
|
|
206
|
+
this.onCompact?.({ strategy: "emergency", before, after: tokens });
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Async context management — attempts LLM summarization before falling back.
|
|
212
|
+
* Call this when you have access to the LLM (between turns).
|
|
213
|
+
*/
|
|
214
|
+
async manageAsync(messages) {
|
|
215
|
+
let result = messages;
|
|
216
|
+
// Tier 0a: Persist large tool_results to disk + replace with preview.
|
|
217
|
+
result = this.persistLargeToolResults(result);
|
|
218
|
+
// Tier 0b: Hard truncate oversized tool_result blocks that weren't
|
|
219
|
+
// persisted (persistence disabled, FS error, or already frozen).
|
|
220
|
+
result = this.truncateToolResults(result);
|
|
221
|
+
// Tier 0c: Aggregate tool result budget (per-message)
|
|
222
|
+
result = applyToolResultBudget(result);
|
|
223
|
+
// Tier 1: microcompact — see manage() for the rationale on the floor.
|
|
224
|
+
const preTier1Tokens = this.estimateTokensHybrid(result);
|
|
225
|
+
if (preTier1Tokens > this.config.maxTokens * this.config.microcompactFloorRatio) {
|
|
226
|
+
const keepRecentN = this.config.microcompactKeepRecent ?? defaultKeepRecent(this.config.maxTokens);
|
|
227
|
+
// See manage(): onClear fires synchronously before microcompact returns,
|
|
228
|
+
// so defer the token re-estimate until result is reassigned.
|
|
229
|
+
let clearedInfo = null;
|
|
230
|
+
result = microcompact(result, {
|
|
231
|
+
keepRecentN,
|
|
232
|
+
onClear: (info) => {
|
|
233
|
+
clearedInfo = info;
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
if (clearedInfo) {
|
|
237
|
+
const after = this.estimateTokensHybrid(result);
|
|
238
|
+
logger.info("context.microcompact", {
|
|
239
|
+
before: preTier1Tokens,
|
|
240
|
+
after,
|
|
241
|
+
keepRecentN,
|
|
242
|
+
clearedRounds: clearedInfo.clearedRounds,
|
|
243
|
+
toolNames: clearedInfo.toolNames,
|
|
244
|
+
});
|
|
245
|
+
this.onCompact?.({ strategy: "micro", before: preTier1Tokens, after });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const tokens = this.estimateTokensHybrid(result);
|
|
249
|
+
const ratio = tokens / this.config.maxTokens;
|
|
250
|
+
// Tier 2: LLM summary if approaching limit
|
|
251
|
+
if (ratio >= this.config.compactAtRatio &&
|
|
252
|
+
this.summarizeFn &&
|
|
253
|
+
this.consecutiveSummaryFailures < 3) {
|
|
254
|
+
try {
|
|
255
|
+
const keepRecentN = Math.max(8, Math.floor(result.length * 0.3));
|
|
256
|
+
const messagesToSummarize = result.slice(1, -keepRecentN); // skip first (userContext) and recent
|
|
257
|
+
if (messagesToSummarize.length > 3) {
|
|
258
|
+
// Rolling summary: if a prior summary is already anchored in the
|
|
259
|
+
// messages (from an earlier compaction in this session or in a
|
|
260
|
+
// resumed session), feed it back so the LLM merges-updates rather
|
|
261
|
+
// than re-summarizes from scratch — preserves info that would
|
|
262
|
+
// otherwise erode across successive compactions.
|
|
263
|
+
const priorSummary = extractAnchoredSummary(result) ?? this.lastSummary;
|
|
264
|
+
const prompt = buildSummarizationPrompt(messagesToSummarize, priorSummary);
|
|
265
|
+
const summary = await this.summarizeFn(prompt);
|
|
266
|
+
if (summary && summary.length > 50) {
|
|
267
|
+
result = applySummaryCompaction(result, summary, keepRecentN, this.transcriptPath);
|
|
268
|
+
this.consecutiveSummaryFailures = 0;
|
|
269
|
+
this.lastSummary = summary;
|
|
270
|
+
const after = estimateTokens(result);
|
|
271
|
+
logger.info("context.summary_compact", {
|
|
272
|
+
before: tokens,
|
|
273
|
+
after,
|
|
274
|
+
summaryLen: summary.length,
|
|
275
|
+
rolling: priorSummary !== undefined,
|
|
276
|
+
});
|
|
277
|
+
this.onCompact?.({ strategy: "summary", before: tokens, after });
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
this.consecutiveSummaryFailures++;
|
|
284
|
+
logger.warn("context.summary_failed", {
|
|
285
|
+
failures: this.consecutiveSummaryFailures,
|
|
286
|
+
error: err.message,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Reuse the `tokens` we already computed above. We only get here if the
|
|
291
|
+
// LLM summary path didn't fire (no summarizeFn, too many failures, or it
|
|
292
|
+
// threw) — fall back to the same severity ladder as manage().
|
|
293
|
+
let live = tokens;
|
|
294
|
+
const snipGate = this.config.maxTokens * this.config.compactAtRatio;
|
|
295
|
+
const windowGate = this.config.maxTokens * (this.config.compactAtRatio + 0.05);
|
|
296
|
+
const emergencyGate = this.config.maxTokens * this.config.summarizeAtRatio;
|
|
297
|
+
// Tier 2 fallback: snip first (cheapest sync option)
|
|
298
|
+
if (live > snipGate) {
|
|
299
|
+
const before = live;
|
|
300
|
+
result = snipCompact(result, 3, 8);
|
|
301
|
+
live = this.estimateTokensHybrid(result);
|
|
302
|
+
this.onCompact?.({ strategy: "snip", before, after: live });
|
|
303
|
+
}
|
|
304
|
+
// Tier 2b: window compact when snip didn't free enough
|
|
305
|
+
if (live > windowGate) {
|
|
306
|
+
const before = live;
|
|
307
|
+
const keepN = Math.max(10, Math.floor(result.length * 0.4));
|
|
308
|
+
result = windowCompact(result, keepN);
|
|
309
|
+
live = this.estimateTokensHybrid(result);
|
|
310
|
+
this.onCompact?.({ strategy: "window", before, after: live });
|
|
311
|
+
}
|
|
312
|
+
// Tier 3: Emergency
|
|
313
|
+
if (live > emergencyGate) {
|
|
314
|
+
const before = live;
|
|
315
|
+
result = windowCompact(result, 6);
|
|
316
|
+
live = this.estimateTokensHybrid(result);
|
|
317
|
+
this.onCompact?.({ strategy: "emergency", before, after: live });
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Persist large tool_result blocks to disk and replace them with a
|
|
323
|
+
* preview + filepath. No-op when transcript path hasn't been set
|
|
324
|
+
* (e.g. unit tests that exercise compaction directly without a session).
|
|
325
|
+
*/
|
|
326
|
+
persistLargeToolResults(messages) {
|
|
327
|
+
if (!this.toolResultsDir)
|
|
328
|
+
return messages;
|
|
329
|
+
if (!this.replacementState) {
|
|
330
|
+
this.replacementState = createContentReplacementState();
|
|
331
|
+
}
|
|
332
|
+
return applyToolResultPersistence(messages, {
|
|
333
|
+
toolResultsDir: this.toolResultsDir,
|
|
334
|
+
state: this.replacementState,
|
|
335
|
+
onPersist: (info) => {
|
|
336
|
+
logger.info("context.tool_result_persisted", info);
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Truncate oversized tool results in messages.
|
|
342
|
+
*/
|
|
343
|
+
truncateToolResults(messages) {
|
|
344
|
+
const maxChars = this.config.maxToolResultChars;
|
|
345
|
+
let modified = false;
|
|
346
|
+
const result = messages.map((msg) => {
|
|
347
|
+
if (!Array.isArray(msg.content))
|
|
348
|
+
return msg;
|
|
349
|
+
const newContent = msg.content.map((block) => {
|
|
350
|
+
if (block.type === "tool_result" && typeof block.content === "string") {
|
|
351
|
+
if (block.content.length > maxChars) {
|
|
352
|
+
modified = true;
|
|
353
|
+
return { ...block, content: truncateToolResult(block.content, maxChars) };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return block;
|
|
357
|
+
});
|
|
358
|
+
return modified ? { ...msg, content: newContent } : msg;
|
|
359
|
+
});
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Reactive compaction check — call during streaming to detect if
|
|
364
|
+
* token usage is approaching the limit mid-turn.
|
|
365
|
+
* Returns true if emergency compaction should be triggered.
|
|
366
|
+
*/
|
|
367
|
+
shouldReactiveCompact(messages, currentResponseTokens) {
|
|
368
|
+
const msgTokens = this.estimateTokensHybrid(messages);
|
|
369
|
+
const total = msgTokens + currentResponseTokens;
|
|
370
|
+
// Trigger at the same gate as the emergency tier — keeps the streaming
|
|
371
|
+
// reactive check aligned with the post-turn ladder.
|
|
372
|
+
return total > this.config.maxTokens * this.config.summarizeAtRatio;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Check if context is approaching limits.
|
|
376
|
+
*/
|
|
377
|
+
checkLimits(messages) {
|
|
378
|
+
const tokens = this.estimateTokensHybrid(messages);
|
|
379
|
+
const ratio = tokens / this.config.maxTokens;
|
|
380
|
+
return {
|
|
381
|
+
tokens,
|
|
382
|
+
ratio,
|
|
383
|
+
needsCompact: ratio >= this.config.compactAtRatio,
|
|
384
|
+
needsEmergency: ratio >= this.config.summarizeAtRatio,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Deduplicate tool calls by hashing arguments.
|
|
389
|
+
*/
|
|
390
|
+
deduplicateToolCalls(calls) {
|
|
391
|
+
const toExecute = [];
|
|
392
|
+
const cached = [];
|
|
393
|
+
for (const call of calls) {
|
|
394
|
+
const hash = this.hashCall(call.toolName, call.args);
|
|
395
|
+
const existing = this.toolCallHashes.get(hash);
|
|
396
|
+
if (existing && existing.count >= 2) {
|
|
397
|
+
cached.push({ ...call, result: existing.lastResult });
|
|
398
|
+
existing.count++;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
toExecute.push(call);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return { toExecute, cached };
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Record a tool call result for dedup tracking.
|
|
408
|
+
*/
|
|
409
|
+
recordToolResult(toolName, args, result) {
|
|
410
|
+
const hash = this.hashCall(toolName, args);
|
|
411
|
+
const existing = this.toolCallHashes.get(hash);
|
|
412
|
+
if (existing) {
|
|
413
|
+
existing.count++;
|
|
414
|
+
existing.lastResult = result;
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
this.toolCallHashes.set(hash, { count: 1, lastResult: result });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
hashCall(toolName, args) {
|
|
421
|
+
return `${toolName}:${JSON.stringify(args)}`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token counter — more accurate token estimation.
|
|
3
|
+
*
|
|
4
|
+
* Uses a character-based heuristic tuned per language/content type,
|
|
5
|
+
* with cache-aware accounting for prompt caching.
|
|
6
|
+
*/
|
|
7
|
+
import type { Message } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Estimate token count for a string, detecting content type.
|
|
10
|
+
*/
|
|
11
|
+
export declare function estimateStringTokens(text: string): number;
|
|
12
|
+
/**
|
|
13
|
+
* Estimate token count for a message array.
|
|
14
|
+
*/
|
|
15
|
+
export declare function estimateMessagesTokens(messages: Message[]): number;
|
|
16
|
+
/**
|
|
17
|
+
* Context window usage report.
|
|
18
|
+
*/
|
|
19
|
+
export interface ContextUsage {
|
|
20
|
+
usedTokens: number;
|
|
21
|
+
maxTokens: number;
|
|
22
|
+
usagePercent: number;
|
|
23
|
+
systemPromptTokens: number;
|
|
24
|
+
messagesTokens: number;
|
|
25
|
+
headroom: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function calculateContextUsage(messages: Message[], systemPrompt: string, maxTokens: number): ContextUsage;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token counter — more accurate token estimation.
|
|
3
|
+
*
|
|
4
|
+
* Uses a character-based heuristic tuned per language/content type,
|
|
5
|
+
* with cache-aware accounting for prompt caching.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Token-to-character ratios by content type.
|
|
9
|
+
* These are empirical averages from Claude/GPT tokenizers.
|
|
10
|
+
*/
|
|
11
|
+
const RATIOS = {
|
|
12
|
+
english: 4.0, // ~4 chars per token for English prose
|
|
13
|
+
code: 3.2, // ~3.2 chars per token for code (more tokens per char)
|
|
14
|
+
json: 3.5, // JSON structures
|
|
15
|
+
mixed: 3.6, // Mixed content (typical for tool results)
|
|
16
|
+
cjk: 1.5, // CJK characters: ~1.5 chars per token
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Estimate token count for a string, detecting content type.
|
|
20
|
+
*/
|
|
21
|
+
export function estimateStringTokens(text) {
|
|
22
|
+
if (!text)
|
|
23
|
+
return 0;
|
|
24
|
+
// Detect CJK content ratio
|
|
25
|
+
const cjkChars = (text.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) ?? []).length;
|
|
26
|
+
const cjkRatio = cjkChars / text.length;
|
|
27
|
+
if (cjkRatio > 0.3) {
|
|
28
|
+
// Weighted average for mixed CJK/ASCII
|
|
29
|
+
const cjkTokens = cjkChars / RATIOS.cjk;
|
|
30
|
+
const otherChars = text.length - cjkChars;
|
|
31
|
+
const otherTokens = otherChars / RATIOS.english;
|
|
32
|
+
return Math.ceil(cjkTokens + otherTokens);
|
|
33
|
+
}
|
|
34
|
+
// Detect code
|
|
35
|
+
const codeIndicators = ["{", "}", "(", ")", ";", "=>", "import ", "function ", "class ", "const "];
|
|
36
|
+
const codeScore = codeIndicators.reduce((s, ind) => s + (text.includes(ind) ? 1 : 0), 0);
|
|
37
|
+
if (codeScore >= 3)
|
|
38
|
+
return Math.ceil(text.length / RATIOS.code);
|
|
39
|
+
if (text.startsWith("{") || text.startsWith("["))
|
|
40
|
+
return Math.ceil(text.length / RATIOS.json);
|
|
41
|
+
return Math.ceil(text.length / RATIOS.english);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Estimate token count for a message array.
|
|
45
|
+
*/
|
|
46
|
+
export function estimateMessagesTokens(messages) {
|
|
47
|
+
let total = 0;
|
|
48
|
+
for (const msg of messages) {
|
|
49
|
+
// Per-message overhead (role, formatting) ≈ 4 tokens
|
|
50
|
+
total += 4;
|
|
51
|
+
if (typeof msg.content === "string") {
|
|
52
|
+
total += estimateStringTokens(msg.content);
|
|
53
|
+
}
|
|
54
|
+
else if (Array.isArray(msg.content)) {
|
|
55
|
+
for (const block of msg.content) {
|
|
56
|
+
total += estimateBlockTokens(block);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return total;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Estimate tokens for a content block.
|
|
64
|
+
*/
|
|
65
|
+
function estimateBlockTokens(block) {
|
|
66
|
+
let tokens = 2; // Block overhead
|
|
67
|
+
if (block.text)
|
|
68
|
+
tokens += estimateStringTokens(block.text);
|
|
69
|
+
if (block.content && typeof block.content === "string") {
|
|
70
|
+
tokens += estimateStringTokens(block.content);
|
|
71
|
+
}
|
|
72
|
+
if (block.input) {
|
|
73
|
+
tokens += estimateStringTokens(JSON.stringify(block.input));
|
|
74
|
+
}
|
|
75
|
+
if (block.name)
|
|
76
|
+
tokens += estimateStringTokens(block.name);
|
|
77
|
+
return tokens;
|
|
78
|
+
}
|
|
79
|
+
export function calculateContextUsage(messages, systemPrompt, maxTokens) {
|
|
80
|
+
const systemPromptTokens = estimateStringTokens(systemPrompt);
|
|
81
|
+
const messagesTokens = estimateMessagesTokens(messages);
|
|
82
|
+
const usedTokens = systemPromptTokens + messagesTokens;
|
|
83
|
+
return {
|
|
84
|
+
usedTokens,
|
|
85
|
+
maxTokens,
|
|
86
|
+
usagePercent: (usedTokens / maxTokens) * 100,
|
|
87
|
+
systemPromptTokens,
|
|
88
|
+
messagesTokens,
|
|
89
|
+
headroom: maxTokens - usedTokens,
|
|
90
|
+
};
|
|
91
|
+
}
|