@superblocksteam/vite-plugin-file-sync 2.0.160-next.0 → 2.0.161-next.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/dist/ai-service/agent/middleware.d.ts +1 -2
- package/dist/ai-service/agent/middleware.d.ts.map +1 -1
- package/dist/ai-service/agent/middleware.js +2 -3
- package/dist/ai-service/agent/middleware.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +5 -4
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
- package/dist/ai-service/agent/tool-message-utils.js +16 -1
- package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
- package/dist/ai-service/agent/tool-result-metrics.d.ts +29 -0
- package/dist/ai-service/agent/tool-result-metrics.d.ts.map +1 -0
- package/dist/ai-service/agent/tool-result-metrics.js +162 -0
- package/dist/ai-service/agent/tool-result-metrics.js.map +1 -0
- package/dist/ai-service/agent/tools/apis/test-api.d.ts +50 -1
- package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/test-api.js +132 -10
- package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
- package/dist/ai-service/agent/tools/build-delete-file.d.ts +1 -2
- package/dist/ai-service/agent/tools/build-delete-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-delete-file.js +2 -0
- package/dist/ai-service/agent/tools/build-delete-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-edit-file.d.ts +1 -2
- package/dist/ai-service/agent/tools/build-edit-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-edit-file.js +2 -0
- package/dist/ai-service/agent/tools/build-edit-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.d.ts +1 -0
- package/dist/ai-service/agent/tools/build-install-packages.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.js +123 -16
- package/dist/ai-service/agent/tools/build-install-packages.js.map +1 -1
- package/dist/ai-service/agent/tools/build-lookup-npm-package.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-lookup-npm-package.js +4 -16
- package/dist/ai-service/agent/tools/build-lookup-npm-package.js.map +1 -1
- package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +11 -7
- package/dist/ai-service/agent/tools/build-manage-checklist.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-manage-checklist.js +18 -9
- package/dist/ai-service/agent/tools/build-manage-checklist.js.map +1 -1
- package/dist/ai-service/agent/tools/build-multi-edit-file.d.ts +1 -2
- package/dist/ai-service/agent/tools/build-multi-edit-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-multi-edit-file.js +2 -0
- package/dist/ai-service/agent/tools/build-multi-edit-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-read-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-read-file.js +31 -18
- package/dist/ai-service/agent/tools/build-read-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-rename-file.d.ts +1 -2
- package/dist/ai-service/agent/tools/build-rename-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-rename-file.js +2 -0
- package/dist/ai-service/agent/tools/build-rename-file.js.map +1 -1
- package/dist/ai-service/agent/tools/build-write-file.d.ts +1 -2
- package/dist/ai-service/agent/tools/build-write-file.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-write-file.js +2 -0
- package/dist/ai-service/agent/tools/build-write-file.js.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts +3 -3
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.js +5 -7
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.js.map +1 -1
- package/dist/ai-service/agent/tools/debug-cache.d.ts +16 -14
- package/dist/ai-service/agent/tools/debug-cache.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/debug-cache.js +200 -42
- package/dist/ai-service/agent/tools/debug-cache.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +13 -13
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.js +52 -20
- package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts +7 -1
- package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/sql-classification.js +229 -11
- package/dist/ai-service/agent/tools/integrations/sql-classification.js.map +1 -1
- package/dist/ai-service/agent/tools/page-edit-guidance.d.ts +2 -0
- package/dist/ai-service/agent/tools/page-edit-guidance.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/page-edit-guidance.js +11 -0
- package/dist/ai-service/agent/tools/page-edit-guidance.js.map +1 -0
- package/dist/ai-service/agent/tools/report-security-findings.d.ts +5 -5
- package/dist/ai-service/agent/tools.d.ts +32 -0
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +54 -33
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/registry.d.ts +1 -1
- package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/registry.js +21 -4
- package/dist/ai-service/agent/tools2/registry.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/end-test-run.d.ts +2 -2
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +20 -11
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.d.ts +4 -0
- package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js +28 -8
- package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/update-test-case-status.d.ts +2 -2
- package/dist/ai-service/app-interface/cancellable-exec.d.ts +34 -0
- package/dist/ai-service/app-interface/cancellable-exec.d.ts.map +1 -0
- package/dist/ai-service/app-interface/cancellable-exec.js +98 -0
- package/dist/ai-service/app-interface/cancellable-exec.js.map +1 -0
- package/dist/ai-service/app-interface/shell.d.ts +20 -3
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +139 -65
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/attachment-upload.d.ts.map +1 -1
- package/dist/ai-service/attachment-upload.js +4 -1
- package/dist/ai-service/attachment-upload.js.map +1 -1
- package/dist/ai-service/attachments/parse-data-url.d.ts +5 -0
- package/dist/ai-service/attachments/parse-data-url.d.ts.map +1 -0
- package/dist/ai-service/attachments/parse-data-url.js +14 -0
- package/dist/ai-service/attachments/parse-data-url.js.map +1 -0
- package/dist/ai-service/chat/chat-session-store.d.ts +0 -8
- package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-session-store.js +34 -611
- package/dist/ai-service/chat/chat-session-store.js.map +1 -1
- package/dist/ai-service/chat/utils.d.ts +2 -6
- package/dist/ai-service/chat/utils.d.ts.map +1 -1
- package/dist/ai-service/chat/utils.js +2 -95
- package/dist/ai-service/chat/utils.js.map +1 -1
- package/dist/ai-service/index.d.ts +3 -2
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +138 -48
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/integrations/mention-safe-tag-name.d.ts +12 -0
- package/dist/ai-service/integrations/mention-safe-tag-name.d.ts.map +1 -0
- package/dist/ai-service/integrations/mention-safe-tag-name.js +18 -0
- package/dist/ai-service/integrations/mention-safe-tag-name.js.map +1 -0
- package/dist/ai-service/integrations/store.d.ts +9 -0
- package/dist/ai-service/integrations/store.d.ts.map +1 -1
- package/dist/ai-service/integrations/store.js +11 -1
- package/dist/ai-service/integrations/store.js.map +1 -1
- package/dist/ai-service/llm/client.d.ts.map +1 -1
- package/dist/ai-service/llm/client.js +67 -4
- package/dist/ai-service/llm/client.js.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.d.ts +1 -0
- package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.js +3 -0
- package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts +7 -0
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/client-side.js +28 -6
- package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +12 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compactor.d.ts +8 -0
- package/dist/ai-service/llm/context-v2/compactor.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compactor.js +15 -3
- package/dist/ai-service/llm/context-v2/compactor.js.map +1 -1
- package/dist/ai-service/llm/context-v2/config.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/config.js +1 -0
- package/dist/ai-service/llm/context-v2/config.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts +44 -1
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context-metrics.js +97 -12
- package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context.d.ts +125 -0
- package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context.js +478 -14
- package/dist/ai-service/llm/context-v2/context.js.map +1 -1
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts +12 -0
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js +2 -1
- package/dist/ai-service/llm/context-v2/phase1-tool-summarizer.js.map +1 -1
- package/dist/ai-service/llm/context-v2/phase2-session-summarizer.js +3 -3
- package/dist/ai-service/llm/context-v2/phase2-session-summarizer.js.map +1 -1
- package/dist/ai-service/llm/context-v2/token-tracker.d.ts +49 -1
- package/dist/ai-service/llm/context-v2/token-tracker.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/token-tracker.js +119 -0
- package/dist/ai-service/llm/context-v2/token-tracker.js.map +1 -1
- package/dist/ai-service/llm/context-v2/truncation-fallback.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/truncation-fallback.js +2 -1
- package/dist/ai-service/llm/context-v2/truncation-fallback.js.map +1 -1
- package/dist/ai-service/llm/context-v2/types.d.ts +9 -0
- package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/types.js.map +1 -1
- package/dist/ai-service/llm/context-v2/well-formed-text.d.ts +24 -0
- package/dist/ai-service/llm/context-v2/well-formed-text.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/well-formed-text.js +73 -0
- package/dist/ai-service/llm/context-v2/well-formed-text.js.map +1 -0
- package/dist/ai-service/llm/error.d.ts +22 -1
- package/dist/ai-service/llm/error.d.ts.map +1 -1
- package/dist/ai-service/llm/error.js +159 -22
- package/dist/ai-service/llm/error.js.map +1 -1
- package/dist/ai-service/llm/stream/empty-tool-call-tracker.d.ts +4 -3
- package/dist/ai-service/llm/stream/empty-tool-call-tracker.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/empty-tool-call-tracker.js +5 -4
- package/dist/ai-service/llm/stream/empty-tool-call-tracker.js.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.js +5 -0
- package/dist/ai-service/llm/stream/idle-monitor.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/logging.d.ts +6 -0
- package/dist/ai-service/llm/stream/observers/logging.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/logging.js +44 -3
- package/dist/ai-service/llm/stream/observers/logging.js.map +1 -1
- package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/orchestrator.js +28 -4
- package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
- package/dist/ai-service/llm/stream/retry-engine.d.ts +1 -1
- package/dist/ai-service/llm/stream/retry-engine.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/retry-engine.js +9 -1
- package/dist/ai-service/llm/stream/retry-engine.js.map +1 -1
- package/dist/ai-service/llm/stream/session.d.ts +6 -0
- package/dist/ai-service/llm/stream/session.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/session.js +6 -0
- package/dist/ai-service/llm/stream/session.js.map +1 -1
- package/dist/ai-service/llm/stream/terminal-reason.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/terminal-reason.js +11 -1
- package/dist/ai-service/llm/stream/terminal-reason.js.map +1 -1
- package/dist/ai-service/llm/stream/wait-state-tracker.d.ts +4 -3
- package/dist/ai-service/llm/stream/wait-state-tracker.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/wait-state-tracker.js +12 -8
- package/dist/ai-service/llm/stream/wait-state-tracker.js.map +1 -1
- package/dist/ai-service/policy-agent-metrics.d.ts +2 -1
- package/dist/ai-service/policy-agent-metrics.d.ts.map +1 -1
- package/dist/ai-service/policy-agent-metrics.js +11 -2
- package/dist/ai-service/policy-agent-metrics.js.map +1 -1
- package/dist/ai-service/policy-gate-error.d.ts +3 -0
- package/dist/ai-service/policy-gate-error.d.ts.map +1 -0
- package/dist/ai-service/policy-gate-error.js +33 -0
- package/dist/ai-service/policy-gate-error.js.map +1 -0
- package/dist/ai-service/resolve-generation-state-for-ui.js +1 -1
- package/dist/ai-service/resolve-generation-state-for-ui.js.map +1 -1
- package/dist/ai-service/security/secret-scanner-service.d.ts +3 -6
- package/dist/ai-service/security/secret-scanner-service.d.ts.map +1 -1
- package/dist/ai-service/security/secret-scanner-service.js +229 -97
- package/dist/ai-service/security/secret-scanner-service.js.map +1 -1
- package/dist/ai-service/security/secret-scanner.d.ts +28 -5
- package/dist/ai-service/security/secret-scanner.d.ts.map +1 -1
- package/dist/ai-service/security/secret-scanner.js +197 -42
- package/dist/ai-service/security/secret-scanner.js.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +49 -0
- package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +1 -0
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.js.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts +2 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +74 -3
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +1 -11
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.d.ts +3 -2
- package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.js +25 -5
- package/dist/ai-service/state-machine/helpers/policy-gate-usage-metrics.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.js +1 -0
- package/dist/ai-service/state-machine/helpers/prompt-billing-metrics.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +4 -0
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.js +6 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -1
- package/dist/ai-service/test-utils/metrics-harness.d.ts +6 -0
- package/dist/ai-service/test-utils/metrics-harness.d.ts.map +1 -1
- package/dist/ai-service/test-utils/metrics-harness.js.map +1 -1
- package/dist/ai-service/transform/api-builder/shared.d.ts.map +1 -1
- package/dist/ai-service/transform/api-builder/shared.js +2 -1
- package/dist/ai-service/transform/api-builder/shared.js.map +1 -1
- package/dist/dev-server-ws-smoke-client.d.ts.map +1 -1
- package/dist/dev-server-ws-smoke-client.js +1 -0
- package/dist/dev-server-ws-smoke-client.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts +21 -0
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +131 -11
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +2 -2
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +2 -12
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/migration-templates/app-fullstack/.gitignore +50 -0
- package/dist/parsing/jsx.d.ts +12 -0
- package/dist/parsing/jsx.d.ts.map +1 -1
- package/dist/parsing/jsx.js +30 -12
- package/dist/parsing/jsx.js.map +1 -1
- package/dist/parsing/page.d.ts.map +1 -1
- package/dist/parsing/page.js +16 -8
- package/dist/parsing/page.js.map +1 -1
- package/dist/parsing/parse-tsx-module.d.ts +4 -0
- package/dist/parsing/parse-tsx-module.d.ts.map +1 -0
- package/dist/parsing/parse-tsx-module.js +13 -0
- package/dist/parsing/parse-tsx-module.js.map +1 -0
- package/dist/plugin-options.d.ts +4 -0
- package/dist/plugin-options.d.ts.map +1 -1
- package/dist/plugin-options.js.map +1 -1
- package/dist/policy-gate-callback-mapper.d.ts +2 -1
- package/dist/policy-gate-callback-mapper.d.ts.map +1 -1
- package/dist/policy-gate-callback-mapper.js +3 -5
- package/dist/policy-gate-callback-mapper.js.map +1 -1
- package/dist/policy-gate-run-registry.d.ts +4 -2
- package/dist/policy-gate-run-registry.d.ts.map +1 -1
- package/dist/policy-gate-run-registry.js +17 -2
- package/dist/policy-gate-run-registry.js.map +1 -1
- package/dist/policy-gate-runner.d.ts +1 -1
- package/dist/policy-gate-runner.d.ts.map +1 -1
- package/dist/policy-gate-runner.js +41 -7
- package/dist/policy-gate-runner.js.map +1 -1
- package/dist/router-parser-metrics.d.ts +2 -0
- package/dist/router-parser-metrics.d.ts.map +1 -0
- package/dist/router-parser-metrics.js +13 -0
- package/dist/router-parser-metrics.js.map +1 -0
- package/dist/router-parser.d.ts +6 -1
- package/dist/router-parser.d.ts.map +1 -1
- package/dist/router-parser.js +72 -17
- package/dist/router-parser.js.map +1 -1
- package/dist/scan-scope-merge.d.ts +1 -1
- package/dist/scan-scope-merge.js +1 -1
- package/dist/source-tracker.d.ts.map +1 -1
- package/dist/source-tracker.js +2 -6
- package/dist/source-tracker.js.map +1 -1
- package/dist/sync-service/download.d.ts.map +1 -1
- package/dist/sync-service/download.js +10 -2
- package/dist/sync-service/download.js.map +1 -1
- package/dist/sync-service/index.d.ts +5 -0
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +66 -14
- package/dist/sync-service/index.js.map +1 -1
- package/dist/sync-service/list-dir.d.ts.map +1 -1
- package/dist/sync-service/list-dir.js +8 -4
- package/dist/sync-service/list-dir.js.map +1 -1
- package/dist/sync-service/snapshot/upload-snapshot.d.ts.map +1 -1
- package/dist/sync-service/snapshot/upload-snapshot.js +20 -0
- package/dist/sync-service/snapshot/upload-snapshot.js.map +1 -1
- package/dist/util/log-sanitizer.d.ts.map +1 -1
- package/dist/util/log-sanitizer.js +10 -8
- package/dist/util/log-sanitizer.js.map +1 -1
- package/dist/util/logger.d.ts +2 -0
- package/dist/util/logger.d.ts.map +1 -1
- package/dist/util/logger.js +357 -38
- package/dist/util/logger.js.map +1 -1
- package/dist/util.d.ts +3 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +2 -2
- package/dist/util.js.map +1 -1
- package/package.json +9 -9
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { performance } from "node:perf_hooks";
|
|
3
3
|
import { setImmediate } from "node:timers/promises";
|
|
4
|
-
import { getPrefixedLogger } from "../../../util/logger.js";
|
|
4
|
+
import { getErrorMeta, getPrefixedLogger } from "../../../util/logger.js";
|
|
5
5
|
import { LLMObsTracer } from "../../llmobs/tracer.js";
|
|
6
6
|
import { findBreakpoints } from "./caching.js";
|
|
7
7
|
import { SERVER_COMPACTION_INTERRUPTED_SUMMARY, SERVER_COMPACTION_SUMMARY, ServerSideCompactionStrategy, } from "./compaction/server-side.js";
|
|
8
|
-
import { DEFAULT_CACHING, resolveContextOptionsV2 } from "./config.js";
|
|
9
|
-
import { recordContextPersist, recordServerCompaction, recordTurnsSinceLastCompaction, } from "./context-metrics.js";
|
|
8
|
+
import { compactionThresholdTokens, DEFAULT_CACHING, resolveContextOptionsV2, } from "./config.js";
|
|
9
|
+
import { recordContextPersist, recordPreflightCompaction, recordServerCompaction, recordTurnsSinceLastCompaction, } from "./context-metrics.js";
|
|
10
10
|
import { OperationQueue } from "./operation-queue.js";
|
|
11
11
|
import { CLIENT_COMPACTION_MARKER } from "./storage/event-types.js";
|
|
12
|
-
import { getTotalInputTokens } from "./token-tracker.js";
|
|
12
|
+
import { estimateMessagesTokens, getTotalInputTokens, } from "./token-tracker.js";
|
|
13
13
|
import { toStoredMessage, toSdkMessages, CACHE_BREAKPOINT_PROVIDER_OPTIONS, normalizeToolMessage, stripUndefinedFromToolOutputs, repairBrokenToolCalls, repairOrphanedToolResults, enforceToolUsePairing, normalizeReasoningProviderOptions, stripUnsignedReasoning, stripTrailingAssistantPrefill, extractTokenUsage, } from "./types.js";
|
|
14
14
|
import { uniquifyFilePartFilenames } from "./uniquify-filenames.js";
|
|
15
|
+
import { makeJsonSafeValue } from "./well-formed-text.js";
|
|
15
16
|
const logger = getPrefixedLogger("[context-v2]");
|
|
16
17
|
/**
|
|
17
18
|
* Persists slower than this get a warning log with the message count.
|
|
@@ -21,6 +22,71 @@ const logger = getPrefixedLogger("[context-v2]");
|
|
|
21
22
|
* Raise or remove once per-turn full-file rewrites are addressed.
|
|
22
23
|
*/
|
|
23
24
|
const SLOW_PERSIST_WARN_MS = 500;
|
|
25
|
+
/**
|
|
26
|
+
* Deadline for the pre-flight oversize compaction pass.
|
|
27
|
+
*
|
|
28
|
+
* Sits just above the summarizer's own per-call budget
|
|
29
|
+
* (`DEFAULT_COMPACTION_SUMMARY_TIMEOUT_MS`, 240s) so the deadline stops firing
|
|
30
|
+
* before the work it is waiting on can even finish.
|
|
31
|
+
*
|
|
32
|
+
* Picked from production rather than from taste. Measured over the four weeks
|
|
33
|
+
* to 2026-09-01, client-side compaction
|
|
34
|
+
* (`superblocks.clark.context.server_compaction.duration_seconds`, filtered to
|
|
35
|
+
* `path:client`) ran 6,690 times with a mean of 36.8s, an hourly-worst-case p99
|
|
36
|
+
* of 238.7s, and a peak of 382.8s. The earlier 120s value sat under the p90 of
|
|
37
|
+
* the hourly maxima, so it abandoned passes that would have succeeded on
|
|
38
|
+
* exactly the largest histories, which are the ones this check exists to
|
|
39
|
+
* rescue. Even 250s does not cover the peak; a pass past it degrades to the
|
|
40
|
+
* provider rejecting the request, reported as `context_too_large`.
|
|
41
|
+
*
|
|
42
|
+
* A longer deadline is only affordable because of the latch. Without
|
|
43
|
+
* `oversizeCompactionExhausted` a doomed pass would be restarted on every
|
|
44
|
+
* prompt, so the wait would be paid per message rather than once per
|
|
45
|
+
* conversation.
|
|
46
|
+
*
|
|
47
|
+
* `compact()` accepts no abort signal, so a pass past its deadline is abandoned
|
|
48
|
+
* rather than cancelled and keeps running (and billing) in the background.
|
|
49
|
+
* `oversizeCompactionSettled` is what keeps a sibling pass out while that
|
|
50
|
+
* orphan is live; raising this value without real cancellation lengthens the
|
|
51
|
+
* orphan window it has to cover.
|
|
52
|
+
*/
|
|
53
|
+
export const PREFLIGHT_COMPACTION_TIMEOUT_MS = 250_000;
|
|
54
|
+
/** Thrown by `withDeadline` so a slow pass can be told apart from a broken one. */
|
|
55
|
+
class DeadlineExceededError extends Error {
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve `promise`, or reject once `timeoutMs` elapses.
|
|
59
|
+
*
|
|
60
|
+
* Deliberately local rather than reusing `withRpcTimeout`: that helper logs
|
|
61
|
+
* "RPC timed out", which would misdirect anyone triaging a summarizer stall.
|
|
62
|
+
*/
|
|
63
|
+
async function withDeadline(promise, timeoutMs) {
|
|
64
|
+
let timeoutId;
|
|
65
|
+
try {
|
|
66
|
+
return await Promise.race([
|
|
67
|
+
promise,
|
|
68
|
+
new Promise((_resolve, reject) => {
|
|
69
|
+
timeoutId = setTimeout(() => reject(new DeadlineExceededError(`compaction did not settle within ${timeoutMs}ms`)), timeoutMs);
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
if (timeoutId !== undefined) {
|
|
75
|
+
clearTimeout(timeoutId);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Which family a compaction strategy belongs to.
|
|
81
|
+
*
|
|
82
|
+
* Read by instance rather than from a field on the interface: there are exactly
|
|
83
|
+
* two implementations, `ServerSideCompactionStrategy` is already imported here,
|
|
84
|
+
* and a `kind` member would have to be added to every strategy and every test
|
|
85
|
+
* mock to answer the same question.
|
|
86
|
+
*/
|
|
87
|
+
function compactionStrategyKind(strategy) {
|
|
88
|
+
return strategy instanceof ServerSideCompactionStrategy ? "server" : "client";
|
|
89
|
+
}
|
|
24
90
|
/** Messages replayed between explicit event-loop yields in initializeWith. */
|
|
25
91
|
const REPLAY_YIELD_INTERVAL = 100;
|
|
26
92
|
/**
|
|
@@ -163,6 +229,49 @@ export class ContextV2 {
|
|
|
163
229
|
// When disposed, this context may still be referenced by in-flight streams.
|
|
164
230
|
// Keep it inert so stale generations cannot persist cleared history.
|
|
165
231
|
isDisposed = false;
|
|
232
|
+
/** Shared by concurrent callers so only one oversize pass ever runs. */
|
|
233
|
+
oversizeCompactionInFlight;
|
|
234
|
+
/**
|
|
235
|
+
* Largest `extraInputTokens` any caller attached to the in-flight pass.
|
|
236
|
+
*
|
|
237
|
+
* Callers sharing a pass can have sized different tool surfaces, and the
|
|
238
|
+
* shared promise would otherwise silently decide on whichever one happened to
|
|
239
|
+
* start it. Max fails safe: under-counting skips a compaction the request
|
|
240
|
+
* needed, over-counting only runs one it could have skipped. Read when the
|
|
241
|
+
* queued callback actually runs, so anything attaching before then counts.
|
|
242
|
+
*
|
|
243
|
+
* Defensive rather than an observed bug: both callers have to be on the SAME
|
|
244
|
+
* ContextV2, and the nested one-shot path uses `context: { mode: "stateless" }`,
|
|
245
|
+
* so it never shares this instance.
|
|
246
|
+
*/
|
|
247
|
+
oversizeCompactionExtraTokens = 0;
|
|
248
|
+
/**
|
|
249
|
+
* The summarizer call itself, unbounded by the deadline the caller waits on.
|
|
250
|
+
* `compactIfOversized` holds the in-flight guard until this settles, so an
|
|
251
|
+
* abandoned `compact()` cannot run alongside a fresh pass.
|
|
252
|
+
*/
|
|
253
|
+
oversizeCompactionSettled;
|
|
254
|
+
/**
|
|
255
|
+
* Set once a pass has ended with the payload STILL over the threshold, for
|
|
256
|
+
* any reason: the strategy declined, the pass threw, the deadline rejected,
|
|
257
|
+
* or it ran and reclaimed too little. While set, `compactIfOversized` returns
|
|
258
|
+
* immediately and starts no new paid pass.
|
|
259
|
+
*
|
|
260
|
+
* The restore-time version of this check got the same protection for free,
|
|
261
|
+
* because `ContextManagerV2` only invoked it while a pending flag was set and
|
|
262
|
+
* a pass that tried and failed never re-flagged. Moving the check onto every
|
|
263
|
+
* `streamText` dropped that, so a summarizer that hangs would stall every
|
|
264
|
+
* later prompt for the full deadline and stack concurrent billable
|
|
265
|
+
* summarizations over the same history. The provider rejecting an oversized
|
|
266
|
+
* request takes ~130ms; this took two minutes per prompt. Worse than the
|
|
267
|
+
* wedge it exists to fix.
|
|
268
|
+
*
|
|
269
|
+
* Deliberately NOT cleared when a pass succeeds and the payload now fits, so
|
|
270
|
+
* later regrowth can still be rescued. Reset only in `setCompactionStrategy`:
|
|
271
|
+
* a different strategy is the one real change that could produce a different
|
|
272
|
+
* outcome, and New Chat resets it for free by constructing a fresh instance.
|
|
273
|
+
*/
|
|
274
|
+
oversizeCompactionExhausted = false;
|
|
166
275
|
// Persists currently mid-flight. A persist that has already passed the
|
|
167
276
|
// dispose guard is committed to writing the snapshot, so `dispose()` alone
|
|
168
277
|
// cannot stop it; `drainPersists()` lets a caller that is about to overwrite
|
|
@@ -218,9 +327,23 @@ export class ContextV2 {
|
|
|
218
327
|
setCompactionStrategy(strategy) {
|
|
219
328
|
if (this.compaction === strategy)
|
|
220
329
|
return;
|
|
330
|
+
const previousKind = compactionStrategyKind(this.compaction);
|
|
221
331
|
this.compaction = strategy;
|
|
222
332
|
this.compaction.updateConfig(this.config);
|
|
223
333
|
this.compaction.setCallbacks(this.compactionCallbacks);
|
|
334
|
+
// Only a change of KIND clears the latch, not any swap.
|
|
335
|
+
//
|
|
336
|
+
// `buildCompactionStrategy` returns a fresh instance every time and the
|
|
337
|
+
// manager calls this on every managed turn, so the reference guard above
|
|
338
|
+
// never short-circuits in production. Clearing on any swap therefore
|
|
339
|
+
// cleared the latch on every single prompt, which made it do nothing and
|
|
340
|
+
// put the per-prompt stall straight back. Kind is the thing that actually
|
|
341
|
+
// changes the answer: the `ai.server-context-management.options` kill
|
|
342
|
+
// switch flipping a latched server-side context over to a client-side one
|
|
343
|
+
// is a genuine second chance, another client instance is not.
|
|
344
|
+
if (compactionStrategyKind(strategy) !== previousKind) {
|
|
345
|
+
this.oversizeCompactionExhausted = false;
|
|
346
|
+
}
|
|
224
347
|
}
|
|
225
348
|
setMetricIdentity(identity) {
|
|
226
349
|
this.metricIdentity = identity;
|
|
@@ -669,6 +792,327 @@ export class ContextV2 {
|
|
|
669
792
|
}
|
|
670
793
|
});
|
|
671
794
|
}
|
|
795
|
+
/**
|
|
796
|
+
* Compact this conversation when the request about to be sent is already too
|
|
797
|
+
* large for the model, BEFORE sending it.
|
|
798
|
+
*
|
|
799
|
+
* A long session can grow its persisted history past the model's input limit
|
|
800
|
+
* without any single live request crossing the compaction threshold: the
|
|
801
|
+
* per-request prompt stays small while storage keeps accumulating. When a
|
|
802
|
+
* dev-server restart or warm-pool recycle then restores that history in full,
|
|
803
|
+
* the first request is rejected by the provider before it reports any usage.
|
|
804
|
+
* Since `afterResponse` is the only compaction trigger and it needs
|
|
805
|
+
* provider-reported usage, compaction can never fire again, every retry
|
|
806
|
+
* replays the same oversized payload, and the app is wedged permanently
|
|
807
|
+
* (ENG-5282).
|
|
808
|
+
*
|
|
809
|
+
* Breaking that cycle needs a check that does not depend on the provider, so
|
|
810
|
+
* the size is estimated from the payload's own content. The estimate only
|
|
811
|
+
* gates the decision. The compaction pipeline, thresholds, and phase
|
|
812
|
+
* selection are the existing ones, so behavior below the threshold is
|
|
813
|
+
* unchanged.
|
|
814
|
+
*
|
|
815
|
+
* Call this from where the request is assembled, after the system prompt is
|
|
816
|
+
* set and the turn is appended: the estimate then covers the array that is
|
|
817
|
+
* actually going on the wire, the UI compaction callbacks are already
|
|
818
|
+
* attached so the user sees the summarization, and a concurrent eviction is
|
|
819
|
+
* handled by the same `isDisposed` guard as the per-response trigger.
|
|
820
|
+
*
|
|
821
|
+
* At most one pass runs at a time, and a pass that ends with the payload
|
|
822
|
+
* still over the threshold latches the context so no later turn starts
|
|
823
|
+
* another paid attempt. See `oversizeCompactionExhausted`.
|
|
824
|
+
*
|
|
825
|
+
* @param extraInputTokens tokens the request carries that are not in the
|
|
826
|
+
* message array, principally the tool-definition schemas. The caller sizes
|
|
827
|
+
* them because it is the layer that holds them; this class stays free of
|
|
828
|
+
* any dependency on the agent tool types. Measured against the five
|
|
829
|
+
* captured production sessions, the estimate reads 0.19x to 1.23x of
|
|
830
|
+
* provider-reported usage, and tool schemas are the largest identified
|
|
831
|
+
* part of that shortfall. Omitting this is exactly today's behavior.
|
|
832
|
+
* Accepts a thunk so the caller's sizing work (schema conversion and
|
|
833
|
+
* serialization of every tool) is skipped entirely when the kill switch is
|
|
834
|
+
* off or the context is latched; a plain number is evaluated by the caller
|
|
835
|
+
* regardless.
|
|
836
|
+
* @returns `true` only when a pass ran and the payload now fits. `false`
|
|
837
|
+
* when it already fit (the common case), when no strategy could act, when
|
|
838
|
+
* the pass failed or timed out, or when it ran and reclaimed too little.
|
|
839
|
+
*/
|
|
840
|
+
async compactIfOversized(extraInputTokens = 0) {
|
|
841
|
+
if (this.isDisposed ||
|
|
842
|
+
this.messages.length === 0 ||
|
|
843
|
+
!this.config.preflightCompactionEnabled) {
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
if (this.oversizeCompactionExhausted) {
|
|
847
|
+
// The metric is anonymous by design (identity is never a label), so this
|
|
848
|
+
// line is what lets oncall find WHICH session is stuck when the `latched`
|
|
849
|
+
// count rises. One line per prompt on a wedged session is the cost.
|
|
850
|
+
logger.warn("Skipping oversize compaction: an earlier pass on this context already ended over the threshold", this.metricIdentity);
|
|
851
|
+
recordPreflightCompaction({
|
|
852
|
+
outcome: "latched",
|
|
853
|
+
durationMs: 0,
|
|
854
|
+
identity: this.metricIdentity,
|
|
855
|
+
});
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
const extraTokens = typeof extraInputTokens === "function"
|
|
859
|
+
? extraInputTokens()
|
|
860
|
+
: extraInputTokens;
|
|
861
|
+
// One pass at a time, and concurrent callers share it rather than stacking
|
|
862
|
+
// paid summarizations over the same history. A later caller may have sized
|
|
863
|
+
// a larger tool surface, so the pass decides on the largest figure attached
|
|
864
|
+
// to it rather than whichever caller happened to start it.
|
|
865
|
+
if (this.oversizeCompactionInFlight) {
|
|
866
|
+
this.oversizeCompactionExtraTokens = Math.max(this.oversizeCompactionExtraTokens, extraTokens);
|
|
867
|
+
return this.oversizeCompactionInFlight;
|
|
868
|
+
}
|
|
869
|
+
// An abandoned summarizer from a timed-out pass is still running and still
|
|
870
|
+
// billing. Refuse to start a second one over the same history.
|
|
871
|
+
if (this.oversizeCompactionSettled) {
|
|
872
|
+
logger.warn("Skipping oversize compaction: a previous pass outlived its deadline and is still running", this.metricIdentity);
|
|
873
|
+
recordPreflightCompaction({
|
|
874
|
+
outcome: "skipped",
|
|
875
|
+
durationMs: 0,
|
|
876
|
+
identity: this.metricIdentity,
|
|
877
|
+
});
|
|
878
|
+
return false;
|
|
879
|
+
}
|
|
880
|
+
this.oversizeCompactionExtraTokens = extraTokens;
|
|
881
|
+
const pass = this.queue.enqueue(() => this.runOversizeCompaction(this.oversizeCompactionExtraTokens));
|
|
882
|
+
this.oversizeCompactionInFlight = pass;
|
|
883
|
+
void pass
|
|
884
|
+
.catch(() => undefined)
|
|
885
|
+
.finally(() => {
|
|
886
|
+
this.oversizeCompactionInFlight = undefined;
|
|
887
|
+
});
|
|
888
|
+
return pass;
|
|
889
|
+
}
|
|
890
|
+
async runOversizeCompaction(extraInputTokens) {
|
|
891
|
+
const startedAt = performance.now();
|
|
892
|
+
let outcome = "failed";
|
|
893
|
+
// Set once `this.messages` holds the compacted window. Anything that throws
|
|
894
|
+
// after that point (the event-log append, persist bookkeeping) happened to
|
|
895
|
+
// a pass that succeeded, so it must not latch the context.
|
|
896
|
+
let committed = false;
|
|
897
|
+
try {
|
|
898
|
+
// Estimated inside the queued callback, not before it. An earlier queued
|
|
899
|
+
// pass may already have shrunk the history, and pairing a stale
|
|
900
|
+
// pre-compaction estimate with the new messages would hand the strategy
|
|
901
|
+
// an inconsistent (messages, usage) pair and skew its phase selection.
|
|
902
|
+
//
|
|
903
|
+
// Sizes the wire payload rather than `this.messages`: `buildMessages`
|
|
904
|
+
// prepends the system prompt and strips `_superblocks`, so this is what
|
|
905
|
+
// the provider will actually receive. The estimate can throw on a
|
|
906
|
+
// malformed message (`JSON.stringify` rejects a BigInt), which the
|
|
907
|
+
// surrounding catch absorbs: this method promises never to be the reason
|
|
908
|
+
// a request fails.
|
|
909
|
+
const payload = await this.buildMessages();
|
|
910
|
+
// `extraInputTokens` is the rest of the request the provider charges for
|
|
911
|
+
// and the message array does not carry, principally the tool schemas.
|
|
912
|
+
// Folded in here rather than compared separately so the synthetic usage
|
|
913
|
+
// handed to `afterResponse` is the same total this gate decided on;
|
|
914
|
+
// passing a messages-only figure would make the strategy's own threshold
|
|
915
|
+
// check disagree with ours and decline the pass we just asked for.
|
|
916
|
+
const estimatedTokens = estimateMessagesTokens(payload) + extraInputTokens;
|
|
917
|
+
const threshold = compactionThresholdTokens(this.config);
|
|
918
|
+
if (estimatedTokens <= threshold) {
|
|
919
|
+
outcome = "fit";
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
logger.warn(`Request payload is oversized: ~${estimatedTokens} estimated tokens > ${threshold} threshold ` +
|
|
923
|
+
`(${this.messages.length} messages); compacting before sending`, this.metricIdentity);
|
|
924
|
+
// Input-only synthetic usage: `computeContextUsed` measures the input
|
|
925
|
+
// window, and a non-zero output count would inflate it.
|
|
926
|
+
//
|
|
927
|
+
// Bounded, because a summarizer that HANGS rather than throws would
|
|
928
|
+
// otherwise block this turn forever. `OperationQueue` is a bare promise
|
|
929
|
+
// chain with no cancellation, so it would also stall every later
|
|
930
|
+
// operation on this context. The deadline is generous: abandoning a
|
|
931
|
+
// compaction that would have worked puts us back at the wedge, so waiting
|
|
932
|
+
// is the cheaper mistake. An abandoned call may still settle in the
|
|
933
|
+
// background; it can no longer hold up the request.
|
|
934
|
+
//
|
|
935
|
+
// `buildMessages()` above primed `_cachedMessages` with the payload this
|
|
936
|
+
// pass is about to replace. `getMessages` serves that cache without
|
|
937
|
+
// joining the queue, so a reader that arrives while the summarizer runs
|
|
938
|
+
// would otherwise be handed the oversized pre-compaction snapshot. Drop
|
|
939
|
+
// it: the reader then queues behind this pass and gets the result. The
|
|
940
|
+
// common fit path returned above with the cache intact, so the request
|
|
941
|
+
// that follows still gets its cache hit.
|
|
942
|
+
this.invalidateMessageCache();
|
|
943
|
+
const settled = this.compaction.afterResponse({
|
|
944
|
+
messages: this.messages,
|
|
945
|
+
usage: {
|
|
946
|
+
inputTokens: estimatedTokens,
|
|
947
|
+
outputTokens: 0,
|
|
948
|
+
totalTokens: estimatedTokens,
|
|
949
|
+
},
|
|
950
|
+
usageIsEstimate: true,
|
|
951
|
+
});
|
|
952
|
+
// Tracked separately from the deadline the caller waits on. `compact()`
|
|
953
|
+
// takes no abort signal, so a pass past its deadline is abandoned rather
|
|
954
|
+
// than cancelled and keeps running and billing; this marker is what stops
|
|
955
|
+
// the queue admitting a sibling pass alongside that orphan. It clears
|
|
956
|
+
// when the SUMMARIZER settles, whether that is before or after the
|
|
957
|
+
// deadline, so the normal path releases it without the caller waiting.
|
|
958
|
+
//
|
|
959
|
+
// The exhausted latch stops a second pass after a timeout on its own, so
|
|
960
|
+
// this covers the one case that clears the latch mid-orphan, a
|
|
961
|
+
// `setCompactionStrategy` swap. It narrows the orphan window rather than
|
|
962
|
+
// closing it; closing it needs an `abortSignal` on `compact()`.
|
|
963
|
+
this.oversizeCompactionSettled = settled;
|
|
964
|
+
// An orphan's eventual outcome is otherwise invisible: the metric already
|
|
965
|
+
// recorded `timed_out` and the request went out without it. These lines
|
|
966
|
+
// are how oncall tells "settled two minutes late" from "still running an
|
|
967
|
+
// hour later".
|
|
968
|
+
let timedOut = false;
|
|
969
|
+
void settled
|
|
970
|
+
.then(() => {
|
|
971
|
+
if (timedOut) {
|
|
972
|
+
logger.warn(`Oversize compaction pass settled ${Math.round(performance.now() - startedAt)}ms after it started, past its deadline; its result was discarded`, this.metricIdentity);
|
|
973
|
+
}
|
|
974
|
+
}, (err) => {
|
|
975
|
+
if (timedOut) {
|
|
976
|
+
logger.warn(`Oversize compaction pass failed after outliving its deadline: ${err}`, this.metricIdentity);
|
|
977
|
+
}
|
|
978
|
+
})
|
|
979
|
+
.finally(() => {
|
|
980
|
+
if (this.oversizeCompactionSettled === settled) {
|
|
981
|
+
this.oversizeCompactionSettled = undefined;
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
let result;
|
|
985
|
+
try {
|
|
986
|
+
result = await withDeadline(settled, PREFLIGHT_COMPACTION_TIMEOUT_MS);
|
|
987
|
+
}
|
|
988
|
+
catch (err) {
|
|
989
|
+
if (err instanceof DeadlineExceededError) {
|
|
990
|
+
timedOut = true;
|
|
991
|
+
outcome = "timed_out";
|
|
992
|
+
}
|
|
993
|
+
throw err;
|
|
994
|
+
}
|
|
995
|
+
if (!result.messages) {
|
|
996
|
+
// No strategy could act, or the strategy declined. Either way the
|
|
997
|
+
// provider is left to reject the request, now labelled
|
|
998
|
+
// `context_too_large`.
|
|
999
|
+
this.oversizeCompactionExhausted = true;
|
|
1000
|
+
outcome = "discarded";
|
|
1001
|
+
logger.warn("Oversize compaction produced no changes; payload remains too large to send", this.metricIdentity);
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
// Measured BEFORE anything is mutated or written, because a compaction is
|
|
1005
|
+
// irreversible once persisted and a pass can come back with the window
|
|
1006
|
+
// essentially unchanged (phase 1 finds nothing prunable and phase 2
|
|
1007
|
+
// declines).
|
|
1008
|
+
//
|
|
1009
|
+
// Three outcomes, not two. The trigger threshold is
|
|
1010
|
+
// `contextLimit - maxOutputTokens - contextBufferTokens`, so 120k on a
|
|
1011
|
+
// 188k model, and tool schemas are irreducible: compaction cannot shrink
|
|
1012
|
+
// them, so `messages_after + extraInputTokens` can sit above the trigger
|
|
1013
|
+
// threshold while being comfortably sendable. Discarding there would
|
|
1014
|
+
// throw away a real shrink and send the original oversized history
|
|
1015
|
+
// instead, which is strictly worse.
|
|
1016
|
+
//
|
|
1017
|
+
// Sendability is measured against `contextLimit - contextBufferTokens`
|
|
1018
|
+
// rather than bare `contextLimit`. `maxOutputTokens` is a generation
|
|
1019
|
+
// reservation, not input the provider charges against the payload, so it
|
|
1020
|
+
// does not belong in a can-this-be-sent bound. The buffer does stay:
|
|
1021
|
+
// against the five captured production sessions this estimator reads
|
|
1022
|
+
// 0.19x to 1.23x of provider-reported usage, so a margin below the hard
|
|
1023
|
+
// limit is doing real work.
|
|
1024
|
+
const candidate = this.markPreCompactionBaselines(result.messages);
|
|
1025
|
+
const remainingTokens = estimateMessagesTokens(await this.buildMessages(candidate)) +
|
|
1026
|
+
extraInputTokens;
|
|
1027
|
+
const sendableLimit = this.config.contextLimit - this.config.contextBufferTokens;
|
|
1028
|
+
if (remainingTokens > sendableLimit) {
|
|
1029
|
+
// Still unsendable. Keep the original history, which at least leaves
|
|
1030
|
+
// the conversation recoverable, and stop paying for the same answer.
|
|
1031
|
+
this.oversizeCompactionExhausted = true;
|
|
1032
|
+
outcome = "discarded";
|
|
1033
|
+
// `compact()` fires onCompactionStart and onCompactionComplete before
|
|
1034
|
+
// it returns, so the user has already been shown "Summarized past
|
|
1035
|
+
// conversation" for work we are about to throw away. Retract it, or the
|
|
1036
|
+
// UI claims a summarization happened while the request goes out with
|
|
1037
|
+
// the original history and gets rejected. Only phase 2 needs this:
|
|
1038
|
+
// `LLMClient.createContext` drops phase-1 callbacks before they render.
|
|
1039
|
+
if (result.phase2MessageId) {
|
|
1040
|
+
this.compactionCallbacks?.onCompactionCancel?.("phase2", result.phase2MessageId);
|
|
1041
|
+
}
|
|
1042
|
+
logger.warn(`Oversize compaction ran but the payload is still unsendable ` +
|
|
1043
|
+
`(~${remainingTokens} estimated tokens > ${sendableLimit} sendable limit, ` +
|
|
1044
|
+
`${estimatedTokens} before); discarding it and leaving the request to the provider`, this.metricIdentity);
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
// Over the compaction threshold but under the limit, so it will send.
|
|
1048
|
+
// Persist it and do NOT latch: real progress was made, and the normal
|
|
1049
|
+
// usage-driven `afterResponse` can carry on from here with the provider's
|
|
1050
|
+
// own numbers instead of this estimate.
|
|
1051
|
+
const underThreshold = remainingTokens <= threshold;
|
|
1052
|
+
if (!underThreshold) {
|
|
1053
|
+
logger.info(`Oversize compaction reduced the payload to a sendable size but not under the ` +
|
|
1054
|
+
`compaction threshold (~${remainingTokens} estimated tokens, ${estimatedTokens} before, ` +
|
|
1055
|
+
`${threshold} threshold, ${sendableLimit} sendable limit)`, this.metricIdentity);
|
|
1056
|
+
}
|
|
1057
|
+
// Re-checked after an await that can last the full deadline. `dispose()`
|
|
1058
|
+
// marks the instance inert so stale generations cannot resurrect cleared
|
|
1059
|
+
// history, and the disk paths already honour it (`runPersist` and
|
|
1060
|
+
// `appendCompactionWindow` both bail on `isDisposed`), but assigning
|
|
1061
|
+
// `this.messages` here would put the pre-dispose conversation back in
|
|
1062
|
+
// memory on a context New Chat or a checkpoint restore just cleared.
|
|
1063
|
+
if (this.isDisposed) {
|
|
1064
|
+
outcome = "discarded";
|
|
1065
|
+
if (result.phase2MessageId) {
|
|
1066
|
+
this.compactionCallbacks?.onCompactionCancel?.("phase2", result.phase2MessageId);
|
|
1067
|
+
}
|
|
1068
|
+
logger.warn("Discarding an oversize compaction result: the context was disposed while it ran", this.metricIdentity);
|
|
1069
|
+
return false;
|
|
1070
|
+
}
|
|
1071
|
+
this.messages = candidate;
|
|
1072
|
+
committed = true;
|
|
1073
|
+
this.invalidateMessageCache();
|
|
1074
|
+
if (result.skillsNeedReload) {
|
|
1075
|
+
this.skillsNeedReload = true;
|
|
1076
|
+
this.readSystemSkills.clear();
|
|
1077
|
+
}
|
|
1078
|
+
if (result.eventLogAnchor) {
|
|
1079
|
+
await this.appendCompactionWindow(this.messages);
|
|
1080
|
+
}
|
|
1081
|
+
await this.persist("compaction");
|
|
1082
|
+
outcome = underThreshold ? "compacted" : "progressed";
|
|
1083
|
+
return underThreshold;
|
|
1084
|
+
}
|
|
1085
|
+
catch (err) {
|
|
1086
|
+
// Never rethrown, and latched so it is never retried: a summarizer that
|
|
1087
|
+
// threw or blew the deadline would stall every later request for the same
|
|
1088
|
+
// deadline and fail the same way, which is worse than the wedge. Degrade
|
|
1089
|
+
// to letting the provider reject the request, now labelled
|
|
1090
|
+
// `context_too_large`. The estimate itself can also throw here
|
|
1091
|
+
// (`JSON.stringify` rejects a BigInt), and latching that is right too:
|
|
1092
|
+
// a malformed message will still be malformed next turn.
|
|
1093
|
+
//
|
|
1094
|
+
// Not latched once the compacted window is in memory: the pass did its
|
|
1095
|
+
// job and the request goes out with the smaller history. Latching there
|
|
1096
|
+
// would block the next rescue over a bookkeeping failure.
|
|
1097
|
+
if (committed) {
|
|
1098
|
+
logger.error(`Oversize compaction applied but its bookkeeping failed: ${err}`, { ...getErrorMeta(err), ...this.metricIdentity });
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
this.oversizeCompactionExhausted = true;
|
|
1102
|
+
logger.error(`Failed to compact oversized payload; leaving it to the provider: ${err}`, { ...getErrorMeta(err), ...this.metricIdentity });
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
finally {
|
|
1106
|
+
// Telemetry only: `outcome` is assigned on every path and nothing here
|
|
1107
|
+
// touches control flow, so a metrics failure cannot change what the
|
|
1108
|
+
// request does.
|
|
1109
|
+
recordPreflightCompaction({
|
|
1110
|
+
outcome,
|
|
1111
|
+
durationMs: performance.now() - startedAt,
|
|
1112
|
+
identity: this.metricIdentity,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
672
1116
|
async getMessages() {
|
|
673
1117
|
// Fast path: return cached result when messages haven't changed.
|
|
674
1118
|
// This avoids re-running 10+ normalization/repair passes and avoids
|
|
@@ -677,9 +1121,23 @@ export class ContextV2 {
|
|
|
677
1121
|
logger.debug(`getMessages: cache hit (${this._cachedMessages.length} messages)`);
|
|
678
1122
|
return [...this._cachedMessages];
|
|
679
1123
|
}
|
|
680
|
-
return this.queue.enqueue(
|
|
681
|
-
|
|
682
|
-
|
|
1124
|
+
return this.queue.enqueue(() => this.buildMessages());
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Build the wire payload. Split out of `getMessages` so a pass already
|
|
1128
|
+
* running inside the operation queue can size the real payload without
|
|
1129
|
+
* re-entering the queue and deadlocking.
|
|
1130
|
+
*/
|
|
1131
|
+
async buildMessages(source) {
|
|
1132
|
+
// A `source` means "size this candidate window", not "this is the context
|
|
1133
|
+
// now": the cache is neither read nor written, so a candidate the caller
|
|
1134
|
+
// ends up discarding cannot leak into the next real request. Same function
|
|
1135
|
+
// either way on purpose, so the before and after measurements are the same
|
|
1136
|
+
// shape and a difference between them means a real difference in payload.
|
|
1137
|
+
const isCandidate = source !== undefined;
|
|
1138
|
+
{
|
|
1139
|
+
// Re-check in case another enqueued op just computed it.
|
|
1140
|
+
if (!isCandidate && this._cachedMessages) {
|
|
683
1141
|
return [...this._cachedMessages];
|
|
684
1142
|
}
|
|
685
1143
|
// Strip system-role messages from the conversation array before
|
|
@@ -695,7 +1153,7 @@ export class ContextV2 {
|
|
|
695
1153
|
// marker through would put a second system-role message in the
|
|
696
1154
|
// middle of the `messages` array and 400 the request on the
|
|
697
1155
|
// first post-compaction turn.
|
|
698
|
-
const conversationMessages = this.messages.filter((msg) => msg.role !== "system");
|
|
1156
|
+
const conversationMessages = (source ?? this.messages).filter((msg) => msg.role !== "system");
|
|
699
1157
|
const allMessages = this.systemPrompt
|
|
700
1158
|
? [this.systemPrompt, ...conversationMessages]
|
|
701
1159
|
: [...conversationMessages];
|
|
@@ -753,21 +1211,27 @@ export class ContextV2 {
|
|
|
753
1211
|
// prefill), so they cannot orphan a tool_use. Cache-breakpoint application
|
|
754
1212
|
// below mutates providerOptions only and is safe.
|
|
755
1213
|
const enforced = enforceToolUsePairing(deduped);
|
|
1214
|
+
// Compaction can persist a lone UTF-16 surrogate (emoji split). Bedrock
|
|
1215
|
+
// then rejects the Converse body as invalid JSON. Repair on the wire
|
|
1216
|
+
// only so already-poisoned chats can send without rewriting history.
|
|
1217
|
+
const jsonSafe = makeJsonSafeValue(enforced);
|
|
756
1218
|
// Apply cache breakpoints (recompute indices after filtering since
|
|
757
1219
|
// the .filter() step may have removed messages, shifting positions)
|
|
758
1220
|
const repairedBreakpoints = findBreakpoints({
|
|
759
|
-
messages:
|
|
1221
|
+
messages: jsonSafe,
|
|
760
1222
|
hasSystemPrompt: this.systemPrompt !== undefined,
|
|
761
1223
|
config: this.caching,
|
|
762
1224
|
});
|
|
763
1225
|
for (const idx of repairedBreakpoints) {
|
|
764
|
-
if (
|
|
765
|
-
|
|
1226
|
+
if (jsonSafe[idx]) {
|
|
1227
|
+
jsonSafe[idx].providerOptions = CACHE_BREAKPOINT_PROVIDER_OPTIONS;
|
|
766
1228
|
}
|
|
767
1229
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1230
|
+
if (!isCandidate) {
|
|
1231
|
+
this._cachedMessages = jsonSafe;
|
|
1232
|
+
}
|
|
1233
|
+
return [...jsonSafe];
|
|
1234
|
+
}
|
|
771
1235
|
}
|
|
772
1236
|
async onCompactionDetected() {
|
|
773
1237
|
const detectedGeneration = this.activeServerCompactionGeneration ??
|