@vybestack/llxprt-code-core 0.11.0 → 0.12.0-nightly.260910.2aac6841e
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/prompt-config/defaults/default-prompts.json +3 -3
- package/dist/src/config/agentClientLifecycle.d.ts +4 -3
- package/dist/src/config/agentClientLifecycle.js +27 -14
- package/dist/src/config/agentClientLifecycle.js.map +1 -1
- package/dist/src/config/config.d.ts +2 -5
- package/dist/src/config/config.js +10 -23
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/configBase.d.ts +5 -1
- package/dist/src/config/configBase.js +27 -6
- package/dist/src/config/configBase.js.map +1 -1
- package/dist/src/config/configBaseCore.d.ts +26 -8
- package/dist/src/config/configBaseCore.js +30 -26
- package/dist/src/config/configBaseCore.js.map +1 -1
- package/dist/src/config/configMcpRuntime.d.ts +16 -0
- package/dist/src/config/configMcpRuntime.js +31 -0
- package/dist/src/config/configMcpRuntime.js.map +1 -0
- package/dist/src/config/configMediaDefaults.d.ts +10 -0
- package/dist/src/config/configMediaDefaults.js +17 -0
- package/dist/src/config/configMediaDefaults.js.map +1 -0
- package/dist/src/config/configTestHarness.d.ts +4 -0
- package/dist/src/config/configTypes.d.ts +4 -54
- package/dist/src/config/configTypes.js +6 -82
- package/dist/src/config/configTypes.js.map +1 -1
- package/dist/src/config/index.d.ts +1 -2
- package/dist/src/config/index.js +1 -2
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/models.d.ts +0 -4
- package/dist/src/config/models.js +0 -4
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/toolRegistryFactory.d.ts +2 -2
- package/dist/src/core/clientContract.d.ts +4 -6
- package/dist/src/core/compression/transcriptPathNotice.d.ts +22 -0
- package/dist/src/core/compression/transcriptPathNotice.js +29 -0
- package/dist/src/core/compression/transcriptPathNotice.js.map +1 -0
- package/dist/src/core/contentGenerator.d.ts +2 -5
- package/dist/src/core/contentGenerator.js +10 -24
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/logger.d.ts +66 -0
- package/dist/src/core/logger.js +345 -30
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/prompts.d.ts +0 -1
- package/dist/src/core/prompts.js +5 -79
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/turn.d.ts +3 -3
- package/dist/src/hooks/hookTranslator.js +5 -4
- package/dist/src/hooks/hookTranslator.js.map +1 -1
- package/dist/src/index.d.ts +8 -6
- package/dist/src/index.js +8 -6
- package/dist/src/index.js.map +1 -1
- package/dist/src/llm-types/index.d.ts +1 -1
- package/dist/src/llm-types/modelEnvelope.d.ts +2 -1
- package/dist/src/llm-types/modelEnvelope.js +17 -1
- package/dist/src/llm-types/modelEnvelope.js.map +1 -1
- package/dist/src/models/schema.d.ts +40 -40
- package/dist/src/policy/policies/read-only.toml +0 -5
- package/dist/src/prompt-config/TemplateEngine.js +0 -3
- package/dist/src/prompt-config/TemplateEngine.js.map +1 -1
- package/dist/src/prompt-config/defaults/providers/gemini/core.md +0 -2
- package/dist/src/prompt-config/defaults/providers/gemini/models/gemini-2.5-flash/core.md +0 -2
- package/dist/src/prompt-config/defaults/providers/gemini/models/gemini-2.5-flash/gemini-2-5-flash/core.md +0 -2
- package/dist/src/prompt-config/prompt-loader.d.ts +1 -1
- package/dist/src/prompt-config/prompt-loader.js +4 -4
- package/dist/src/prompt-config/prompt-loader.js.map +1 -1
- package/dist/src/prompt-config/types.d.ts +0 -1
- package/dist/src/recording/CheckpointService.d.ts +4 -0
- package/dist/src/recording/CheckpointService.js +67 -46
- package/dist/src/recording/CheckpointService.js.map +1 -1
- package/dist/src/recording/HistoryMutationService.d.ts +1 -1
- package/dist/src/recording/HistoryMutationService.js +27 -4
- package/dist/src/recording/HistoryMutationService.js.map +1 -1
- package/dist/src/recording/RecordingIntegration.d.ts +36 -2
- package/dist/src/recording/RecordingIntegration.js +224 -7
- package/dist/src/recording/RecordingIntegration.js.map +1 -1
- package/dist/src/recording/ReplayEngine.d.ts +8 -10
- package/dist/src/recording/ReplayEngine.js +161 -95
- package/dist/src/recording/ReplayEngine.js.map +1 -1
- package/dist/src/recording/SessionDiscovery.d.ts +4 -2
- package/dist/src/recording/SessionDiscovery.js +13 -5
- package/dist/src/recording/SessionDiscovery.js.map +1 -1
- package/dist/src/recording/SessionRecordingService.d.ts +23 -3
- package/dist/src/recording/SessionRecordingService.js +209 -51
- package/dist/src/recording/SessionRecordingService.js.map +1 -1
- package/dist/src/recording/SessionTransitionService.d.ts +7 -0
- package/dist/src/recording/SessionTransitionService.js +66 -13
- package/dist/src/recording/SessionTransitionService.js.map +1 -1
- package/dist/src/recording/index.d.ts +1 -0
- package/dist/src/recording/index.js +1 -0
- package/dist/src/recording/index.js.map +1 -1
- package/dist/src/recording/janitor/cleanupTypes.d.ts +3 -0
- package/dist/src/recording/janitor/cleanupTypes.js.map +1 -1
- package/dist/src/recording/janitor/mediaReclamation.d.ts +23 -0
- package/dist/src/recording/janitor/mediaReclamation.js +195 -0
- package/dist/src/recording/janitor/mediaReclamation.js.map +1 -0
- package/dist/src/recording/janitor/sessionJanitor.js +47 -1
- package/dist/src/recording/janitor/sessionJanitor.js.map +1 -1
- package/dist/src/recording/janitor/sessionScanner.d.ts +12 -9
- package/dist/src/recording/janitor/sessionScanner.js +248 -193
- package/dist/src/recording/janitor/sessionScanner.js.map +1 -1
- package/dist/src/recording/replay-test-helpers.d.ts +10 -1
- package/dist/src/recording/replay-test-helpers.js +24 -2
- package/dist/src/recording/replay-test-helpers.js.map +1 -1
- package/dist/src/recording/replayCheckpointMetadata.d.ts +28 -0
- package/dist/src/recording/replayCheckpointMetadata.js +96 -0
- package/dist/src/recording/replayCheckpointMetadata.js.map +1 -0
- package/dist/src/recording/replayErrorFormatting.d.ts +6 -0
- package/dist/src/recording/replayErrorFormatting.js +38 -0
- package/dist/src/recording/replayErrorFormatting.js.map +1 -0
- package/dist/src/recording/resumeSession.d.ts +3 -0
- package/dist/src/recording/resumeSession.js +7 -1
- package/dist/src/recording/resumeSession.js.map +1 -1
- package/dist/src/recording/semanticMediaPurgeReplayValidation.d.ts +14 -0
- package/dist/src/recording/semanticMediaPurgeReplayValidation.js +24 -0
- package/dist/src/recording/semanticMediaPurgeReplayValidation.js.map +1 -0
- package/dist/src/recording/session-media-package-blobs.d.ts +8 -0
- package/dist/src/recording/session-media-package-blobs.js +72 -0
- package/dist/src/recording/session-media-package-blobs.js.map +1 -0
- package/dist/src/recording/session-media-package-import.d.ts +26 -0
- package/dist/src/recording/session-media-package-import.js +124 -0
- package/dist/src/recording/session-media-package-import.js.map +1 -0
- package/dist/src/recording/session-media-package-state.d.ts +19 -0
- package/dist/src/recording/session-media-package-state.js +56 -0
- package/dist/src/recording/session-media-package-state.js.map +1 -0
- package/dist/src/recording/session-media-package-validation.d.ts +59 -0
- package/dist/src/recording/session-media-package-validation.js +286 -0
- package/dist/src/recording/session-media-package-validation.js.map +1 -0
- package/dist/src/recording/session-media-package-writer.d.ts +18 -0
- package/dist/src/recording/session-media-package-writer.js +70 -0
- package/dist/src/recording/session-media-package-writer.js.map +1 -0
- package/dist/src/recording/session-media-package.d.ts +27 -0
- package/dist/src/recording/session-media-package.js +366 -0
- package/dist/src/recording/session-media-package.js.map +1 -0
- package/dist/src/recording/types.d.ts +34 -2
- package/dist/src/recording/types.js.map +1 -1
- package/dist/src/runtime/AgentRuntimeContext.d.ts +22 -5
- package/dist/src/runtime/AgentRuntimeLoader.d.ts +6 -0
- package/dist/src/runtime/AgentRuntimeLoader.js +25 -6
- package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -1
- package/dist/src/runtime/contracts/PromptEstimation.d.ts +1 -0
- package/dist/src/runtime/contracts/PromptEstimation.js.map +1 -1
- package/dist/src/runtime/contracts/RuntimeProvider.d.ts +3 -2
- package/dist/src/runtime/contracts/RuntimeProviderManager.d.ts +0 -2
- package/dist/src/runtime/createAgentRuntimeContext.js +28 -1
- package/dist/src/runtime/createAgentRuntimeContext.js.map +1 -1
- package/dist/src/runtime/providerRuntimeContext.d.ts +12 -0
- package/dist/src/runtime/providerRuntimeContext.js +3 -0
- package/dist/src/runtime/providerRuntimeContext.js.map +1 -1
- package/dist/src/runtime/runtimeAdapters.js +2 -2
- package/dist/src/runtime/runtimeAdapters.js.map +1 -1
- package/dist/src/services/history/ContentConverters.d.ts +11 -7
- package/dist/src/services/history/ContentConverters.js +11 -7
- package/dist/src/services/history/ContentConverters.js.map +1 -1
- package/dist/src/services/history/HistoryService.d.ts +18 -180
- package/dist/src/services/history/HistoryService.js +53 -523
- package/dist/src/services/history/HistoryService.js.map +1 -1
- package/dist/src/services/history/HistoryServiceCore.d.ts +278 -0
- package/dist/src/services/history/HistoryServiceCore.js +727 -0
- package/dist/src/services/history/HistoryServiceCore.js.map +1 -0
- package/dist/src/services/history/IContent.d.ts +118 -16
- package/dist/src/services/history/IContent.js +284 -4
- package/dist/src/services/history/IContent.js.map +1 -1
- package/dist/src/services/history/contentSize.js +22 -2
- package/dist/src/services/history/contentSize.js.map +1 -1
- package/dist/src/services/history/historyChronology.d.ts +7 -0
- package/dist/src/services/history/historyChronology.js +35 -3
- package/dist/src/services/history/historyChronology.js.map +1 -1
- package/dist/src/services/history/historyCloneUtils.d.ts +5 -4
- package/dist/src/services/history/historyCloneUtils.js +23 -16
- package/dist/src/services/history/historyCloneUtils.js.map +1 -1
- package/dist/src/services/history/historyCompressionQueue.d.ts +33 -0
- package/dist/src/services/history/historyCompressionQueue.js +81 -0
- package/dist/src/services/history/historyCompressionQueue.js.map +1 -0
- package/dist/src/services/history/historyEventTypes.d.ts +3 -0
- package/dist/src/services/history/historyMutationFailure.d.ts +15 -0
- package/dist/src/services/history/historyMutationFailure.js +22 -0
- package/dist/src/services/history/historyMutationFailure.js.map +1 -1
- package/dist/src/services/history/historyProviderPipeline.d.ts +1 -1
- package/dist/src/services/history/historyProviderPipeline.js +15 -5
- package/dist/src/services/history/historyProviderPipeline.js.map +1 -1
- package/dist/src/services/history/historyTokenEstimation.js +10 -6
- package/dist/src/services/history/historyTokenEstimation.js.map +1 -1
- package/dist/src/services/history/historyToolNormalization.d.ts +2 -0
- package/dist/src/services/history/historyToolNormalization.js +26 -9
- package/dist/src/services/history/historyToolNormalization.js.map +1 -1
- package/dist/src/services/history/provider-file-binding.d.ts +11 -0
- package/dist/src/services/history/provider-file-binding.js +70 -0
- package/dist/src/services/history/provider-file-binding.js.map +1 -0
- package/dist/src/services/history/semantic-media-purge.d.ts +66 -0
- package/dist/src/services/history/semantic-media-purge.js +404 -0
- package/dist/src/services/history/semantic-media-purge.js.map +1 -0
- package/dist/src/storage/SessionPersistenceService.d.ts +53 -2
- package/dist/src/storage/SessionPersistenceService.js +435 -23
- package/dist/src/storage/SessionPersistenceService.js.map +1 -1
- package/dist/src/storage/history-media-ownership.d.ts +38 -0
- package/dist/src/storage/history-media-ownership.js +136 -0
- package/dist/src/storage/history-media-ownership.js.map +1 -0
- package/dist/src/storage/local-media-store-child.d.ts +6 -0
- package/dist/src/storage/local-media-store-child.js +140 -0
- package/dist/src/storage/local-media-store-child.js.map +1 -0
- package/dist/src/storage/local-media-store-files.d.ts +44 -0
- package/dist/src/storage/local-media-store-files.js +460 -0
- package/dist/src/storage/local-media-store-files.js.map +1 -0
- package/dist/src/storage/local-media-store-lock-recovery.d.ts +15 -0
- package/dist/src/storage/local-media-store-lock-recovery.js +112 -0
- package/dist/src/storage/local-media-store-lock-recovery.js.map +1 -0
- package/dist/src/storage/local-media-store-persistence.d.ts +48 -0
- package/dist/src/storage/local-media-store-persistence.js +443 -0
- package/dist/src/storage/local-media-store-persistence.js.map +1 -0
- package/dist/src/storage/local-media-store-quota.d.ts +6 -0
- package/dist/src/storage/local-media-store-quota.js +66 -0
- package/dist/src/storage/local-media-store-quota.js.map +1 -0
- package/dist/src/storage/local-media-store-reservations.d.ts +55 -0
- package/dist/src/storage/local-media-store-reservations.js +377 -0
- package/dist/src/storage/local-media-store-reservations.js.map +1 -0
- package/dist/src/storage/local-media-store-types.d.ts +110 -0
- package/dist/src/storage/local-media-store-types.js +35 -0
- package/dist/src/storage/local-media-store-types.js.map +1 -0
- package/dist/src/storage/local-media-store-validation.d.ts +34 -0
- package/dist/src/storage/local-media-store-validation.js +449 -0
- package/dist/src/storage/local-media-store-validation.js.map +1 -0
- package/dist/src/storage/local-media-store.d.ts +47 -0
- package/dist/src/storage/local-media-store.js +353 -0
- package/dist/src/storage/local-media-store.js.map +1 -0
- package/dist/src/storage/media-admission-service.d.ts +49 -0
- package/dist/src/storage/media-admission-service.js +333 -0
- package/dist/src/storage/media-admission-service.js.map +1 -0
- package/dist/src/storage/media-lifecycle-metrics.d.ts +75 -0
- package/dist/src/storage/media-lifecycle-metrics.js +105 -0
- package/dist/src/storage/media-lifecycle-metrics.js.map +1 -0
- package/dist/src/storage/media-reference-lifecycle.d.ts +16 -0
- package/dist/src/storage/media-reference-lifecycle.js +84 -0
- package/dist/src/storage/media-reference-lifecycle.js.map +1 -0
- package/dist/src/storage/request-media-resolver.d.ts +67 -0
- package/dist/src/storage/request-media-resolver.js +325 -0
- package/dist/src/storage/request-media-resolver.js.map +1 -0
- package/dist/src/storage/session-media-service-factories.d.ts +19 -0
- package/dist/src/storage/session-media-service-factories.js +34 -0
- package/dist/src/storage/session-media-service-factories.js.map +1 -0
- package/dist/src/storage/session-persistence-helpers.d.ts +10 -0
- package/dist/src/storage/session-persistence-helpers.js +46 -0
- package/dist/src/storage/session-persistence-helpers.js.map +1 -0
- package/dist/src/test-utils/config.d.ts +2 -0
- package/dist/src/test-utils/config.js +5 -5
- package/dist/src/test-utils/config.js.map +1 -1
- package/dist/src/test-utils/runtime.d.ts +2 -0
- package/dist/src/test-utils/runtime.js +1 -2
- package/dist/src/test-utils/runtime.js.map +1 -1
- package/dist/src/tools-adapters/CoreShellToolHostAdapter.js +1 -9
- package/dist/src/tools-adapters/CoreShellToolHostAdapter.js.map +1 -1
- package/dist/src/tools-adapters/CoreSkillServiceAdapter.d.ts +0 -2
- package/dist/src/tools-adapters/CoreSkillServiceAdapter.js +0 -17
- package/dist/src/tools-adapters/CoreSkillServiceAdapter.js.map +1 -1
- package/dist/src/tools-adapters/CoreToolHostAdapter.d.ts +0 -21
- package/dist/src/tools-adapters/CoreToolHostAdapter.js +0 -8
- package/dist/src/tools-adapters/CoreToolHostAdapter.js.map +1 -1
- package/dist/src/utils/environmentContext.d.ts +8 -9
- package/dist/src/utils/environmentContext.js +11 -22
- package/dist/src/utils/environmentContext.js.map +1 -1
- package/dist/src/utils/errorParsing.d.ts +1 -2
- package/dist/src/utils/errorParsing.js +22 -41
- package/dist/src/utils/errorParsing.js.map +1 -1
- package/dist/src/utils/generateContentResponseUtilities.js +12 -0
- package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
- package/dist/src/utils/summarizer.d.ts +1 -2
- package/dist/src/utils/summarizer.js +6 -5
- package/dist/src/utils/summarizer.js.map +1 -1
- package/dist/src/utils/tool-utils.js +7 -1
- package/dist/src/utils/tool-utils.js.map +1 -1
- package/index.ts +0 -4
- package/package.json +38 -30
- package/src/config/agentClientLifecycle.ts +53 -22
- package/src/config/config.ts +11 -26
- package/src/config/configBase.ts +46 -7
- package/src/config/configBaseCore.ts +38 -36
- package/src/config/configMcpRuntime.ts +46 -0
- package/src/config/configMediaDefaults.ts +19 -0
- package/src/config/configTypes.ts +13 -53
- package/src/config/index.ts +1 -4
- package/src/config/models.ts +0 -6
- package/src/config/toolRegistryFactory.ts +2 -2
- package/src/core/clientContract.ts +4 -6
- package/src/core/compression/transcriptPathNotice.ts +31 -0
- package/src/core/contentGenerator.ts +15 -39
- package/src/core/logger.ts +370 -39
- package/src/core/prompts.ts +4 -114
- package/src/core/turn.ts +3 -3
- package/src/hooks/hookTranslator.ts +6 -4
- package/src/index.ts +7 -7
- package/src/llm-types/index.ts +5 -0
- package/src/llm-types/modelEnvelope.ts +26 -2
- package/src/policy/policies/read-only.toml +0 -5
- package/src/prompt-config/TemplateEngine.ts +0 -3
- package/src/prompt-config/defaults/providers/gemini/core.md +0 -2
- package/src/prompt-config/defaults/providers/gemini/models/gemini-2.5-flash/core.md +0 -2
- package/src/prompt-config/defaults/providers/gemini/models/gemini-2.5-flash/gemini-2-5-flash/core.md +0 -2
- package/src/prompt-config/prompt-loader.ts +7 -8
- package/src/prompt-config/types.ts +0 -1
- package/src/recording/CheckpointService.ts +110 -45
- package/src/recording/HistoryMutationService.ts +28 -3
- package/src/recording/RecordingIntegration.ts +258 -11
- package/src/recording/ReplayEngine.ts +220 -110
- package/src/recording/SessionDiscovery.ts +30 -4
- package/src/recording/SessionRecordingService.ts +253 -55
- package/src/recording/SessionTransitionService.ts +75 -12
- package/src/recording/index.ts +7 -0
- package/src/recording/janitor/cleanupTypes.ts +4 -0
- package/src/recording/janitor/mediaReclamation.ts +295 -0
- package/src/recording/janitor/sessionJanitor.ts +54 -1
- package/src/recording/janitor/sessionScanner.ts +319 -227
- package/src/recording/replay-test-helpers.ts +33 -2
- package/src/recording/replayCheckpointMetadata.ts +115 -0
- package/src/recording/replayErrorFormatting.ts +43 -0
- package/src/recording/resumeSession.ts +10 -0
- package/src/recording/semanticMediaPurgeReplayValidation.ts +35 -0
- package/src/recording/session-media-package-blobs.ts +116 -0
- package/src/recording/session-media-package-import.ts +189 -0
- package/src/recording/session-media-package-state.ts +120 -0
- package/src/recording/session-media-package-validation.ts +400 -0
- package/src/recording/session-media-package-writer.ts +117 -0
- package/src/recording/session-media-package.ts +692 -0
- package/src/recording/types.ts +37 -2
- package/src/runtime/AgentRuntimeContext.ts +28 -5
- package/src/runtime/AgentRuntimeLoader.ts +32 -7
- package/src/runtime/contracts/PromptEstimation.ts +1 -0
- package/src/runtime/contracts/RuntimeProvider.ts +3 -8
- package/src/runtime/contracts/RuntimeProviderManager.ts +0 -2
- package/src/runtime/createAgentRuntimeContext.ts +43 -1
- package/src/runtime/providerRuntimeContext.ts +22 -0
- package/src/runtime/runtimeAdapters.ts +2 -2
- package/src/services/history/ContentConverters.ts +11 -7
- package/src/services/history/HistoryService.ts +71 -696
- package/src/services/history/HistoryServiceCore.ts +1055 -0
- package/src/services/history/IContent.ts +460 -20
- package/src/services/history/contentSize.ts +22 -2
- package/src/services/history/historyChronology.ts +53 -3
- package/src/services/history/historyCloneUtils.ts +26 -22
- package/src/services/history/historyCompressionQueue.ts +102 -0
- package/src/services/history/historyEventTypes.ts +9 -0
- package/src/services/history/historyMutationFailure.ts +33 -0
- package/src/services/history/historyProviderPipeline.ts +19 -5
- package/src/services/history/historyTokenEstimation.ts +9 -5
- package/src/services/history/historyToolNormalization.ts +43 -11
- package/src/services/history/provider-file-binding.ts +115 -0
- package/src/services/history/semantic-media-purge.ts +593 -0
- package/src/storage/SessionPersistenceService.ts +667 -30
- package/src/storage/history-media-ownership.ts +185 -0
- package/src/storage/local-media-store-child.ts +163 -0
- package/src/storage/local-media-store-files.ts +643 -0
- package/src/storage/local-media-store-lock-recovery.ts +159 -0
- package/src/storage/local-media-store-persistence.ts +737 -0
- package/src/storage/local-media-store-quota.ts +111 -0
- package/src/storage/local-media-store-reservations.ts +485 -0
- package/src/storage/local-media-store-types.ts +161 -0
- package/src/storage/local-media-store-validation.ts +643 -0
- package/src/storage/local-media-store.ts +696 -0
- package/src/storage/media-admission-service.ts +512 -0
- package/src/storage/media-lifecycle-metrics.ts +228 -0
- package/src/storage/media-reference-lifecycle.ts +164 -0
- package/src/storage/request-media-resolver.ts +491 -0
- package/src/storage/session-media-service-factories.ts +48 -0
- package/src/storage/session-persistence-helpers.ts +85 -0
- package/src/test-utils/config.ts +7 -5
- package/src/test-utils/runtime.ts +3 -2
- package/src/tools-adapters/CoreShellToolHostAdapter.ts +1 -11
- package/src/tools-adapters/CoreSkillServiceAdapter.ts +0 -23
- package/src/tools-adapters/CoreToolHostAdapter.ts +0 -36
- package/src/utils/environmentContext.ts +11 -30
- package/src/utils/errorParsing.ts +19 -69
- package/src/utils/generateContentResponseUtilities.ts +28 -1
- package/src/utils/summarizer.ts +8 -10
- package/src/utils/tool-utils.ts +7 -1
- package/dist/src/code_assist/codeAssist.d.ts +0 -19
- package/dist/src/code_assist/codeAssist.js +0 -41
- package/dist/src/code_assist/codeAssist.js.map +0 -1
- package/dist/src/code_assist/contentGeneratorAdapters.d.ts +0 -49
- package/dist/src/code_assist/contentGeneratorAdapters.js +0 -197
- package/dist/src/code_assist/contentGeneratorAdapters.js.map +0 -1
- package/dist/src/code_assist/converter.d.ts +0 -74
- package/dist/src/code_assist/converter.js +0 -158
- package/dist/src/code_assist/converter.js.map +0 -1
- package/dist/src/code_assist/googleGenAIWrapper.d.ts +0 -27
- package/dist/src/code_assist/googleGenAIWrapper.js +0 -46
- package/dist/src/code_assist/googleGenAIWrapper.js.map +0 -1
- package/dist/src/code_assist/oauth-credential-storage.d.ts +0 -30
- package/dist/src/code_assist/oauth-credential-storage.js +0 -135
- package/dist/src/code_assist/oauth-credential-storage.js.map +0 -1
- package/dist/src/code_assist/server.d.ts +0 -44
- package/dist/src/code_assist/server.js +0 -154
- package/dist/src/code_assist/server.js.map +0 -1
- package/dist/src/code_assist/setup.d.ts +0 -20
- package/dist/src/code_assist/setup.js +0 -126
- package/dist/src/code_assist/setup.js.map +0 -1
- package/dist/src/code_assist/types.d.ts +0 -161
- package/dist/src/code_assist/types.js +0 -46
- package/dist/src/code_assist/types.js.map +0 -1
- package/dist/src/utils/getFolderStructure.d.ts +0 -31
- package/dist/src/utils/getFolderStructure.js +0 -240
- package/dist/src/utils/getFolderStructure.js.map +0 -1
- package/dist/src/utils/installationManager.d.ts +0 -16
- package/dist/src/utils/installationManager.js +0 -51
- package/dist/src/utils/installationManager.js.map +0 -1
- package/dist/src/utils/llm-edit-fixer.d.ts +0 -25
- package/dist/src/utils/llm-edit-fixer.js +0 -120
- package/dist/src/utils/llm-edit-fixer.js.map +0 -1
- package/src/code_assist/codeAssist.ts +0 -59
- package/src/code_assist/contentGeneratorAdapters.ts +0 -275
- package/src/code_assist/converter.ts +0 -288
- package/src/code_assist/googleGenAIWrapper.ts +0 -90
- package/src/code_assist/oauth-credential-storage.ts +0 -171
- package/src/code_assist/server.ts +0 -263
- package/src/code_assist/setup.ts +0 -189
- package/src/code_assist/types.ts +0 -201
- package/src/utils/getFolderStructure.ts +0 -382
- package/src/utils/installationManager.ts +0 -59
- package/src/utils/llm-edit-fixer.ts +0 -157
|
@@ -10,6 +10,20 @@ import * as crypto from 'node:crypto';
|
|
|
10
10
|
import { type IContent } from '../services/history/IContent.js';
|
|
11
11
|
import type { Storage } from '@vybestack/llxprt-code-settings';
|
|
12
12
|
import { DebugLogger } from '../debug/index.js';
|
|
13
|
+
import type { LocalMediaStore } from './local-media-store.js';
|
|
14
|
+
import {
|
|
15
|
+
MediaAdmissionService,
|
|
16
|
+
type MediaAdmissionContext,
|
|
17
|
+
} from './media-admission-service.js';
|
|
18
|
+
import {
|
|
19
|
+
collectMediaReferences,
|
|
20
|
+
verifyHistoryMedia,
|
|
21
|
+
} from './media-reference-lifecycle.js';
|
|
22
|
+
import {
|
|
23
|
+
containsMediaDiagnostic,
|
|
24
|
+
errorCode,
|
|
25
|
+
persistenceRequestLowerBound,
|
|
26
|
+
} from './session-persistence-helpers.js';
|
|
13
27
|
import {
|
|
14
28
|
type ToolResultDisplay,
|
|
15
29
|
type ToolCallConfirmationDetails,
|
|
@@ -66,6 +80,8 @@ export interface PersistedUIHistoryItem {
|
|
|
66
80
|
export interface PersistedSession {
|
|
67
81
|
/** Schema version for future migrations */
|
|
68
82
|
version: number;
|
|
83
|
+
/** Monotonic save generation. Absent in legacy persisted sessions. */
|
|
84
|
+
generation?: number;
|
|
69
85
|
/** Unique session identifier */
|
|
70
86
|
sessionId: string;
|
|
71
87
|
/** Hash of project root for validation */
|
|
@@ -91,6 +107,53 @@ export interface PersistedSession {
|
|
|
91
107
|
*/
|
|
92
108
|
const PERSISTED_SESSION_PREFIX = 'persisted-session-';
|
|
93
109
|
|
|
110
|
+
export interface SessionPersistenceServiceOptions {
|
|
111
|
+
readonly mediaStore?: LocalMediaStore;
|
|
112
|
+
readonly maxQueueBytes?: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface PreparedPersistenceSave {
|
|
116
|
+
publish(): Promise<void>;
|
|
117
|
+
rollback(): Promise<void>;
|
|
118
|
+
finalize(): Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface LoadedSessionMediaOwnership {
|
|
122
|
+
release(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface LoadedPersistedSession extends PersistedSession {
|
|
126
|
+
readonly mediaOwnership: LoadedSessionMediaOwnership;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface PendingPersistenceSave {
|
|
130
|
+
readonly generation: number;
|
|
131
|
+
readonly history: IContent[];
|
|
132
|
+
readonly metadata: PersistedSession['metadata'] | undefined;
|
|
133
|
+
readonly uiHistory: PersistedUIHistoryItem[] | undefined;
|
|
134
|
+
readonly updatedAt: string;
|
|
135
|
+
accountedBytes: number;
|
|
136
|
+
readonly resolve: () => void;
|
|
137
|
+
readonly reject: (error: unknown) => void;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface PreparedSaveState {
|
|
141
|
+
accountedBytes: number;
|
|
142
|
+
admittedHistory: readonly IContent[] | undefined;
|
|
143
|
+
reservedContentIds: readonly string[];
|
|
144
|
+
previousContents: Buffer | undefined;
|
|
145
|
+
ownershipPending: boolean;
|
|
146
|
+
published: boolean;
|
|
147
|
+
settled: boolean;
|
|
148
|
+
readonly admissionContext: MediaAdmissionContext;
|
|
149
|
+
readonly ownerId: string;
|
|
150
|
+
readonly tempPath: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type PersistenceSaveOutcome =
|
|
154
|
+
| { readonly ok: true }
|
|
155
|
+
| { readonly ok: false; readonly error: unknown };
|
|
156
|
+
|
|
94
157
|
/**
|
|
95
158
|
* Service for persisting and restoring conversation sessions.
|
|
96
159
|
* Enables the --continue flag to resume previous sessions.
|
|
@@ -100,10 +163,30 @@ export class SessionPersistenceService {
|
|
|
100
163
|
private readonly sessionId: string;
|
|
101
164
|
private readonly chatsDir: string;
|
|
102
165
|
private readonly sessionFilePath: string;
|
|
166
|
+
private readonly mediaStore: LocalMediaStore | undefined;
|
|
167
|
+
private readonly maxQueueBytes: number;
|
|
168
|
+
private pendingBytes = 0;
|
|
169
|
+
private nextGeneration = 0;
|
|
170
|
+
private saveInProgress = false;
|
|
171
|
+
private transactionActive = false;
|
|
172
|
+
private readonly transactionWaiters: Array<() => void> = [];
|
|
173
|
+
private readonly saveQueue: PendingPersistenceSave[] = [];
|
|
103
174
|
|
|
104
|
-
constructor(
|
|
175
|
+
constructor(
|
|
176
|
+
storage: Storage,
|
|
177
|
+
sessionId: string,
|
|
178
|
+
options: SessionPersistenceServiceOptions = {},
|
|
179
|
+
) {
|
|
180
|
+
const maxQueueBytes = options.maxQueueBytes ?? Number.MAX_SAFE_INTEGER;
|
|
181
|
+
if (!Number.isSafeInteger(maxQueueBytes) || maxQueueBytes < 0) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
'Session persistence queue byte limit must be a non-negative safe integer',
|
|
184
|
+
);
|
|
185
|
+
}
|
|
105
186
|
this.storage = storage;
|
|
106
187
|
this.sessionId = sessionId;
|
|
188
|
+
this.mediaStore = options.mediaStore;
|
|
189
|
+
this.maxQueueBytes = maxQueueBytes;
|
|
107
190
|
this.chatsDir = path.join(storage.getProjectTempDir(), 'chats');
|
|
108
191
|
|
|
109
192
|
// Use timestamp-based filename for easy "most recent" lookup
|
|
@@ -114,6 +197,111 @@ export class SessionPersistenceService {
|
|
|
114
197
|
);
|
|
115
198
|
}
|
|
116
199
|
|
|
200
|
+
private preflightQueueRequest(
|
|
201
|
+
history: readonly IContent[],
|
|
202
|
+
metadata: PersistedSession['metadata'] | undefined,
|
|
203
|
+
uiHistory: readonly PersistedUIHistoryItem[] | undefined,
|
|
204
|
+
): void {
|
|
205
|
+
const lowerBound = persistenceRequestLowerBound(
|
|
206
|
+
history,
|
|
207
|
+
metadata,
|
|
208
|
+
uiHistory,
|
|
209
|
+
);
|
|
210
|
+
if (lowerBound > this.maxQueueBytes - this.pendingBytes) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Session persistence queue byte limit exceeded: ${this.pendingBytes} + at least ${lowerBound} > ${this.maxQueueBytes}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private async releaseAdmission(
|
|
218
|
+
history: readonly IContent[],
|
|
219
|
+
context: MediaAdmissionContext,
|
|
220
|
+
): Promise<void> {
|
|
221
|
+
if (this.mediaStore === undefined) return;
|
|
222
|
+
await new MediaAdmissionService(this.mediaStore).releaseContents(
|
|
223
|
+
history,
|
|
224
|
+
context,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private async releaseSaveOwnership(
|
|
229
|
+
admissionHistory: readonly IContent[] | undefined,
|
|
230
|
+
admissionContext: MediaAdmissionContext,
|
|
231
|
+
reservedContentIds: readonly string[],
|
|
232
|
+
ownerId: string,
|
|
233
|
+
): Promise<void> {
|
|
234
|
+
const failures: unknown[] = [];
|
|
235
|
+
await this.collectCleanupFailure(failures, () =>
|
|
236
|
+
this.releaseMedia(reservedContentIds, ownerId),
|
|
237
|
+
);
|
|
238
|
+
if (admissionHistory !== undefined) {
|
|
239
|
+
await this.collectCleanupFailure(failures, () =>
|
|
240
|
+
this.releaseAdmission(admissionHistory, admissionContext),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (failures.length === 1) throw failures[0];
|
|
244
|
+
if (failures.length > 1) {
|
|
245
|
+
throw new AggregateError(
|
|
246
|
+
failures,
|
|
247
|
+
'Session media ownership release failed',
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private async reserveMedia(
|
|
253
|
+
history: readonly IContent[],
|
|
254
|
+
ownerId: string,
|
|
255
|
+
): Promise<readonly string[]> {
|
|
256
|
+
if (this.mediaStore === undefined) return [];
|
|
257
|
+
const references = collectMediaReferences(history);
|
|
258
|
+
const unique = new Map(
|
|
259
|
+
references.map((reference) => [reference.contentId, reference]),
|
|
260
|
+
);
|
|
261
|
+
const reserved: string[] = [];
|
|
262
|
+
try {
|
|
263
|
+
for (const reference of unique.values()) {
|
|
264
|
+
await this.mediaStore.reserve(reference, ownerId);
|
|
265
|
+
reserved.push(reference.contentId);
|
|
266
|
+
}
|
|
267
|
+
return reserved;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
const releaseFailures: unknown[] = [];
|
|
270
|
+
for (const contentId of reserved) {
|
|
271
|
+
try {
|
|
272
|
+
await this.mediaStore.release(contentId, ownerId);
|
|
273
|
+
} catch (releaseError) {
|
|
274
|
+
releaseFailures.push(releaseError);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (releaseFailures.length > 0) {
|
|
278
|
+
throw new AggregateError(
|
|
279
|
+
[error, ...releaseFailures],
|
|
280
|
+
'Media reservation and rollback failed',
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private async releaseMedia(
|
|
288
|
+
contentIds: readonly string[],
|
|
289
|
+
ownerId: string,
|
|
290
|
+
): Promise<void> {
|
|
291
|
+
if (this.mediaStore === undefined) return;
|
|
292
|
+
const failures: unknown[] = [];
|
|
293
|
+
for (const contentId of contentIds) {
|
|
294
|
+
try {
|
|
295
|
+
await this.mediaStore.release(contentId, ownerId);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
failures.push(error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (failures.length > 0) {
|
|
301
|
+
throw new AggregateError(failures, 'Failed to release persisted media');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
117
305
|
/**
|
|
118
306
|
* Get the directory containing persisted sessions
|
|
119
307
|
*/
|
|
@@ -136,52 +324,497 @@ export class SessionPersistenceService {
|
|
|
136
324
|
metadata?: PersistedSession['metadata'],
|
|
137
325
|
uiHistory?: PersistedUIHistoryItem[],
|
|
138
326
|
): Promise<void> {
|
|
327
|
+
this.preflightQueueRequest(history, metadata, uiHistory);
|
|
328
|
+
const generation = ++this.nextGeneration;
|
|
329
|
+
const historySnapshot = structuredClone(history);
|
|
330
|
+
const metadataSnapshot = structuredClone(metadata);
|
|
331
|
+
const uiHistorySnapshot = structuredClone(uiHistory);
|
|
332
|
+
const updatedAt = new Date().toISOString();
|
|
333
|
+
const queuedSession = this.buildSession(
|
|
334
|
+
generation,
|
|
335
|
+
historySnapshot,
|
|
336
|
+
metadataSnapshot,
|
|
337
|
+
uiHistorySnapshot,
|
|
338
|
+
updatedAt,
|
|
339
|
+
);
|
|
340
|
+
const accountedBytes = Buffer.byteLength(
|
|
341
|
+
JSON.stringify(queuedSession, null, 2),
|
|
342
|
+
'utf8',
|
|
343
|
+
);
|
|
344
|
+
this.enforcePendingByteIncrease(accountedBytes);
|
|
345
|
+
this.pendingBytes += accountedBytes;
|
|
346
|
+
return new Promise<void>((resolve, reject) => {
|
|
347
|
+
this.saveQueue.push({
|
|
348
|
+
generation,
|
|
349
|
+
history: historySnapshot,
|
|
350
|
+
metadata: metadataSnapshot,
|
|
351
|
+
uiHistory: uiHistorySnapshot,
|
|
352
|
+
updatedAt,
|
|
353
|
+
accountedBytes,
|
|
354
|
+
resolve,
|
|
355
|
+
reject,
|
|
356
|
+
});
|
|
357
|
+
this.startNextSave();
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private startNextSave(): void {
|
|
362
|
+
if (this.saveInProgress || this.transactionActive) return;
|
|
363
|
+
const pending = this.saveQueue.shift();
|
|
364
|
+
if (pending === undefined) return;
|
|
365
|
+
this.saveInProgress = true;
|
|
366
|
+
void this.executeSave(pending).then(
|
|
367
|
+
() => this.completeSave(pending, { ok: true }),
|
|
368
|
+
(error: unknown) => this.completeSave(pending, { ok: false, error }),
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private completeSave(
|
|
373
|
+
pending: PendingPersistenceSave,
|
|
374
|
+
outcome: PersistenceSaveOutcome,
|
|
375
|
+
): void {
|
|
376
|
+
this.pendingBytes -= pending.accountedBytes;
|
|
377
|
+
this.saveInProgress = false;
|
|
378
|
+
if (outcome.ok) pending.resolve();
|
|
379
|
+
else pending.reject(outcome.error);
|
|
380
|
+
this.startNextSave();
|
|
381
|
+
this.notifyIdleWaiters();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private async restorePersistenceTarget(
|
|
385
|
+
previousTargetBytes: Buffer | null,
|
|
386
|
+
): Promise<void> {
|
|
387
|
+
if (previousTargetBytes === null) {
|
|
388
|
+
await fs.promises.rm(this.sessionFilePath, { force: true });
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const restorePath = `${this.sessionFilePath}.restore.${crypto.randomUUID()}.tmp`;
|
|
139
393
|
try {
|
|
140
|
-
|
|
141
|
-
await fs.promises.
|
|
394
|
+
await fs.promises.writeFile(restorePath, previousTargetBytes);
|
|
395
|
+
await fs.promises.rename(restorePath, this.sessionFilePath);
|
|
396
|
+
} catch (error: unknown) {
|
|
397
|
+
try {
|
|
398
|
+
await fs.promises.rm(restorePath, { force: true });
|
|
399
|
+
} catch (cleanupError: unknown) {
|
|
400
|
+
throw new AggregateError(
|
|
401
|
+
[error, cleanupError],
|
|
402
|
+
'Session persistence restoration and temporary-file cleanup failed',
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private acquireTransaction(): Promise<void> {
|
|
410
|
+
if (
|
|
411
|
+
!this.saveInProgress &&
|
|
412
|
+
!this.transactionActive &&
|
|
413
|
+
this.saveQueue.length === 0
|
|
414
|
+
) {
|
|
415
|
+
this.transactionActive = true;
|
|
416
|
+
return Promise.resolve();
|
|
417
|
+
}
|
|
418
|
+
return new Promise<void>((resolve) => {
|
|
419
|
+
this.transactionWaiters.push(resolve);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private notifyIdleWaiters(): void {
|
|
424
|
+
if (
|
|
425
|
+
this.saveInProgress ||
|
|
426
|
+
this.transactionActive ||
|
|
427
|
+
this.saveQueue.length > 0
|
|
428
|
+
) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const waiter = this.transactionWaiters.shift();
|
|
432
|
+
if (waiter !== undefined) {
|
|
433
|
+
this.transactionActive = true;
|
|
434
|
+
waiter();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
142
437
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
438
|
+
async prepareSave(
|
|
439
|
+
history: readonly IContent[],
|
|
440
|
+
): Promise<PreparedPersistenceSave> {
|
|
441
|
+
this.preflightQueueRequest(history, undefined, undefined);
|
|
442
|
+
await this.acquireTransaction();
|
|
443
|
+
let state: PreparedSaveState | undefined;
|
|
444
|
+
try {
|
|
445
|
+
const generation = ++this.nextGeneration;
|
|
446
|
+
const historySnapshot = structuredClone([...history]);
|
|
447
|
+
const updatedAt = new Date().toISOString();
|
|
448
|
+
const accountedBytes = Buffer.byteLength(
|
|
449
|
+
JSON.stringify(
|
|
450
|
+
this.buildSession(
|
|
451
|
+
generation,
|
|
452
|
+
historySnapshot,
|
|
453
|
+
undefined,
|
|
454
|
+
undefined,
|
|
455
|
+
updatedAt,
|
|
456
|
+
),
|
|
457
|
+
null,
|
|
458
|
+
2,
|
|
459
|
+
),
|
|
460
|
+
'utf8',
|
|
461
|
+
);
|
|
462
|
+
this.enforcePendingByteIncrease(accountedBytes);
|
|
463
|
+
state = {
|
|
464
|
+
accountedBytes,
|
|
465
|
+
admittedHistory: undefined,
|
|
466
|
+
reservedContentIds: [],
|
|
467
|
+
previousContents: undefined,
|
|
468
|
+
ownershipPending: false,
|
|
469
|
+
published: false,
|
|
470
|
+
settled: false,
|
|
471
|
+
admissionContext: {
|
|
472
|
+
turnId: `persistence-generation-${generation}`,
|
|
473
|
+
source: 'session-persistence-save',
|
|
474
|
+
reservationOwnerScope: `persistence-admission:${this.sessionId}:${generation}:${crypto.randomUUID()}`,
|
|
475
|
+
},
|
|
476
|
+
ownerId: `persistence:${this.sessionId}:${generation}:${crypto.randomUUID()}`,
|
|
477
|
+
tempPath: `${this.sessionFilePath}.${generation}.${crypto.randomUUID()}.tmp`,
|
|
152
478
|
};
|
|
479
|
+
this.pendingBytes += accountedBytes;
|
|
480
|
+
await this.stagePreparedSave(
|
|
481
|
+
state,
|
|
482
|
+
historySnapshot,
|
|
483
|
+
generation,
|
|
484
|
+
updatedAt,
|
|
485
|
+
);
|
|
486
|
+
return this.preparedSaveLifecycle(state);
|
|
487
|
+
} catch (error: unknown) {
|
|
488
|
+
this.finishPreparedSave(state);
|
|
489
|
+
throw error;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
153
492
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
JSON.stringify(session, null, 2),
|
|
159
|
-
'utf-8',
|
|
493
|
+
private enforcePendingByteIncrease(bytes: number): void {
|
|
494
|
+
if (bytes > this.maxQueueBytes - this.pendingBytes) {
|
|
495
|
+
throw new Error(
|
|
496
|
+
`Session persistence queue byte limit exceeded: ${this.pendingBytes} + ${bytes} > ${this.maxQueueBytes}`,
|
|
160
497
|
);
|
|
161
|
-
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private async stagePreparedSave(
|
|
502
|
+
state: PreparedSaveState,
|
|
503
|
+
history: IContent[],
|
|
504
|
+
generation: number,
|
|
505
|
+
updatedAt: string,
|
|
506
|
+
): Promise<void> {
|
|
507
|
+
try {
|
|
508
|
+
state.admittedHistory =
|
|
509
|
+
this.mediaStore === undefined
|
|
510
|
+
? history
|
|
511
|
+
: await new MediaAdmissionService(this.mediaStore).admitContents(
|
|
512
|
+
history,
|
|
513
|
+
state.admissionContext,
|
|
514
|
+
);
|
|
515
|
+
state.ownershipPending = true;
|
|
516
|
+
await verifyHistoryMedia(
|
|
517
|
+
state.admittedHistory,
|
|
518
|
+
this.mediaStore,
|
|
519
|
+
'session-persistence-save',
|
|
520
|
+
);
|
|
521
|
+
const serialized = JSON.stringify(
|
|
522
|
+
this.buildSession(
|
|
523
|
+
generation,
|
|
524
|
+
[...state.admittedHistory],
|
|
525
|
+
undefined,
|
|
526
|
+
undefined,
|
|
527
|
+
updatedAt,
|
|
528
|
+
),
|
|
529
|
+
null,
|
|
530
|
+
2,
|
|
531
|
+
);
|
|
532
|
+
const serializedBytes = Buffer.byteLength(serialized, 'utf8');
|
|
533
|
+
const accountingDelta = serializedBytes - state.accountedBytes;
|
|
534
|
+
this.enforcePendingByteIncrease(accountingDelta);
|
|
535
|
+
state.accountedBytes = serializedBytes;
|
|
536
|
+
this.pendingBytes += accountingDelta;
|
|
537
|
+
state.reservedContentIds = await this.reserveMedia(
|
|
538
|
+
state.admittedHistory,
|
|
539
|
+
state.ownerId,
|
|
540
|
+
);
|
|
541
|
+
state.previousContents = await this.readPersistenceTarget();
|
|
542
|
+
await fs.promises.mkdir(this.chatsDir, { recursive: true });
|
|
543
|
+
await fs.promises.writeFile(state.tempPath, serialized, 'utf-8');
|
|
544
|
+
} catch (error: unknown) {
|
|
545
|
+
const failures: unknown[] = [error];
|
|
546
|
+
await this.collectCleanupFailure(failures, () =>
|
|
547
|
+
fs.promises.rm(state.tempPath, { force: true }),
|
|
548
|
+
);
|
|
549
|
+
if (state.ownershipPending) {
|
|
550
|
+
const released = await this.collectCleanupFailure(failures, () =>
|
|
551
|
+
this.releasePreparedOwnership(state),
|
|
552
|
+
);
|
|
553
|
+
if (released) state.ownershipPending = false;
|
|
554
|
+
}
|
|
555
|
+
if (failures.length > 1) {
|
|
556
|
+
throw new AggregateError(
|
|
557
|
+
failures,
|
|
558
|
+
'Session persistence staging failed',
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private async readPersistenceTarget(): Promise<Buffer | undefined> {
|
|
566
|
+
try {
|
|
567
|
+
return await fs.promises.readFile(this.sessionFilePath);
|
|
568
|
+
} catch (error: unknown) {
|
|
569
|
+
if (errorCode(error) === 'ENOENT') return undefined;
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
private preparedSaveLifecycle(
|
|
575
|
+
state: PreparedSaveState,
|
|
576
|
+
): PreparedPersistenceSave {
|
|
577
|
+
return {
|
|
578
|
+
publish: () => this.publishPreparedSave(state),
|
|
579
|
+
rollback: () => this.rollbackPreparedSave(state),
|
|
580
|
+
finalize: async () => this.finalizePreparedSave(state),
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private async publishPreparedSave(state: PreparedSaveState): Promise<void> {
|
|
585
|
+
if (state.settled || state.published) {
|
|
586
|
+
throw new Error('Persistence save was already published or settled');
|
|
587
|
+
}
|
|
588
|
+
await fs.promises.rename(state.tempPath, this.sessionFilePath);
|
|
589
|
+
state.published = true;
|
|
590
|
+
await this.releasePreparedOwnership(state);
|
|
591
|
+
state.ownershipPending = false;
|
|
592
|
+
state.reservedContentIds = [];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private async rollbackPreparedSave(state: PreparedSaveState): Promise<void> {
|
|
596
|
+
if (state.settled) return;
|
|
597
|
+
const failures: unknown[] = [];
|
|
598
|
+
await this.collectCleanupFailure(failures, () =>
|
|
599
|
+
state.published
|
|
600
|
+
? this.restorePersistenceTarget(state.previousContents ?? null)
|
|
601
|
+
: fs.promises.rm(state.tempPath, { force: true }),
|
|
602
|
+
);
|
|
603
|
+
if (state.ownershipPending) {
|
|
604
|
+
const released = await this.collectCleanupFailure(failures, () =>
|
|
605
|
+
this.releasePreparedOwnership(state),
|
|
606
|
+
);
|
|
607
|
+
if (released) {
|
|
608
|
+
state.ownershipPending = false;
|
|
609
|
+
state.reservedContentIds = [];
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
this.finishPreparedSave(state);
|
|
613
|
+
if (failures.length === 1) throw failures[0];
|
|
614
|
+
if (failures.length > 1) {
|
|
615
|
+
throw new AggregateError(failures, 'Session persistence rollback failed');
|
|
616
|
+
}
|
|
617
|
+
}
|
|
162
618
|
|
|
619
|
+
private async releasePreparedOwnership(
|
|
620
|
+
state: PreparedSaveState,
|
|
621
|
+
): Promise<void> {
|
|
622
|
+
await this.releaseSaveOwnership(
|
|
623
|
+
state.admittedHistory,
|
|
624
|
+
state.admissionContext,
|
|
625
|
+
state.reservedContentIds,
|
|
626
|
+
state.ownerId,
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
private finalizePreparedSave(state: PreparedSaveState): void {
|
|
631
|
+
if (!state.published) {
|
|
632
|
+
throw new Error('Cannot finalize an unpublished persistence save');
|
|
633
|
+
}
|
|
634
|
+
this.finishPreparedSave(state);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
private finishPreparedSave(state: PreparedSaveState | undefined): void {
|
|
638
|
+
if (state?.settled === true) return;
|
|
639
|
+
if (state !== undefined) {
|
|
640
|
+
state.settled = true;
|
|
641
|
+
this.pendingBytes -= state.accountedBytes;
|
|
642
|
+
}
|
|
643
|
+
this.transactionActive = false;
|
|
644
|
+
this.startNextSave();
|
|
645
|
+
this.notifyIdleWaiters();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private async collectCleanupFailure(
|
|
649
|
+
failures: unknown[],
|
|
650
|
+
cleanup: () => Promise<void>,
|
|
651
|
+
): Promise<boolean> {
|
|
652
|
+
try {
|
|
653
|
+
await cleanup();
|
|
654
|
+
return true;
|
|
655
|
+
} catch (error: unknown) {
|
|
656
|
+
failures.push(error);
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
private async executeSave(pending: PendingPersistenceSave): Promise<void> {
|
|
662
|
+
const admissionContext: MediaAdmissionContext = {
|
|
663
|
+
turnId: `persistence-generation-${pending.generation}`,
|
|
664
|
+
source: 'session-persistence-save',
|
|
665
|
+
reservationOwnerScope: `persistence-admission:${this.sessionId}:${pending.generation}:${crypto.randomUUID()}`,
|
|
666
|
+
};
|
|
667
|
+
const ownerId = `persistence:${this.sessionId}:${pending.generation}:${crypto.randomUUID()}`;
|
|
668
|
+
const tempPath = `${this.sessionFilePath}.${pending.generation}.${crypto.randomUUID()}.tmp`;
|
|
669
|
+
let admittedHistory: IContent[] | undefined;
|
|
670
|
+
let reservedContentIds: readonly string[] = [];
|
|
671
|
+
const failures: unknown[] = [];
|
|
672
|
+
try {
|
|
673
|
+
admittedHistory =
|
|
674
|
+
this.mediaStore === undefined
|
|
675
|
+
? pending.history
|
|
676
|
+
: await new MediaAdmissionService(this.mediaStore).admitContents(
|
|
677
|
+
pending.history,
|
|
678
|
+
admissionContext,
|
|
679
|
+
);
|
|
680
|
+
await verifyHistoryMedia(
|
|
681
|
+
admittedHistory,
|
|
682
|
+
this.mediaStore,
|
|
683
|
+
'session-persistence-save',
|
|
684
|
+
);
|
|
685
|
+
const session = this.buildSession(
|
|
686
|
+
pending.generation,
|
|
687
|
+
admittedHistory,
|
|
688
|
+
pending.metadata,
|
|
689
|
+
pending.uiHistory,
|
|
690
|
+
pending.updatedAt,
|
|
691
|
+
);
|
|
692
|
+
const serialized = JSON.stringify(session, null, 2);
|
|
693
|
+
const serializedBytes = Buffer.byteLength(serialized, 'utf8');
|
|
694
|
+
const accountingDelta = serializedBytes - pending.accountedBytes;
|
|
695
|
+
this.enforcePendingByteIncrease(accountingDelta);
|
|
696
|
+
pending.accountedBytes = serializedBytes;
|
|
697
|
+
this.pendingBytes += accountingDelta;
|
|
698
|
+
reservedContentIds = await this.reserveMedia(admittedHistory, ownerId);
|
|
699
|
+
await fs.promises.mkdir(this.chatsDir, { recursive: true });
|
|
700
|
+
await fs.promises.writeFile(tempPath, serialized, 'utf-8');
|
|
701
|
+
await fs.promises.rename(tempPath, this.sessionFilePath);
|
|
163
702
|
logger.debug('Session saved:', {
|
|
164
703
|
path: this.sessionFilePath,
|
|
165
|
-
historyLength:
|
|
166
|
-
|
|
704
|
+
historyLength: admittedHistory.length,
|
|
705
|
+
generation: pending.generation,
|
|
706
|
+
metadata: pending.metadata,
|
|
167
707
|
});
|
|
168
|
-
} catch (error) {
|
|
708
|
+
} catch (error: unknown) {
|
|
169
709
|
logger.error('Failed to save session:', error);
|
|
710
|
+
failures.push(error);
|
|
711
|
+
try {
|
|
712
|
+
await fs.promises.rm(tempPath, { force: true });
|
|
713
|
+
} catch (cleanupError: unknown) {
|
|
714
|
+
failures.push(cleanupError);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (admittedHistory !== undefined) {
|
|
718
|
+
try {
|
|
719
|
+
await this.releaseSaveOwnership(
|
|
720
|
+
admittedHistory,
|
|
721
|
+
admissionContext,
|
|
722
|
+
reservedContentIds,
|
|
723
|
+
ownerId,
|
|
724
|
+
);
|
|
725
|
+
} catch (releaseError: unknown) {
|
|
726
|
+
failures.push(releaseError);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (failures.length === 1) throw failures[0];
|
|
730
|
+
if (failures.length > 1) {
|
|
731
|
+
throw new AggregateError(failures, 'Session save failed');
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private buildSession(
|
|
736
|
+
generation: number,
|
|
737
|
+
history: IContent[],
|
|
738
|
+
metadata: PersistedSession['metadata'] | undefined,
|
|
739
|
+
uiHistory: PersistedUIHistoryItem[] | undefined,
|
|
740
|
+
updatedAt: string,
|
|
741
|
+
): PersistedSession {
|
|
742
|
+
return {
|
|
743
|
+
version: 1,
|
|
744
|
+
generation,
|
|
745
|
+
sessionId: this.sessionId,
|
|
746
|
+
projectHash: this.getProjectHash(),
|
|
747
|
+
createdAt: this.getCreatedAt(),
|
|
748
|
+
updatedAt,
|
|
749
|
+
history,
|
|
750
|
+
...(uiHistory === undefined ? {} : { uiHistory }),
|
|
751
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
getPendingByteCount(): number {
|
|
756
|
+
return this.pendingBytes;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
private async loadMediaOwnership(
|
|
760
|
+
session: PersistedSession,
|
|
761
|
+
): Promise<LoadedPersistedSession> {
|
|
762
|
+
const admissionContext: MediaAdmissionContext = {
|
|
763
|
+
turnId: 'session-persistence-load',
|
|
764
|
+
source: 'session-persistence-load',
|
|
765
|
+
reservationOwnerScope: `persistence-load:${this.sessionId}:${crypto.randomUUID()}`,
|
|
766
|
+
};
|
|
767
|
+
let admittedHistory: IContent[] | undefined;
|
|
768
|
+
try {
|
|
769
|
+
admittedHistory =
|
|
770
|
+
this.mediaStore === undefined
|
|
771
|
+
? session.history
|
|
772
|
+
: await new MediaAdmissionService(this.mediaStore).admitContents(
|
|
773
|
+
session.history,
|
|
774
|
+
admissionContext,
|
|
775
|
+
);
|
|
776
|
+
await verifyHistoryMedia(
|
|
777
|
+
admittedHistory,
|
|
778
|
+
this.mediaStore,
|
|
779
|
+
'session-persistence-load',
|
|
780
|
+
);
|
|
781
|
+
} catch (error: unknown) {
|
|
782
|
+
if (admittedHistory === undefined) throw error;
|
|
783
|
+
try {
|
|
784
|
+
await this.releaseAdmission(admittedHistory, admissionContext);
|
|
785
|
+
} catch (cleanupError: unknown) {
|
|
786
|
+
throw new AggregateError(
|
|
787
|
+
[error, cleanupError],
|
|
788
|
+
'Session load and media ownership release failed',
|
|
789
|
+
);
|
|
790
|
+
}
|
|
170
791
|
throw error;
|
|
171
792
|
}
|
|
793
|
+
let released = false;
|
|
794
|
+
return {
|
|
795
|
+
...session,
|
|
796
|
+
history: admittedHistory,
|
|
797
|
+
mediaOwnership: {
|
|
798
|
+
release: async (): Promise<void> => {
|
|
799
|
+
if (released) return;
|
|
800
|
+
await this.releaseAdmission(admittedHistory, admissionContext);
|
|
801
|
+
released = true;
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
};
|
|
172
805
|
}
|
|
173
806
|
|
|
174
807
|
/**
|
|
175
808
|
* Load the most recent session for this project
|
|
176
809
|
*/
|
|
177
|
-
async loadMostRecent(): Promise<
|
|
810
|
+
async loadMostRecent(): Promise<LoadedPersistedSession | null> {
|
|
178
811
|
try {
|
|
179
812
|
// Find all persisted session files (readdir throws ENOENT if dir doesn't exist)
|
|
180
813
|
let files: string[];
|
|
181
814
|
try {
|
|
182
815
|
files = await fs.promises.readdir(this.chatsDir);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
if ((err
|
|
816
|
+
} catch (err: unknown) {
|
|
817
|
+
if (errorCode(err) === 'ENOENT') {
|
|
185
818
|
logger.debug('No chats directory found');
|
|
186
819
|
return null;
|
|
187
820
|
}
|
|
@@ -223,16 +856,20 @@ export class SessionPersistenceService {
|
|
|
223
856
|
return null;
|
|
224
857
|
}
|
|
225
858
|
|
|
859
|
+
const loadedSession = await this.loadMediaOwnership(session);
|
|
226
860
|
logger.debug('Session loaded:', {
|
|
227
|
-
sessionId:
|
|
228
|
-
historyLength:
|
|
229
|
-
createdAt:
|
|
230
|
-
updatedAt:
|
|
861
|
+
sessionId: loadedSession.sessionId,
|
|
862
|
+
historyLength: loadedSession.history.length,
|
|
863
|
+
createdAt: loadedSession.createdAt,
|
|
864
|
+
updatedAt: loadedSession.updatedAt,
|
|
231
865
|
});
|
|
232
866
|
|
|
233
|
-
return
|
|
867
|
+
return loadedSession;
|
|
234
868
|
} catch (error) {
|
|
235
869
|
logger.error('Failed to load session:', error);
|
|
870
|
+
if (containsMediaDiagnostic(error)) {
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
236
873
|
|
|
237
874
|
// If file is corrupted, back it up and return null
|
|
238
875
|
if (error instanceof SyntaxError) {
|