@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
143 lines • 5.24 kB
TypeScript
/**
* Activation Store
*
* Persists per-session element activation state to disk.
* Each MCP session (identified by DOLLHOUSE_SESSION_ID) gets its own
* activation file so concurrent sessions maintain independent profiles.
*
* Follows the DangerZoneEnforcer pattern:
* - DI-managed singleton with FileOperationsService
* - initialize() loads from disk (tolerates missing/corrupt files)
* - In-memory state is the hot path; disk writes are fire-and-forget
* - atomicWriteFile (write-to-temp + rename) prevents partial reads
*
* Forward compatibility: The versioned file format (v1) can evolve to
* include userId, orgId, and audit fields for multi-user HTTPS mode
* without breaking existing installations.
*
* @since v2.0.0 - Issue #598
*/
import type { FileOperationsService } from './FileOperationsService.js';
/**
* A persisted activation record for a single element.
*/
export interface PersistedActivation {
/** Element name (human-readable, used for all types) */
name: string;
/** For personas only: the filename key used by PersonaManager */
filename?: string;
/** ISO-8601 timestamp of when activation was persisted */
activatedAt: string;
}
export interface PersistedActivationStateSnapshot {
sessionId: string;
lastUpdated: string;
activations: Record<string, PersistedActivation[]>;
}
/**
* Per-session activation state persistence.
*
* Persists element activation state to `~/.dollhouse/state/activations-{sessionId}.json`.
* Each MCP session is identified by the `DOLLHOUSE_SESSION_ID` environment variable.
*
* Thread-safety note: Node.js is single-threaded, so Map operations are safe.
* For multi-process deployments (future HTTPS), consider Redis or DB backing.
*
* @example
* ```ts
* // Session ID set via environment:
* // DOLLHOUSE_SESSION_ID=claude-code
*
* const store = new ActivationStore(fileOps);
* await store.initialize(); // loads ~/.dollhouse/state/activations-claude-code.json
*
* store.recordActivation('skill', 'code-reviewer');
* store.recordActivation('persona', 'Creative Dev', 'creative-dev.md');
*
* // On next server start, initialize() restores these activations
* ```
*/
export declare class ActivationStore {
private readonly fileOps;
private readonly stateDir;
private readonly sessionId;
private readonly runtimeSessionId;
private readonly persistPath;
private readonly enabled;
private state;
constructor(fileOps: FileOperationsService, stateDir?: string);
/**
* Load persisted activations from disk.
* Call once after construction to restore state from a previous session.
* If the file is missing or corrupt, starts with empty activations.
*/
initialize(): Promise<void>;
/**
* Record an element activation. Fire-and-forget persist.
*/
recordActivation(elementType: string, name: string, filename?: string): void;
/**
* Record an element deactivation. Fire-and-forget persist.
*/
recordDeactivation(elementType: string, name: string): void;
/**
* Remove a specific activation by name (used during restore to prune stale entries).
*/
removeStaleActivation(elementType: string, name: string): void;
/**
* Get all persisted activations for a given element type.
*/
getActivations(elementType: string): PersistedActivation[];
/**
* Read persisted activation snapshots from disk for reporting/diagnostics.
*
* This intentionally does not mutate the store's in-memory state, and it is
* safe to call from the web console to inspect other sessions' persisted
* activations without changing live policy enforcement for the current
* process.
*/
listPersistedActivationStates(sessionId?: string): Promise<PersistedActivationStateSnapshot[]>;
private getPersistedActivationFilenames;
private readPersistedActivationState;
private isPersistedActivationState;
private normalizePersistedActivations;
private normalizePersistedActivation;
private logSnapshotReadError;
/**
* Get the session ID this store is scoped to.
*/
getSessionId(): string;
/**
* Runtime-unique session identifier used for live console/session registry
* surfaces when the stable persistence identity is shared across unnamed
* sessions.
*/
getRuntimeSessionId(): string;
/**
* Check if persistence is enabled.
*/
isEnabled(): boolean;
/**
* Clear all persisted activations. Used for testing or admin reset.
*/
clearAll(): void;
/**
* Fire-and-forget persistence with retry for transient disk failures.
* Retries up to PERSIST_MAX_RETRIES times with a short delay.
* Disk failure does not block activation operations.
*/
private persistAsync;
/**
* Attempt to persist with retries for transient failures (e.g., EBUSY, EAGAIN).
*/
private persistWithRetry;
/**
* Write current activation state to disk.
*/
private persist;
private createEmptyState;
private getTotalActivationCount;
private getActivationCounts;
private logResolvedSessionIdentity;
}
//# sourceMappingURL=ActivationStore.d.ts.map