From 887d9c9c1b5c9457f2990d97b573b93a6cea7a24 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 18 Oct 2025 19:32:39 +0200 Subject: [PATCH] chore: allow two storage versions Signed-off-by: Timo Glastra --- packages/core/src/agent/Agent.ts | 22 ++++++++++++------- .../storage/migration/StorageUpdateService.ts | 10 ++++++++- .../src/storage/migration/UpdateAssistant.ts | 11 +++++++++- .../core/src/storage/migration/updates.ts | 5 +++++ .../src/context/TenantAgentContextProvider.ts | 16 ++++++++++---- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/core/src/agent/Agent.ts b/packages/core/src/agent/Agent.ts index d580c8c3de..faa647c7e4 100644 --- a/packages/core/src/agent/Agent.ts +++ b/packages/core/src/agent/Agent.ts @@ -3,7 +3,7 @@ import { InjectionSymbols } from '../constants' import { JwsService } from '../crypto/JwsService' import { CredoError } from '../error' import { DependencyManager } from '../plugins' -import { StorageUpdateService, StorageVersionRepository, UpdateAssistant } from '../storage' +import { isStorageUpToDate, StorageUpdateService, StorageVersionRepository, UpdateAssistant } from '../storage' import type { InitConfig } from '../types' import { AgentConfig } from './AgentConfig' import type { AgentDependencies } from './AgentDependencies' @@ -87,23 +87,29 @@ export class Agent extends BaseAge // Make sure the storage is up to date const storageUpdateService = this.dependencyManager.resolve(StorageUpdateService) - const isStorageUpToDate = await storageUpdateService.isUpToDate(this.agentContext) - this.logger.info(`Agent storage is ${isStorageUpToDate ? '' : 'not '}up to date.`) + const currentStorageVersion = await storageUpdateService.getCurrentStorageVersion(this.agentContext) + const mustUpdate = !isStorageUpToDate(currentStorageVersion, StorageUpdateService.previousFrameworkStorageVersion) + const canUpdate = !isStorageUpToDate(currentStorageVersion, StorageUpdateService.frameworkStorageVersion) + if (canUpdate) { + this.logger.info( + `Agent storage is not up to date. Current storage version is ${currentStorageVersion}, latest storage version is ${StorageUpdateService.frameworkStorageVersion}` + ) + } else { + this.logger.info(`Agent storage is up to date. `) + } - if (!isStorageUpToDate && this.agentConfig.autoUpdateStorageOnStartup) { + if (canUpdate && this.agentConfig.autoUpdateStorageOnStartup) { const updateAssistant = new UpdateAssistant(this) await updateAssistant.initialize() await updateAssistant.update() - } else if (!isStorageUpToDate) { - const currentVersion = await storageUpdateService.getCurrentStorageVersion(this.agentContext) - + } else if (mustUpdate) { // Close agent context to prevent un-initialized agent with initialized agent context await this.dependencyManager.closeAgentContext(this.agentContext) throw new CredoError( // TODO: add link to where documentation on how to update can be found. - `Current agent storage is not up to date. To prevent the framework state from getting corrupted the agent initialization is aborted. Make sure to update the agent storage (currently at ${currentVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). You can also downgrade your version of Credo.` + `Current agent storage is not up to date. To prevent the framework state from getting corrupted the agent initialization is aborted. Make sure to update the agent storage (currently at ${currentStorageVersion}) to the latest or previous version (${UpdateAssistant.frameworkStorageVersion} or ${UpdateAssistant.previousFrameworkStorageVersion}). You can also downgrade your version of Credo.` ) } diff --git a/packages/core/src/storage/migration/StorageUpdateService.ts b/packages/core/src/storage/migration/StorageUpdateService.ts index 586be4868d..16ee45e0df 100644 --- a/packages/core/src/storage/migration/StorageUpdateService.ts +++ b/packages/core/src/storage/migration/StorageUpdateService.ts @@ -7,7 +7,11 @@ import { isStorageUpToDate } from './isUpToDate' import { StorageVersionRecord } from './repository/StorageVersionRecord' import { StorageVersionRepository } from './repository/StorageVersionRepository' import type { UpdateToVersion } from './updates' -import { CURRENT_FRAMEWORK_STORAGE_VERSION, INITIAL_STORAGE_VERSION } from './updates' +import { + CURRENT_FRAMEWORK_STORAGE_VERSION, + INITIAL_STORAGE_VERSION, + PREVIOUS_FRAMEWORK_STORAGE_VERSION, +} from './updates' @injectable() export class StorageUpdateService { @@ -46,6 +50,10 @@ export class StorageUpdateService { return CURRENT_FRAMEWORK_STORAGE_VERSION } + public static get previousFrameworkStorageVersion() { + return PREVIOUS_FRAMEWORK_STORAGE_VERSION + } + public async setCurrentStorageVersion(agentContext: AgentContext, storageVersion: VersionString) { this.logger.debug(`Setting current agent storage version to ${storageVersion}`) const storageVersionRecord = await this.storageVersionRepository.findById( diff --git a/packages/core/src/storage/migration/UpdateAssistant.ts b/packages/core/src/storage/migration/UpdateAssistant.ts index 341d1f2f2e..b3a0f16d97 100644 --- a/packages/core/src/storage/migration/UpdateAssistant.ts +++ b/packages/core/src/storage/migration/UpdateAssistant.ts @@ -6,7 +6,12 @@ import { StorageUpdateError } from './error/StorageUpdateError' import { StorageUpdateService } from './StorageUpdateService' import type { Update, UpdateConfig, UpdateToVersion } from './updates' -import { CURRENT_FRAMEWORK_STORAGE_VERSION, DEFAULT_UPDATE_CONFIG, supportedUpdates } from './updates' +import { + CURRENT_FRAMEWORK_STORAGE_VERSION, + DEFAULT_UPDATE_CONFIG, + PREVIOUS_FRAMEWORK_STORAGE_VERSION, + supportedUpdates, +} from './updates' export interface UpdateAssistantUpdateOptions { updateToVersion?: UpdateToVersion @@ -43,6 +48,10 @@ export class UpdateAssistant = BaseAgent> { return CURRENT_FRAMEWORK_STORAGE_VERSION } + public static get previousFrameworkStorageVersion() { + return PREVIOUS_FRAMEWORK_STORAGE_VERSION + } + public async getNeededUpdates(toVersion?: UpdateToVersion) { const currentStorageVersion = parseVersionString( await this.storageUpdateService.getCurrentStorageVersion(this.agent.context) diff --git a/packages/core/src/storage/migration/updates.ts b/packages/core/src/storage/migration/updates.ts index 119467e83b..1fc268f18d 100644 --- a/packages/core/src/storage/migration/updates.ts +++ b/packages/core/src/storage/migration/updates.ts @@ -59,6 +59,11 @@ export const CURRENT_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdat typeof supportedUpdates >['toVersion'] +// Previous version is second-last toVersion from the supported updates +export const PREVIOUS_FRAMEWORK_STORAGE_VERSION = supportedUpdates[supportedUpdates.length - 2].toVersion as LastItem< + typeof supportedUpdates +>['toVersion'] + export const STORAGE_VERSION_RECORD_ID = 'STORAGE_VERSION_RECORD_ID' type LastItem = T extends readonly [...infer _, infer U] ? U : T[0] | undefined diff --git a/packages/tenants/src/context/TenantAgentContextProvider.ts b/packages/tenants/src/context/TenantAgentContextProvider.ts index 72fc834419..e31ec0ad7b 100644 --- a/packages/tenants/src/context/TenantAgentContextProvider.ts +++ b/packages/tenants/src/context/TenantAgentContextProvider.ts @@ -11,6 +11,7 @@ import { JsonEncoder, Kms, type Logger, + StorageUpdateService, TypedArrayEncoder, UpdateAssistant, type UpdateAssistantUpdateOptions, @@ -67,18 +68,25 @@ export class TenantAgentContextProvider implements AgentContextProvider { // TODO: maybe we can look at not having to retrieve the tenant record if there's already a context available. const tenantRecord = await this.tenantRecordService.getTenantById(this.rootAgentContext, tenantId) - const shouldUpdate = !isStorageUpToDate(tenantRecord.storageVersion) + const canUpdate = !isStorageUpToDate(tenantRecord.storageVersion) + const mustUpdate = !isStorageUpToDate( + tenantRecord.storageVersion, + StorageUpdateService.previousFrameworkStorageVersion + ) // If the tenant storage is not up to date, and autoUpdate is disabled we throw an error - if (shouldUpdate && !this.rootAgentContext.config.autoUpdateStorageOnStartup) { + if (mustUpdate && !this.rootAgentContext.config.autoUpdateStorageOnStartup) { throw new CredoError( - `Current agent storage for tenant ${tenantRecord.id} is not up to date. To prevent the tenant state from getting corrupted the tenant initialization is aborted. Make sure to update the tenant storage (currently at ${tenantRecord.storageVersion}) to the latest version (${UpdateAssistant.frameworkStorageVersion}). You can also downgrade your version of Credo.` + `Current agent storage for tenant ${tenantRecord.id} is not up to date. To prevent the tenant state from getting corrupted the tenant initialization is aborted. Make sure to update the tenant storage (currently at ${tenantRecord.storageVersion}) to the latest or previous version (${UpdateAssistant.frameworkStorageVersion} or ${UpdateAssistant.frameworkStorageVersion}). You can also downgrade your version of Credo.` ) } const agentContext = await this.tenantSessionCoordinator.getContextForSession(tenantRecord, { provisionContext, - runInMutex: shouldUpdate ? (agentContext) => this._updateTenantStorage(tenantRecord, agentContext) : undefined, + runInMutex: + canUpdate && this.rootAgentContext.config.autoUpdateStorageOnStartup + ? (agentContext) => this._updateTenantStorage(tenantRecord, agentContext) + : undefined, }) this.logger.debug(`Created tenant agent context for context correlation id '${contextCorrelationId}'`)