@code-yeongyu/senpi 2026.8.28 → 2026.8.30-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +196 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +50 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +330 -29
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +155 -37
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/compaction-execution.d.ts +14 -0
- package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
- package/dist/core/compaction/compaction-execution.js +121 -0
- package/dist/core/compaction/compaction-execution.js.map +1 -0
- package/dist/core/compaction/compaction-settings.d.ts +17 -0
- package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/compaction-settings.js +17 -0
- package/dist/core/compaction/compaction-settings.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +13 -26
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +7 -136
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
- package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/ideal-compaction-settings.js +7 -0
- package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
- package/dist/core/compaction-settings-access.d.ts +19 -0
- package/dist/core/compaction-settings-access.d.ts.map +1 -0
- package/dist/core/compaction-settings-access.js +10 -0
- package/dist/core/compaction-settings-access.js.map +1 -0
- package/dist/core/compaction-settings-resolver.d.ts +22 -0
- package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
- package/dist/core/compaction-settings-resolver.js +46 -0
- package/dist/core/compaction-settings-resolver.js.map +1 -0
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +45 -21
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts +6 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +22 -5
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
- package/dist/core/credential-pool/env-slots.js +2 -0
- package/dist/core/credential-pool/env-slots.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +24 -6
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +97 -10
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +28 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +163 -48
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle.js +13 -2
- package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +121 -147
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.js +2 -0
- package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +43 -26
- package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +7 -0
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +13 -0
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +4 -2
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +31 -3
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
- package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +74 -37
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/retry-fallback/chains.d.ts +23 -1
- package/dist/core/retry-fallback/chains.d.ts.map +1 -1
- package/dist/core/retry-fallback/chains.js +59 -3
- package/dist/core/retry-fallback/chains.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +9 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/retry-fallback/validate.js +1 -1
- package/dist/core/retry-fallback/validate.js.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +40 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +15 -85
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-public-types.d.ts +11 -0
- package/dist/core/settings-public-types.d.ts.map +1 -0
- package/dist/core/settings-public-types.js +2 -0
- package/dist/core/settings-public-types.js.map +1 -0
- package/dist/core/settings-shapes.d.ts +36 -0
- package/dist/core/settings-shapes.d.ts.map +1 -0
- package/dist/core/settings-shapes.js +2 -0
- package/dist/core/settings-shapes.js.map +1 -0
- package/dist/core/shared-host-policy.d.ts +6 -0
- package/dist/core/shared-host-policy.d.ts.map +1 -0
- package/dist/core/shared-host-policy.js +7 -0
- package/dist/core/shared-host-policy.js.map +1 -0
- package/dist/core/terminal-settings.d.ts +22 -0
- package/dist/core/terminal-settings.d.ts.map +1 -0
- package/dist/core/terminal-settings.js +2 -0
- package/dist/core/terminal-settings.js.map +1 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -15
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +76 -34
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +27 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +34 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/grok/chrome.d.ts +2 -0
- package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
- package/dist/modes/interactive/grok/chrome.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +606 -45
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +390 -239
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +8 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +268 -63
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +19 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +79 -15
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +96 -25
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +2 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +23 -12
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +20 -8
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +91 -16
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +131 -37
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.js +66 -0
- package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +3 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +248 -3
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.d.ts.map +1 -1
- package/dist/modes/rpc/session-binding.js +21 -1
- package/dist/modes/rpc/session-binding.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +22 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +148 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +4 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +58 -7
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
- package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +17 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +67 -4
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
- package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
- package/dist/modes/rpc/socket-event-fanout.js +84 -0
- package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
- package/docs/rpc.md +55 -2
- package/docs/settings.md +34 -0
- package/docs/windows.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { rendezvousOrder } from "@earendil-works/pi-ai/auth/pool/select";
|
|
3
3
|
import { listSlots as listCredentialSlots } from "@earendil-works/pi-ai/auth/pool/slots";
|
|
4
|
+
import { resolveConfigValue } from "../resolve-config-value.js";
|
|
4
5
|
import { classifyCredentialFailure } from "./classify.js";
|
|
5
6
|
import { discoverEnvSlots } from "./env-slots.js";
|
|
6
7
|
import { runCredentialFailover } from "./failover.js";
|
|
8
|
+
import { acquireHalfOpenLease } from "./state-store.js";
|
|
7
9
|
/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */
|
|
8
10
|
export const sha256SlotHasher = (input) => createHash("sha256").update(input).digest().readBigUInt64BE(0);
|
|
9
11
|
function overlayState(slot, state) {
|
|
@@ -14,6 +16,7 @@ function overlayState(slot, state) {
|
|
|
14
16
|
...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),
|
|
15
17
|
...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),
|
|
16
18
|
...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),
|
|
19
|
+
...(state.lease === undefined ? {} : { lease: state.lease }),
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
@@ -23,26 +26,87 @@ function overlayState(slot, state) {
|
|
|
23
26
|
* persisted health applies only while its HMAC revision still matches the
|
|
24
27
|
* current env value, so rotating a key in place clears its own stale block.
|
|
25
28
|
*/
|
|
26
|
-
export async function listRotationSlots(sources) {
|
|
29
|
+
export async function listRotationSlots(sources, options = {}) {
|
|
30
|
+
const acquireLeases = options.acquireLeases !== false;
|
|
27
31
|
const { providerId, credential, env, repository } = sources;
|
|
32
|
+
const policySlots = Object.entries(sources.policy?.slots ?? {}).flatMap(([name, ref]) => {
|
|
33
|
+
const envVarName = ref.env ?? `models.json:${name}`;
|
|
34
|
+
const key = ref.env !== undefined ? env(ref.env) : ref.value !== undefined ? resolveConfigValue(ref.value, {}) : undefined;
|
|
35
|
+
if (!key)
|
|
36
|
+
return [];
|
|
37
|
+
return [{ name, envVarName, key, source: "env" }];
|
|
38
|
+
});
|
|
28
39
|
if (credential) {
|
|
29
40
|
const state = await repository.listSlots(providerId, "stored");
|
|
30
|
-
|
|
41
|
+
const slots = [];
|
|
42
|
+
for (const slot of listCredentialSlots(credential)) {
|
|
43
|
+
const current = state[slot.name];
|
|
44
|
+
if (acquireLeases &&
|
|
45
|
+
current?.blockedUntil !== undefined &&
|
|
46
|
+
current.blockedUntil <= (sources.now ?? Date.now)()) {
|
|
47
|
+
const lease = await acquireHalfOpenLease(repository, providerId, "stored", slot.name, {
|
|
48
|
+
now: (sources.now ?? Date.now)(),
|
|
49
|
+
});
|
|
50
|
+
if (!lease)
|
|
51
|
+
continue;
|
|
52
|
+
const leased = await repository.listSlots(providerId, "stored");
|
|
53
|
+
slots.push(overlayState({
|
|
54
|
+
name: slot.name,
|
|
55
|
+
lane: "stored",
|
|
56
|
+
pinned: credential.pinned === slot.name,
|
|
57
|
+
}, leased[slot.name]));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
slots.push(overlayState({
|
|
61
|
+
name: slot.name,
|
|
62
|
+
lane: "stored",
|
|
63
|
+
pinned: credential.pinned === slot.name,
|
|
64
|
+
}, current));
|
|
65
|
+
}
|
|
66
|
+
if (policySlots.length === 0)
|
|
67
|
+
return slots;
|
|
68
|
+
const namedSources = {
|
|
69
|
+
...sources,
|
|
70
|
+
credential: undefined,
|
|
71
|
+
policy: { ...sources.policy, slots: {} },
|
|
72
|
+
};
|
|
73
|
+
const namedSlots = await listEnvRotationSlots(namedSources, policySlots, acquireLeases);
|
|
74
|
+
return [...slots, ...namedSlots];
|
|
31
75
|
}
|
|
32
|
-
const envSlots = discoverEnvSlots(providerId, env);
|
|
76
|
+
const envSlots = [...discoverEnvSlots(providerId, env), ...policySlots];
|
|
77
|
+
return listEnvRotationSlots(sources, envSlots, acquireLeases);
|
|
78
|
+
}
|
|
79
|
+
async function listEnvRotationSlots(sources, envSlots, acquireLeases = true) {
|
|
33
80
|
if (envSlots.length === 0)
|
|
34
81
|
return [];
|
|
82
|
+
const { providerId, repository } = sources;
|
|
35
83
|
const state = await repository.listSlots(providerId, "env");
|
|
36
84
|
const slots = [];
|
|
37
85
|
for (const slot of envSlots) {
|
|
38
86
|
const persisted = state[slot.name];
|
|
39
87
|
const revision = await repository.envCredentialRevision(slot.envVarName, slot.key);
|
|
40
|
-
|
|
41
|
-
|
|
88
|
+
let applicable = persisted?.credentialRevision === revision ? persisted : undefined;
|
|
89
|
+
if (acquireLeases &&
|
|
90
|
+
applicable?.blockedUntil !== undefined &&
|
|
91
|
+
applicable.blockedUntil <= (sources.now ?? Date.now)()) {
|
|
92
|
+
const lease = await acquireHalfOpenLease(repository, providerId, "env", slot.name, {
|
|
93
|
+
now: (sources.now ?? Date.now)(),
|
|
94
|
+
});
|
|
95
|
+
if (!lease)
|
|
96
|
+
continue;
|
|
97
|
+
const leased = await repository.listSlots(providerId, "env");
|
|
98
|
+
applicable = leased[slot.name];
|
|
99
|
+
}
|
|
100
|
+
slots.push(overlayState({
|
|
101
|
+
name: slot.name,
|
|
102
|
+
lane: "env",
|
|
103
|
+
envKey: slot.key,
|
|
104
|
+
envVarName: slot.envVarName,
|
|
105
|
+
}, applicable));
|
|
42
106
|
}
|
|
43
107
|
return slots;
|
|
44
108
|
}
|
|
45
|
-
function blockPatch(block, current, now, credentialRevision) {
|
|
109
|
+
function blockPatch(block, current, now, credentialRevision, policy) {
|
|
46
110
|
const failureCount = (current?.failureCount ?? 0) + 1;
|
|
47
111
|
const base = {
|
|
48
112
|
failureCount,
|
|
@@ -50,7 +114,11 @@ function blockPatch(block, current, now, credentialRevision) {
|
|
|
50
114
|
...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),
|
|
51
115
|
};
|
|
52
116
|
if (block.reason === "rate_limit") {
|
|
53
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
...base,
|
|
119
|
+
blockedUntil: now + Math.min(policy?.cooldownCapMs ?? block.cooldownMs, block.cooldownMs),
|
|
120
|
+
blockReason: "rate_limit",
|
|
121
|
+
};
|
|
54
122
|
}
|
|
55
123
|
return { ...base, blockReason: block.reason };
|
|
56
124
|
}
|
|
@@ -70,11 +138,15 @@ export function streamWithCredentialRotation(options) {
|
|
|
70
138
|
const { sources, runAttempt } = options;
|
|
71
139
|
const hasher = options.hasher ?? sha256SlotHasher;
|
|
72
140
|
const affinityKey = options.affinityKey ?? randomUUID();
|
|
141
|
+
const useAffinity = sources.policy?.affinity !== false;
|
|
73
142
|
const now = sources.now ?? Date.now;
|
|
74
143
|
return runCredentialFailover({
|
|
75
144
|
listSlots: () => listRotationSlots(sources),
|
|
76
145
|
select: (candidates) => {
|
|
77
|
-
const
|
|
146
|
+
const pinned = candidates.find((candidate) => candidate.pinned === true);
|
|
147
|
+
if (pinned)
|
|
148
|
+
return pinned;
|
|
149
|
+
const ordered = useAffinity ? rendezvousOrder(affinityKey, candidates, hasher) : candidates;
|
|
78
150
|
const winner = ordered[0];
|
|
79
151
|
if (!winner)
|
|
80
152
|
throw new Error("credential rotation selected from an empty candidate set");
|
|
@@ -83,12 +155,27 @@ export function streamWithCredentialRotation(options) {
|
|
|
83
155
|
runAttempt,
|
|
84
156
|
isCommittedOutput: (event) => event.type !== "start",
|
|
85
157
|
errorFromEvent,
|
|
86
|
-
classify: classifyCredentialFailure,
|
|
158
|
+
classify: (error, context) => classifyCredentialFailure(error, {
|
|
159
|
+
...context,
|
|
160
|
+
cooldownBaseMs: sources.policy?.cooldownBaseMs,
|
|
161
|
+
cooldownCapMs: sources.policy?.cooldownCapMs,
|
|
162
|
+
}),
|
|
163
|
+
onSuccess: async (slot) => {
|
|
164
|
+
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => current
|
|
165
|
+
? {
|
|
166
|
+
...current,
|
|
167
|
+
lastSuccessAt: now(),
|
|
168
|
+
lease: undefined,
|
|
169
|
+
blockedUntil: undefined,
|
|
170
|
+
blockReason: undefined,
|
|
171
|
+
}
|
|
172
|
+
: undefined);
|
|
173
|
+
},
|
|
87
174
|
persistBlock: async (slot, block) => {
|
|
88
175
|
const revision = slot.lane === "env" && slot.envVarName !== undefined && slot.envKey !== undefined
|
|
89
176
|
? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)
|
|
90
177
|
: undefined;
|
|
91
|
-
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => blockPatch(block, current, now(), revision));
|
|
178
|
+
await sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) => blockPatch(block, current, now(), revision, sources.policy));
|
|
92
179
|
},
|
|
93
180
|
now,
|
|
94
181
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rotation-stream.js","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,eAAe,EAAmB,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,SAAS,IAAI,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACzF,OAAO,EAAwB,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAgB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGpE,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAe,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAmBtH,SAAS,YAAY,CAAC,IAAkB,EAAE,KAAsC;IAC/E,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO;QACN,GAAG,IAAI;QACP,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;QACjF,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;QAC9E,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;KACjF,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAwB;IAC/D,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAC5D,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC/D,OAAO,mBAAmB,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnD,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CACnE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,qBAAqB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACnF,MAAM,UAAU,GAAG,SAAS,EAAE,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACtF,KAAK,CAAC,IAAI,CACT,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,UAAU,CAAC,CACzG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAClB,KAAsB,EACtB,OAAwC,EACxC,GAAW,EACX,kBAA2B;IAE3B,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG;QACZ,YAAY;QACZ,GAAG,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC;QACnE,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;KACzF,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;QACnC,OAAO,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;IACrF,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAC/C,CAAC;AAYD,SAAS,cAAc,CAAC,KAA4B;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,uBAAuB,CAAC;IACpE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC3C,OAAkC;IAElC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC;IAClD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,UAAU,EAAE,CAAC;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IAEpC,OAAO,qBAAqB,CAAsC;QACjE,SAAS,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC;QAC3C,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE;YACtB,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;YACzF,OAAO,MAAM,CAAC;QACf,CAAC;QACD,UAAU;QACV,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO;QACpD,cAAc;QACd,QAAQ,EAAE,yBAAyB;QACnC,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YACnC,MAAM,QAAQ,GACb,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAChF,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;gBAC9E,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAC9F,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,QAAQ,CAAC,CAC3C,CAAC;QACH,CAAC;QACD,GAAG;KACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { AssistantMessageEvent, Credential } from \"@earendil-works/pi-ai\";\nimport { rendezvousOrder, type SlotHasher } from \"@earendil-works/pi-ai/auth/pool/select\";\nimport { listSlots as listCredentialSlots } from \"@earendil-works/pi-ai/auth/pool/slots\";\nimport { type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\nimport { discoverEnvSlots } from \"./env-slots.ts\";\nimport { type RunSlot, runCredentialFailover } from \"./failover.ts\";\nimport type { CredentialSlotRepository, CredentialSlotState } from \"./state-store.ts\";\n\n/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */\nexport const sha256SlotHasher: SlotHasher = (input) => createHash(\"sha256\").update(input).digest().readBigUInt64BE(0);\n\nexport type RotationLane = \"stored\" | \"env\";\n\nexport type RotationSlot = RunSlot & {\n\tlane: RotationLane;\n\t/** Env-lane key material for the attempt; never serialized or persisted. */\n\tenvKey?: string;\n\tenvVarName?: string;\n};\n\nexport type RotationSources = {\n\tproviderId: string;\n\tcredential: Credential | undefined;\n\tenv: (name: string) => string | undefined;\n\trepository: CredentialSlotRepository;\n\tnow?: () => number;\n};\n\nfunction overlayState(slot: RotationSlot, state: CredentialSlotState | undefined): RotationSlot {\n\tif (!state) return slot;\n\treturn {\n\t\t...slot,\n\t\t...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),\n\t\t...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),\n\t\t...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),\n\t};\n}\n\n/**\n * Lists the provider's rotation slots with sidecar health overlaid. Stored\n * credentials own the lane when present; env slots participate only when\n * nothing is stored, preserving today's resolution precedence. An env slot's\n * persisted health applies only while its HMAC revision still matches the\n * current env value, so rotating a key in place clears its own stale block.\n */\nexport async function listRotationSlots(sources: RotationSources): Promise<RotationSlot[]> {\n\tconst { providerId, credential, env, repository } = sources;\n\tif (credential) {\n\t\tconst state = await repository.listSlots(providerId, \"stored\");\n\t\treturn listCredentialSlots(credential).map((slot) =>\n\t\t\toverlayState({ name: slot.name, lane: \"stored\" }, state[slot.name]),\n\t\t);\n\t}\n\tconst envSlots = discoverEnvSlots(providerId, env);\n\tif (envSlots.length === 0) return [];\n\tconst state = await repository.listSlots(providerId, \"env\");\n\tconst slots: RotationSlot[] = [];\n\tfor (const slot of envSlots) {\n\t\tconst persisted = state[slot.name];\n\t\tconst revision = await repository.envCredentialRevision(slot.envVarName, slot.key);\n\t\tconst applicable = persisted?.credentialRevision === revision ? persisted : undefined;\n\t\tslots.push(\n\t\t\toverlayState({ name: slot.name, lane: \"env\", envKey: slot.key, envVarName: slot.envVarName }, applicable),\n\t\t);\n\t}\n\treturn slots;\n}\n\nfunction blockPatch(\n\tblock: CredentialBlock,\n\tcurrent: CredentialSlotState | undefined,\n\tnow: number,\n\tcredentialRevision?: string,\n): Omit<CredentialSlotState, \"stateVersion\"> {\n\tconst failureCount = (current?.failureCount ?? 0) + 1;\n\tconst base = {\n\t\tfailureCount,\n\t\t...(credentialRevision === undefined ? {} : { credentialRevision }),\n\t\t...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),\n\t};\n\tif (block.reason === \"rate_limit\") {\n\t\treturn { ...base, blockedUntil: now + block.cooldownMs, blockReason: \"rate_limit\" };\n\t}\n\treturn { ...base, blockReason: block.reason };\n}\n\nexport type CredentialRotationOptions = {\n\tsources: RotationSources;\n\t/** Stable session key keeps a session on its slot; absent, each request distributes. */\n\taffinityKey?: string;\n\thasher?: SlotHasher;\n\trunAttempt: (\n\t\tslot: RotationSlot,\n\t) => AsyncIterable<AssistantMessageEvent> | Promise<AsyncIterable<AssistantMessageEvent>>;\n};\n\nfunction errorFromEvent(event: AssistantMessageEvent): unknown {\n\tif (event.type !== \"error\") return undefined;\n\tconst message = event.error.errorMessage ?? \"provider stream error\";\n\treturn new Error(message);\n}\n\n/**\n * In-lane credential rotation for one provider request. Selection follows the\n * HRW order for the affinity key; only the `start` bookkeeping event counts as\n * pre-commit, so any delta bars silent rotation (default-DENY) and failures\n * after output carry the turn-retry suppression marker.\n */\nexport function streamWithCredentialRotation(\n\toptions: CredentialRotationOptions,\n): AsyncGenerator<AssistantMessageEvent> {\n\tconst { sources, runAttempt } = options;\n\tconst hasher = options.hasher ?? sha256SlotHasher;\n\tconst affinityKey = options.affinityKey ?? randomUUID();\n\tconst now = sources.now ?? Date.now;\n\n\treturn runCredentialFailover<AssistantMessageEvent, RotationSlot>({\n\t\tlistSlots: () => listRotationSlots(sources),\n\t\tselect: (candidates) => {\n\t\t\tconst ordered = rendezvousOrder(affinityKey, candidates, hasher);\n\t\t\tconst winner = ordered[0];\n\t\t\tif (!winner) throw new Error(\"credential rotation selected from an empty candidate set\");\n\t\t\treturn winner;\n\t\t},\n\t\trunAttempt,\n\t\tisCommittedOutput: (event) => event.type !== \"start\",\n\t\terrorFromEvent,\n\t\tclassify: classifyCredentialFailure,\n\t\tpersistBlock: async (slot, block) => {\n\t\t\tconst revision =\n\t\t\t\tslot.lane === \"env\" && slot.envVarName !== undefined && slot.envKey !== undefined\n\t\t\t\t\t? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)\n\t\t\t\t\t: undefined;\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tblockPatch(block, current, now(), revision),\n\t\t\t);\n\t\t},\n\t\tnow,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rotation-stream.js","sourceRoot":"","sources":["../../../src/core/credential-pool/rotation-stream.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { AssistantMessageEvent, Credential } from \"@earendil-works/pi-ai\";\nimport { rendezvousOrder, type SlotHasher } from \"@earendil-works/pi-ai/auth/pool/select\";\nimport { listSlots as listCredentialSlots, type PooledCredential } from \"@earendil-works/pi-ai/auth/pool/slots\";\nimport { resolveConfigValue } from \"../resolve-config-value.ts\";\nimport { type CredentialBlock, classifyCredentialFailure } from \"./classify.ts\";\nimport { discoverEnvSlots } from \"./env-slots.ts\";\nimport { type RunSlot, runCredentialFailover } from \"./failover.ts\";\nimport { acquireHalfOpenLease, type CredentialSlotRepository, type CredentialSlotState } from \"./state-store.ts\";\n\n/** The exact hash the claude-sdk-oauth affinity oracle uses, so pools never remap. */\nexport const sha256SlotHasher: SlotHasher = (input) => createHash(\"sha256\").update(input).digest().readBigUInt64BE(0);\n\nexport type RotationLane = \"stored\" | \"env\";\n\nexport type RotationSlot = RunSlot & {\n\tlane: RotationLane;\n\t/** Env-lane key material for the attempt; never serialized or persisted. */\n\tenvKey?: string;\n\tenvVarName?: string;\n};\n\nexport type RotationSources = {\n\tproviderId: string;\n\tcredential: Credential | undefined;\n\tenv: (name: string) => string | undefined;\n\trepository: CredentialSlotRepository;\n\tpolicy?: {\n\t\taffinity?: boolean;\n\t\tcooldownBaseMs?: number;\n\t\tcooldownCapMs?: number;\n\t\tslots?: Record<string, { env?: string; value?: string }>;\n\t};\n\tnow?: () => number;\n};\n\nfunction overlayState(slot: RotationSlot, state: CredentialSlotState | undefined): RotationSlot {\n\tif (!state) return slot;\n\treturn {\n\t\t...slot,\n\t\t...(state.blockedUntil === undefined ? {} : { blockedUntil: state.blockedUntil }),\n\t\t...(state.blockReason === undefined ? {} : { blockReason: state.blockReason }),\n\t\t...(state.failureCount === undefined ? {} : { failureCount: state.failureCount }),\n\t\t...(state.lease === undefined ? {} : { lease: state.lease }),\n\t};\n}\n\n/**\n * Lists the provider's rotation slots with sidecar health overlaid. Stored\n * credentials own the lane when present; env slots participate only when\n * nothing is stored, preserving today's resolution precedence. An env slot's\n * persisted health applies only while its HMAC revision still matches the\n * current env value, so rotating a key in place clears its own stale block.\n */\nexport async function listRotationSlots(\n\tsources: RotationSources,\n\toptions: { acquireLeases?: boolean } = {},\n): Promise<RotationSlot[]> {\n\tconst acquireLeases = options.acquireLeases !== false;\n\tconst { providerId, credential, env, repository } = sources;\n\tconst policySlots = Object.entries(sources.policy?.slots ?? {}).flatMap(([name, ref]) => {\n\t\tconst envVarName = ref.env ?? `models.json:${name}`;\n\t\tconst key =\n\t\t\tref.env !== undefined ? env(ref.env) : ref.value !== undefined ? resolveConfigValue(ref.value, {}) : undefined;\n\t\tif (!key) return [];\n\t\treturn [{ name, envVarName, key, source: \"env\" as const }];\n\t});\n\tif (credential) {\n\t\tconst state = await repository.listSlots(providerId, \"stored\");\n\t\tconst slots: RotationSlot[] = [];\n\t\tfor (const slot of listCredentialSlots(credential)) {\n\t\t\tconst current = state[slot.name];\n\t\t\tif (\n\t\t\t\tacquireLeases &&\n\t\t\t\tcurrent?.blockedUntil !== undefined &&\n\t\t\t\tcurrent.blockedUntil <= (sources.now ?? Date.now)()\n\t\t\t) {\n\t\t\t\tconst lease = await acquireHalfOpenLease(repository, providerId, \"stored\", slot.name, {\n\t\t\t\t\tnow: (sources.now ?? Date.now)(),\n\t\t\t\t});\n\t\t\t\tif (!lease) continue;\n\t\t\t\tconst leased = await repository.listSlots(providerId, \"stored\");\n\t\t\t\tslots.push(\n\t\t\t\t\toverlayState(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: slot.name,\n\t\t\t\t\t\t\tlane: \"stored\",\n\t\t\t\t\t\t\tpinned: (credential as PooledCredential).pinned === slot.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tleased[slot.name],\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tslots.push(\n\t\t\t\toverlayState(\n\t\t\t\t\t{\n\t\t\t\t\t\tname: slot.name,\n\t\t\t\t\t\tlane: \"stored\",\n\t\t\t\t\t\tpinned: (credential as PooledCredential).pinned === slot.name,\n\t\t\t\t\t},\n\t\t\t\t\tcurrent,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (policySlots.length === 0) return slots;\n\t\tconst namedSources: RotationSources = {\n\t\t\t...sources,\n\t\t\tcredential: undefined,\n\t\t\tpolicy: { ...sources.policy, slots: {} },\n\t\t};\n\t\tconst namedSlots = await listEnvRotationSlots(namedSources, policySlots, acquireLeases);\n\t\treturn [...slots, ...namedSlots];\n\t}\n\tconst envSlots = [...discoverEnvSlots(providerId, env), ...policySlots];\n\treturn listEnvRotationSlots(sources, envSlots, acquireLeases);\n}\n\nasync function listEnvRotationSlots(\n\tsources: RotationSources,\n\tenvSlots: readonly { name: string; envVarName: string; key: string }[],\n\tacquireLeases = true,\n): Promise<RotationSlot[]> {\n\tif (envSlots.length === 0) return [];\n\tconst { providerId, repository } = sources;\n\tconst state = await repository.listSlots(providerId, \"env\");\n\tconst slots: RotationSlot[] = [];\n\tfor (const slot of envSlots) {\n\t\tconst persisted = state[slot.name];\n\t\tconst revision = await repository.envCredentialRevision(slot.envVarName, slot.key);\n\t\tlet applicable = persisted?.credentialRevision === revision ? persisted : undefined;\n\t\tif (\n\t\t\tacquireLeases &&\n\t\t\tapplicable?.blockedUntil !== undefined &&\n\t\t\tapplicable.blockedUntil <= (sources.now ?? Date.now)()\n\t\t) {\n\t\t\tconst lease = await acquireHalfOpenLease(repository, providerId, \"env\", slot.name, {\n\t\t\t\tnow: (sources.now ?? Date.now)(),\n\t\t\t});\n\t\t\tif (!lease) continue;\n\t\t\tconst leased = await repository.listSlots(providerId, \"env\");\n\t\t\tapplicable = leased[slot.name];\n\t\t}\n\t\tslots.push(\n\t\t\toverlayState(\n\t\t\t\t{\n\t\t\t\t\tname: slot.name,\n\t\t\t\t\tlane: \"env\",\n\t\t\t\t\tenvKey: slot.key,\n\t\t\t\t\tenvVarName: slot.envVarName,\n\t\t\t\t},\n\t\t\t\tapplicable,\n\t\t\t),\n\t\t);\n\t}\n\treturn slots;\n}\n\nfunction blockPatch(\n\tblock: CredentialBlock,\n\tcurrent: CredentialSlotState | undefined,\n\tnow: number,\n\tcredentialRevision?: string,\n\tpolicy?: { cooldownBaseMs?: number; cooldownCapMs?: number },\n): Omit<CredentialSlotState, \"stateVersion\"> {\n\tconst failureCount = (current?.failureCount ?? 0) + 1;\n\tconst base = {\n\t\tfailureCount,\n\t\t...(credentialRevision === undefined ? {} : { credentialRevision }),\n\t\t...(current?.lastSuccessAt === undefined ? {} : { lastSuccessAt: current.lastSuccessAt }),\n\t};\n\tif (block.reason === \"rate_limit\") {\n\t\treturn {\n\t\t\t...base,\n\t\t\tblockedUntil: now + Math.min(policy?.cooldownCapMs ?? block.cooldownMs, block.cooldownMs),\n\t\t\tblockReason: \"rate_limit\",\n\t\t};\n\t}\n\treturn { ...base, blockReason: block.reason };\n}\n\nexport type CredentialRotationOptions = {\n\tsources: RotationSources;\n\t/** Stable session key keeps a session on its slot; absent, each request distributes. */\n\taffinityKey?: string;\n\thasher?: SlotHasher;\n\trunAttempt: (\n\t\tslot: RotationSlot,\n\t) => AsyncIterable<AssistantMessageEvent> | Promise<AsyncIterable<AssistantMessageEvent>>;\n};\n\nfunction errorFromEvent(event: AssistantMessageEvent): unknown {\n\tif (event.type !== \"error\") return undefined;\n\tconst message = event.error.errorMessage ?? \"provider stream error\";\n\treturn new Error(message);\n}\n\n/**\n * In-lane credential rotation for one provider request. Selection follows the\n * HRW order for the affinity key; only the `start` bookkeeping event counts as\n * pre-commit, so any delta bars silent rotation (default-DENY) and failures\n * after output carry the turn-retry suppression marker.\n */\nexport function streamWithCredentialRotation(\n\toptions: CredentialRotationOptions,\n): AsyncGenerator<AssistantMessageEvent> {\n\tconst { sources, runAttempt } = options;\n\tconst hasher = options.hasher ?? sha256SlotHasher;\n\tconst affinityKey = options.affinityKey ?? randomUUID();\n\tconst useAffinity = sources.policy?.affinity !== false;\n\tconst now = sources.now ?? Date.now;\n\n\treturn runCredentialFailover<AssistantMessageEvent, RotationSlot>({\n\t\tlistSlots: () => listRotationSlots(sources),\n\t\tselect: (candidates) => {\n\t\t\tconst pinned = candidates.find((candidate) => candidate.pinned === true);\n\t\t\tif (pinned) return pinned;\n\t\t\tconst ordered = useAffinity ? rendezvousOrder(affinityKey, candidates, hasher) : candidates;\n\n\t\t\tconst winner = ordered[0];\n\t\t\tif (!winner) throw new Error(\"credential rotation selected from an empty candidate set\");\n\t\t\treturn winner;\n\t\t},\n\t\trunAttempt,\n\t\tisCommittedOutput: (event) => event.type !== \"start\",\n\t\terrorFromEvent,\n\t\tclassify: (error, context) =>\n\t\t\tclassifyCredentialFailure(error, {\n\t\t\t\t...context,\n\t\t\t\tcooldownBaseMs: sources.policy?.cooldownBaseMs,\n\t\t\t\tcooldownCapMs: sources.policy?.cooldownCapMs,\n\t\t\t}),\n\t\tonSuccess: async (slot) => {\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tcurrent\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...current,\n\t\t\t\t\t\t\tlastSuccessAt: now(),\n\t\t\t\t\t\t\tlease: undefined,\n\t\t\t\t\t\t\tblockedUntil: undefined,\n\t\t\t\t\t\t\tblockReason: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t},\n\t\tpersistBlock: async (slot, block) => {\n\t\t\tconst revision =\n\t\t\t\tslot.lane === \"env\" && slot.envVarName !== undefined && slot.envKey !== undefined\n\t\t\t\t\t? await sources.repository.envCredentialRevision(slot.envVarName, slot.envKey)\n\t\t\t\t\t: undefined;\n\t\t\tawait sources.repository.mutateSlotState(sources.providerId, slot.lane, slot.name, (current) =>\n\t\t\t\tblockPatch(block, current, now(), revision, sources.policy),\n\t\t\t);\n\t\t},\n\t\tnow,\n\t});\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ContextEvent, ExtensionContext } from "../../types.ts";
|
|
2
|
+
import { type EmergencyPruneLatch } from "./speculative.ts";
|
|
3
|
+
export declare function buildCompactionContext(input: {
|
|
4
|
+
event: ContextEvent;
|
|
5
|
+
ctx: ExtensionContext;
|
|
6
|
+
contextWindow: number;
|
|
7
|
+
/**
|
|
8
|
+
* Context window minus the model's output reserve. The emergency prune budgets against the
|
|
9
|
+
* space a request can actually occupy, so it must not see the full window.
|
|
10
|
+
*/
|
|
11
|
+
promptContextWindow: number;
|
|
12
|
+
toolAdmissionEnabled: boolean;
|
|
13
|
+
breakerFallback: boolean;
|
|
14
|
+
laneOwnsCompaction: boolean;
|
|
15
|
+
emergencyPruneLatch: EmergencyPruneLatch;
|
|
16
|
+
}): import("@earendil-works/pi-ai").Message[];
|
|
17
|
+
//# sourceMappingURL=context-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-pipeline.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/context-pipeline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUrE,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,kBAAkB,CAAC;AAErF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC7C,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,gBAAgB,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,mBAAmB,CAAC;CACzC,6CAwBA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { convertToLlm } from "../../../messages.js";
|
|
2
|
+
import { BUILTIN_CONTEXT_REDUCTION_OPTIONS, reduceContextMessages, shouldApplyContextReduction, } from "./context-reduction.js";
|
|
3
|
+
import { markOpenAiRemoteReplayBoundary } from "./openai-remote.js";
|
|
4
|
+
import { isOpenAiRemoteCompactionModel } from "./openai-remote-model.js";
|
|
5
|
+
import { admitContextToolResults } from "./orchestration.js";
|
|
6
|
+
import { repairOrphanedToolResults } from "./repair-tool-pairs.js";
|
|
7
|
+
import { hardLimitEmergencyPrune } from "./speculative.js";
|
|
8
|
+
export function buildCompactionContext(input) {
|
|
9
|
+
const admittedMessages = admitContextToolResults(input.event.messages, input.contextWindow, input.toolAdmissionEnabled);
|
|
10
|
+
const sourceMessages = !input.laneOwnsCompaction &&
|
|
11
|
+
(input.breakerFallback ||
|
|
12
|
+
shouldApplyContextReduction({
|
|
13
|
+
usageTokens: input.ctx.getContextUsage()?.tokens ?? null,
|
|
14
|
+
contextWindow: input.contextWindow,
|
|
15
|
+
isProviderNativeCompactionPath: isOpenAiRemoteCompactionModel(input.ctx.model) || input.laneOwnsCompaction,
|
|
16
|
+
}))
|
|
17
|
+
? reduceContextMessages(admittedMessages, BUILTIN_CONTEXT_REDUCTION_OPTIONS).messages
|
|
18
|
+
: admittedMessages;
|
|
19
|
+
const emergency = input.laneOwnsCompaction
|
|
20
|
+
? { messages: sourceMessages, needsAggressiveCompaction: false }
|
|
21
|
+
: hardLimitEmergencyPrune(sourceMessages, input.promptContextWindow, input.emergencyPruneLatch);
|
|
22
|
+
const marked = markOpenAiRemoteReplayBoundary(emergency.messages, {
|
|
23
|
+
model: input.ctx.model,
|
|
24
|
+
branchEntries: input.ctx.sessionManager.getBranch(),
|
|
25
|
+
});
|
|
26
|
+
return repairOrphanedToolResults(convertToLlm(marked));
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=context-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-pipeline.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/context-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EACN,iCAAiC,EACjC,qBAAqB,EACrB,2BAA2B,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAA4B,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAErF,MAAM,UAAU,sBAAsB,CAAC,KAatC;IACA,MAAM,gBAAgB,GAAG,uBAAuB,CAC/C,KAAK,CAAC,KAAK,CAAC,QAAQ,EACpB,KAAK,CAAC,aAAa,EACnB,KAAK,CAAC,oBAAoB,CAC1B,CAAC;IACF,MAAM,cAAc,GACnB,CAAC,KAAK,CAAC,kBAAkB;QACzB,CAAC,KAAK,CAAC,eAAe;YACrB,2BAA2B,CAAC;gBAC3B,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,MAAM,IAAI,IAAI;gBACxD,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,8BAA8B,EAAE,6BAA6B,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,kBAAkB;aAC1G,CAAC,CAAC;QACH,CAAC,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,iCAAiC,CAAC,CAAC,QAAQ;QACrF,CAAC,CAAC,gBAAgB,CAAC;IACrB,MAAM,SAAS,GAAG,KAAK,CAAC,kBAAkB;QACzC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,yBAAyB,EAAE,KAAK,EAAE;QAChE,CAAC,CAAC,uBAAuB,CAAC,cAAc,EAAE,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACjG,MAAM,MAAM,GAAG,8BAA8B,CAAC,SAAS,CAAC,QAAQ,EAAE;QACjE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK;QACtB,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE;KACnD,CAAC,CAAC;IACH,OAAO,yBAAyB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import { convertToLlm } from \"../../../messages.ts\";\nimport type { ContextEvent, ExtensionContext } from \"../../types.ts\";\nimport {\n\tBUILTIN_CONTEXT_REDUCTION_OPTIONS,\n\treduceContextMessages,\n\tshouldApplyContextReduction,\n} from \"./context-reduction.ts\";\nimport { markOpenAiRemoteReplayBoundary } from \"./openai-remote.ts\";\nimport { isOpenAiRemoteCompactionModel } from \"./openai-remote-model.ts\";\nimport { admitContextToolResults } from \"./orchestration.ts\";\nimport { repairOrphanedToolResults } from \"./repair-tool-pairs.ts\";\nimport { type EmergencyPruneLatch, hardLimitEmergencyPrune } from \"./speculative.ts\";\n\nexport function buildCompactionContext(input: {\n\tevent: ContextEvent;\n\tctx: ExtensionContext;\n\tcontextWindow: number;\n\t/**\n\t * Context window minus the model's output reserve. The emergency prune budgets against the\n\t * space a request can actually occupy, so it must not see the full window.\n\t */\n\tpromptContextWindow: number;\n\ttoolAdmissionEnabled: boolean;\n\tbreakerFallback: boolean;\n\tlaneOwnsCompaction: boolean;\n\temergencyPruneLatch: EmergencyPruneLatch;\n}) {\n\tconst admittedMessages = admitContextToolResults(\n\t\tinput.event.messages,\n\t\tinput.contextWindow,\n\t\tinput.toolAdmissionEnabled,\n\t);\n\tconst sourceMessages =\n\t\t!input.laneOwnsCompaction &&\n\t\t(input.breakerFallback ||\n\t\t\tshouldApplyContextReduction({\n\t\t\t\tusageTokens: input.ctx.getContextUsage()?.tokens ?? null,\n\t\t\t\tcontextWindow: input.contextWindow,\n\t\t\t\tisProviderNativeCompactionPath: isOpenAiRemoteCompactionModel(input.ctx.model) || input.laneOwnsCompaction,\n\t\t\t}))\n\t\t\t? reduceContextMessages(admittedMessages, BUILTIN_CONTEXT_REDUCTION_OPTIONS).messages\n\t\t\t: admittedMessages;\n\tconst emergency = input.laneOwnsCompaction\n\t\t? { messages: sourceMessages, needsAggressiveCompaction: false }\n\t\t: hardLimitEmergencyPrune(sourceMessages, input.promptContextWindow, input.emergencyPruneLatch);\n\tconst marked = markOpenAiRemoteReplayBoundary(emergency.messages, {\n\t\tmodel: input.ctx.model,\n\t\tbranchEntries: input.ctx.sessionManager.getBranch(),\n\t});\n\treturn repairOrphanedToolResults(convertToLlm(marked));\n}\n"]}
|
|
@@ -6,14 +6,20 @@ interface RecoveryMetadata {
|
|
|
6
6
|
todoSnapshot?: unknown;
|
|
7
7
|
checkpoint?: unknown;
|
|
8
8
|
}
|
|
9
|
+
export type DeterministicFallbackRejectionReason = "missing-preparation-boundary" | "unsafe-retained-content" | "retained-token-budget-exceeded" | "atomic-tool-chain-cut" | "context-reconstruction-failed";
|
|
9
10
|
interface DeterministicFallbackDetails {
|
|
10
11
|
schema: "senpi.compaction.deterministic-fallback.v1";
|
|
11
12
|
origin: "required-compaction-recovery";
|
|
12
13
|
failureKind: RequiredCompactionFallbackFailure;
|
|
13
14
|
taskIntent?: string;
|
|
14
|
-
retainedSuffix?: "prepared" | "latest-user-turn";
|
|
15
|
+
retainedSuffix?: "prepared" | "latest-user-turn" | "earlier-safe-boundary";
|
|
16
|
+
}
|
|
17
|
+
export interface DeterministicFallbackDiagnostic {
|
|
18
|
+
rejectionReason?: DeterministicFallbackRejectionReason;
|
|
19
|
+
candidatesChecked?: number;
|
|
20
|
+
budgetExceeded?: boolean;
|
|
15
21
|
}
|
|
16
22
|
export declare function classifyRequiredCompactionFallbackFailure(error: unknown): RequiredCompactionFallbackFailure | undefined;
|
|
17
|
-
export declare function createRequiredCompactionFallback(preparation: CompactionPreparation, contextWindow: number, failureKind: RequiredCompactionFallbackFailure, metadata: RecoveryMetadata, branchEntries?: SessionEntry[]): CompactionResult<DeterministicFallbackDetails> | undefined;
|
|
23
|
+
export declare function createRequiredCompactionFallback(preparation: CompactionPreparation, contextWindow: number, failureKind: RequiredCompactionFallbackFailure, metadata: RecoveryMetadata, branchEntries?: SessionEntry[], diagnostics?: DeterministicFallbackDiagnostic): CompactionResult<DeterministicFallbackDetails> | undefined;
|
|
18
24
|
export {};
|
|
19
25
|
//# sourceMappingURL=deterministic-fallback.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deterministic-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAkB,MAAM,8BAA8B,CAAC;AAGjH,OAAO,
|
|
1
|
+
{"version":3,"file":"deterministic-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAkB,MAAM,8BAA8B,CAAC;AAGjH,OAAO,EAIN,KAAK,YAAY,EACjB,MAAM,6BAA6B,CAAC;AAOrC,MAAM,MAAM,iCAAiC,GAC1C,uBAAuB,GACvB,2BAA2B,GAC3B,kCAAkC,CAAC;AAEtC,UAAU,gBAAgB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,oCAAoC,GAC7C,8BAA8B,GAC9B,yBAAyB,GACzB,gCAAgC,GAChC,uBAAuB,GACvB,+BAA+B,CAAC;AAEnC,UAAU,4BAA4B;IACrC,MAAM,EAAE,4CAA4C,CAAC;IACrD,MAAM,EAAE,8BAA8B,CAAC;IACvC,WAAW,EAAE,iCAAiC,CAAC;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,GAAG,kBAAkB,GAAG,uBAAuB,CAAC;CAC3E;AAED,MAAM,WAAW,+BAA+B;IAC/C,eAAe,CAAC,EAAE,oCAAoC,CAAC;IACvD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AA8CD,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,OAAO,GACZ,iCAAiC,GAAG,SAAS,CAW/C;AAED,wBAAgB,gCAAgC,CAC/C,WAAW,EAAE,qBAAqB,EAClC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,iCAAiC,EAC9C,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,GAAE,YAAY,EAAO,EAClC,WAAW,CAAC,EAAE,+BAA+B,GAC3C,gBAAgB,CAAC,4BAA4B,CAAC,GAAG,SAAS,CAuN5D"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { estimateTokens } from "../../../compaction/index.js";
|
|
2
2
|
import { StreamDurationBudgetError, StreamIdleTimeoutError } from "../../../compaction/stream-watchdog.js";
|
|
3
3
|
import { filterContextExcludedMessages } from "../../../messages.js";
|
|
4
|
-
import { buildSessionContext } from "../../../session-manager.js";
|
|
4
|
+
import { buildSessionContext, getSessionContextEntryId, } from "../../../session-manager.js";
|
|
5
5
|
import { SummarizationOverflowExhaustedError } from "./overflow-retry.js";
|
|
6
|
+
import { resolveEffectiveReserveTokens } from "./policy.js";
|
|
6
7
|
import { hasUnsafeRetainedContent } from "./retained-message-safety.js";
|
|
7
8
|
import { SummaryRequestError } from "./speculative.js";
|
|
8
9
|
import { capUtf8Bytes } from "./task-intent.js";
|
|
@@ -59,31 +60,6 @@ function isSafeBoundedValue(value, seen = new Set(), depth = 0) {
|
|
|
59
60
|
}
|
|
60
61
|
return true;
|
|
61
62
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Conservative retained-context size. Fails closed (Infinity) on any unsafe or
|
|
64
|
-
* un-serializable value, and exits early once `budget` is exceeded so an
|
|
65
|
-
* oversized retained message cannot force an allocation-heavy full scan.
|
|
66
|
-
*/
|
|
67
|
-
function estimateConservativeTokens(messages, budget) {
|
|
68
|
-
let total = 0;
|
|
69
|
-
for (const message of messages) {
|
|
70
|
-
if (!isSafeBoundedValue(message))
|
|
71
|
-
return Number.POSITIVE_INFINITY;
|
|
72
|
-
let serialized;
|
|
73
|
-
try {
|
|
74
|
-
serialized = JSON.stringify(message);
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return Number.POSITIVE_INFINITY;
|
|
78
|
-
}
|
|
79
|
-
if (serialized === undefined)
|
|
80
|
-
return Number.POSITIVE_INFINITY;
|
|
81
|
-
total += Math.max(estimateTokens(message), Buffer.byteLength(serialized));
|
|
82
|
-
if (total > budget)
|
|
83
|
-
return total;
|
|
84
|
-
}
|
|
85
|
-
return total;
|
|
86
|
-
}
|
|
87
63
|
export function classifyRequiredCompactionFallbackFailure(error) {
|
|
88
64
|
if (error instanceof StreamDurationBudgetError || error instanceof StreamIdleTimeoutError) {
|
|
89
65
|
return "summarization-timeout";
|
|
@@ -96,9 +72,11 @@ export function classifyRequiredCompactionFallbackFailure(error) {
|
|
|
96
72
|
}
|
|
97
73
|
return undefined;
|
|
98
74
|
}
|
|
99
|
-
export function createRequiredCompactionFallback(preparation, contextWindow, failureKind, metadata, branchEntries = []) {
|
|
75
|
+
export function createRequiredCompactionFallback(preparation, contextWindow, failureKind, metadata, branchEntries = [], diagnostics) {
|
|
100
76
|
const preparedBoundaryIndex = branchEntries.findIndex((entry) => entry.id === preparation.firstKeptEntryId);
|
|
101
77
|
if (!preparation.firstKeptEntryId || preparedBoundaryIndex === -1) {
|
|
78
|
+
if (diagnostics)
|
|
79
|
+
diagnostics.rejectionReason = "missing-preparation-boundary";
|
|
102
80
|
return undefined;
|
|
103
81
|
}
|
|
104
82
|
const marker = [
|
|
@@ -125,7 +103,111 @@ export function createRequiredCompactionFallback(preparation, contextWindow, fai
|
|
|
125
103
|
failureKind,
|
|
126
104
|
...(taskIntent ? { taskIntent } : {}),
|
|
127
105
|
};
|
|
106
|
+
let candidateCount = 0;
|
|
107
|
+
const syntheticCompaction = {
|
|
108
|
+
type: "compaction",
|
|
109
|
+
id: "__senpi_deterministic_fallback_preview__",
|
|
110
|
+
parentId: branchEntries.at(-1)?.id ?? null,
|
|
111
|
+
timestamp: new Date(0).toISOString(),
|
|
112
|
+
summary,
|
|
113
|
+
firstKeptEntryId: branchEntries[0]?.id ?? preparation.firstKeptEntryId,
|
|
114
|
+
tokensBefore: preparation.tokensBefore,
|
|
115
|
+
details: { ...baseDetails, retainedSuffix: "prepared" },
|
|
116
|
+
fromHook: true,
|
|
117
|
+
};
|
|
118
|
+
let projectedMessages;
|
|
119
|
+
try {
|
|
120
|
+
projectedMessages = filterContextExcludedMessages(buildSessionContext([...branchEntries, syntheticCompaction]).messages);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
if (diagnostics)
|
|
124
|
+
diagnostics.rejectionReason = "context-reconstruction-failed";
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const messageIndexesByEntryId = new Map();
|
|
128
|
+
for (const [index, message] of projectedMessages.entries()) {
|
|
129
|
+
const entryId = getSessionContextEntryId(message);
|
|
130
|
+
if (entryId !== undefined)
|
|
131
|
+
messageIndexesByEntryId.set(entryId, index);
|
|
132
|
+
}
|
|
133
|
+
const summaryIndex = messageIndexesByEntryId.get(syntheticCompaction.id);
|
|
134
|
+
const unsafeSuffix = new Array(projectedMessages.length + 1).fill(false);
|
|
135
|
+
const tokenSuffix = new Array(projectedMessages.length + 1).fill(0);
|
|
136
|
+
const toolCalls = new Map();
|
|
137
|
+
const toolResults = new Map();
|
|
138
|
+
for (let index = projectedMessages.length - 1; index >= 0; index--) {
|
|
139
|
+
const message = projectedMessages[index];
|
|
140
|
+
tokenSuffix[index] = tokenSuffix[index + 1];
|
|
141
|
+
let messageUnsafe = false;
|
|
142
|
+
let serialized;
|
|
143
|
+
try {
|
|
144
|
+
messageUnsafe = hasUnsafeRetainedContent([message]);
|
|
145
|
+
serialized = isSafeBoundedValue(message) ? JSON.stringify(message) : undefined;
|
|
146
|
+
tokenSuffix[index] +=
|
|
147
|
+
serialized === undefined
|
|
148
|
+
? Number.POSITIVE_INFINITY
|
|
149
|
+
: Math.max(estimateTokens(message), Buffer.byteLength(serialized));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
messageUnsafe = true;
|
|
153
|
+
tokenSuffix[index] = Number.POSITIVE_INFINITY;
|
|
154
|
+
}
|
|
155
|
+
unsafeSuffix[index] = unsafeSuffix[index + 1] || messageUnsafe;
|
|
156
|
+
if (!isRecord(message))
|
|
157
|
+
continue;
|
|
158
|
+
if (message.role === "toolResult" && typeof message.toolCallId === "string") {
|
|
159
|
+
const indexes = toolResults.get(message.toolCallId) ?? [];
|
|
160
|
+
indexes.push(index);
|
|
161
|
+
toolResults.set(message.toolCallId, indexes);
|
|
162
|
+
}
|
|
163
|
+
else if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
164
|
+
for (const block of message.content) {
|
|
165
|
+
if (!isRecord(block) || block.type !== "toolCall" || typeof block.id !== "string")
|
|
166
|
+
continue;
|
|
167
|
+
const call = toolCalls.get(block.id) ?? { indexes: [], incomplete: false };
|
|
168
|
+
call.indexes.push(index);
|
|
169
|
+
call.incomplete ||= block.incomplete === true;
|
|
170
|
+
toolCalls.set(block.id, call);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const invalidToolChainDiff = new Int32Array(projectedMessages.length + 2);
|
|
175
|
+
const markInvalidRange = (start, endExclusive) => {
|
|
176
|
+
const boundedStart = Math.max(0, start);
|
|
177
|
+
const boundedEnd = Math.min(projectedMessages.length + 1, endExclusive);
|
|
178
|
+
if (boundedStart >= boundedEnd)
|
|
179
|
+
return;
|
|
180
|
+
invalidToolChainDiff[boundedStart]++;
|
|
181
|
+
invalidToolChainDiff[boundedEnd]--;
|
|
182
|
+
};
|
|
183
|
+
for (const [id, call] of toolCalls) {
|
|
184
|
+
const results = toolResults.get(id) ?? [];
|
|
185
|
+
if (call.indexes.length !== 1 || results.length !== 1) {
|
|
186
|
+
markInvalidRange(0, Math.max(call.indexes[0] ?? -1, results[0] ?? -1) + 1);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const callIndex = call.indexes[0];
|
|
190
|
+
const resultIndex = results[0];
|
|
191
|
+
if (call.incomplete)
|
|
192
|
+
markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);
|
|
193
|
+
else if (callIndex < resultIndex)
|
|
194
|
+
markInvalidRange(callIndex + 1, resultIndex + 1);
|
|
195
|
+
else
|
|
196
|
+
markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);
|
|
197
|
+
}
|
|
198
|
+
for (const [id, results] of toolResults) {
|
|
199
|
+
if (toolCalls.has(id))
|
|
200
|
+
continue;
|
|
201
|
+
markInvalidRange(0, results[0] + 1);
|
|
202
|
+
}
|
|
203
|
+
const toolChainValidAt = new Array(projectedMessages.length + 1).fill(true);
|
|
204
|
+
let invalidToolChainCount = 0;
|
|
205
|
+
for (let index = 0; index < toolChainValidAt.length; index++) {
|
|
206
|
+
invalidToolChainCount += invalidToolChainDiff[index];
|
|
207
|
+
toolChainValidAt[index] = invalidToolChainCount === 0;
|
|
208
|
+
}
|
|
128
209
|
const projectCandidate = (firstKeptEntryId, retainedSuffix) => {
|
|
210
|
+
candidateCount++;
|
|
129
211
|
const details = { ...baseDetails, retainedSuffix };
|
|
130
212
|
const result = {
|
|
131
213
|
summary,
|
|
@@ -133,41 +215,74 @@ export function createRequiredCompactionFallback(preparation, contextWindow, fai
|
|
|
133
215
|
tokensBefore: preparation.tokensBefore,
|
|
134
216
|
details,
|
|
135
217
|
};
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
summary: result.summary,
|
|
142
|
-
firstKeptEntryId: result.firstKeptEntryId,
|
|
143
|
-
tokensBefore: result.tokensBefore,
|
|
144
|
-
details: result.details,
|
|
145
|
-
fromHook: true,
|
|
146
|
-
};
|
|
147
|
-
let retainedMessages;
|
|
148
|
-
try {
|
|
149
|
-
retainedMessages = filterContextExcludedMessages(buildSessionContext([...branchEntries, syntheticCompaction]).messages);
|
|
218
|
+
const startIndex = messageIndexesByEntryId.get(firstKeptEntryId);
|
|
219
|
+
if (startIndex === undefined) {
|
|
220
|
+
if (diagnostics)
|
|
221
|
+
diagnostics.rejectionReason = "context-reconstruction-failed";
|
|
222
|
+
return undefined;
|
|
150
223
|
}
|
|
151
|
-
|
|
224
|
+
const retainedStart = Math.min(startIndex, projectedMessages.length);
|
|
225
|
+
if (unsafeSuffix[retainedStart]) {
|
|
226
|
+
if (diagnostics)
|
|
227
|
+
diagnostics.rejectionReason = "unsafe-retained-content";
|
|
152
228
|
return undefined;
|
|
153
229
|
}
|
|
154
|
-
if (
|
|
230
|
+
if (!toolChainValidAt[retainedStart]) {
|
|
231
|
+
if (diagnostics)
|
|
232
|
+
diagnostics.rejectionReason = "atomic-tool-chain-cut";
|
|
155
233
|
return undefined;
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
234
|
+
}
|
|
235
|
+
const budget = contextWindow -
|
|
236
|
+
resolveEffectiveReserveTokens(contextWindow, preparation.settings.reserveTokens, preparation.settings.reserveScalingEnabled !== false);
|
|
237
|
+
const summaryTokens = summaryIndex === undefined
|
|
238
|
+
? Number.POSITIVE_INFINITY
|
|
239
|
+
: Math.max(estimateTokens(projectedMessages[summaryIndex]), Buffer.byteLength(JSON.stringify(projectedMessages[summaryIndex])));
|
|
240
|
+
const retainedTokens = (tokenSuffix[retainedStart] ?? Number.POSITIVE_INFINITY) + summaryTokens;
|
|
241
|
+
if (retainedTokens > budget) {
|
|
242
|
+
if (diagnostics) {
|
|
243
|
+
diagnostics.rejectionReason = "retained-token-budget-exceeded";
|
|
244
|
+
diagnostics.budgetExceeded = true;
|
|
245
|
+
}
|
|
159
246
|
return undefined;
|
|
247
|
+
}
|
|
160
248
|
return { ...result, estimatedTokensAfter: retainedTokens };
|
|
161
249
|
};
|
|
250
|
+
// 1. Try prepared boundary
|
|
162
251
|
const prepared = projectCandidate(preparation.firstKeptEntryId, "prepared");
|
|
163
|
-
if (prepared)
|
|
252
|
+
if (prepared) {
|
|
253
|
+
if (diagnostics)
|
|
254
|
+
diagnostics.candidatesChecked = candidateCount;
|
|
164
255
|
return prepared;
|
|
256
|
+
}
|
|
257
|
+
// 2. If prepared boundary cut an atomic signed chain or was rejected, walk backward
|
|
258
|
+
// to the actual chain boundary. The chain length is determined by the session.
|
|
259
|
+
for (let index = preparedBoundaryIndex - 1; index >= 0; index--) {
|
|
260
|
+
const entry = branchEntries[index];
|
|
261
|
+
if (entry.type === "compaction")
|
|
262
|
+
break;
|
|
263
|
+
// Only consider message turn starts or assistant call starts
|
|
264
|
+
const earlier = projectCandidate(entry.id, "earlier-safe-boundary");
|
|
265
|
+
if (earlier) {
|
|
266
|
+
if (diagnostics)
|
|
267
|
+
diagnostics.candidatesChecked = candidateCount;
|
|
268
|
+
return earlier;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// 3. Try latest meaningful user turn
|
|
165
272
|
for (let index = branchEntries.length - 1; index > preparedBoundaryIndex; index--) {
|
|
166
273
|
const entry = branchEntries[index];
|
|
167
274
|
if (!hasMeaningfulUserText(entry))
|
|
168
275
|
continue;
|
|
169
|
-
|
|
276
|
+
const latestUser = projectCandidate(entry.id, "latest-user-turn");
|
|
277
|
+
if (latestUser) {
|
|
278
|
+
if (diagnostics)
|
|
279
|
+
diagnostics.candidatesChecked = candidateCount;
|
|
280
|
+
return latestUser;
|
|
281
|
+
}
|
|
282
|
+
break;
|
|
170
283
|
}
|
|
284
|
+
if (diagnostics)
|
|
285
|
+
diagnostics.candidatesChecked = candidateCount;
|
|
171
286
|
return undefined;
|
|
172
287
|
}
|
|
173
288
|
//# sourceMappingURL=deterministic-fallback.js.map
|