/** * Agent Memory Service * * Manages agent-level persistent memory stored in ~/.crewly/agents/{agentId}/ * Provides storage for role knowledge, preferences, and performance metrics. * * @module services/memory/agent-memory.service */ import { AgentMemory, AgentPreferences, RoleKnowledgeEntry, PerformanceMetrics, type RoleKnowledgeCategory } from '../../types/memory.types.js'; /** * Metrics recorded when a task is completed */ export interface TaskCompletionMetrics { /** Number of iterations to complete the task */ iterations: number; /** Whether quality gates passed on first try */ qualityGatePassed: boolean; /** Time to complete in minutes */ completionTimeMinutes?: number; /** Type of task (e.g., 'bug-fix', 'feature', 'refactor') */ taskType?: string; } /** * Interface for the Agent Memory Service */ export interface IAgentMemoryService { initializeAgent(agentId: string, role: string): Promise; addRoleKnowledge(agentId: string, entry: Omit): Promise; getRoleKnowledge(agentId: string, category?: RoleKnowledgeCategory): Promise; reinforceKnowledge(agentId: string, entryId: string): Promise; updatePreferences(agentId: string, preferences: Partial): Promise; getPreferences(agentId: string): Promise; recordTaskCompletion(agentId: string, metrics: TaskCompletionMetrics): Promise; recordError(agentId: string, errorPattern: string, resolution?: string): Promise; getPerformanceMetrics(agentId: string): Promise; generateAgentContext(agentId: string): Promise; pruneStaleEntries(agentId: string, olderThanDays: number): Promise; getAgentMemory(agentId: string): Promise; /** * v3 (M4): Persist a memory object after the supersession service mutates * entries in place. Narrow wrapper around the canonical write path. */ saveSupersededMemory(agentId: string, memory: AgentMemory): Promise; } /** * Service for managing agent-level persistent memory * * Follows singleton pattern for consistent state management. * Uses atomic file writes to prevent data corruption. * * @example * ```typescript * const memoryService = AgentMemoryService.getInstance(); * await memoryService.initializeAgent('dev-001', 'developer'); * await memoryService.addRoleKnowledge('dev-001', { * category: 'best-practice', * content: 'Always run tests before committing', * confidence: 0.5 * }); * ``` */ export declare class AgentMemoryService implements IAgentMemoryService { private static instance; private static instanceHome; private readonly basePath; private readonly logger; private readonly memoryCache; private readonly CACHE_TTL_MS; /** * Creates a new AgentMemoryService instance * * @param crewlyHome - Optional custom path for crewly home directory */ constructor(crewlyHome?: string); /** * Gets the singleton instance of AgentMemoryService * * @param crewlyHome - Optional custom path for crewly home directory * @returns The singleton AgentMemoryService instance */ static getInstance(crewlyHome?: string): AgentMemoryService; /** * Clears the singleton instance (useful for testing) */ static clearInstance(): void; /** * Ensures the base agents directory exists */ private ensureBaseDirectory; /** * Gets the path to an agent's memory directory * * @param agentId - The agent's unique identifier * @returns Absolute path to the agent's memory directory */ private getAgentPath; /** * Gets the path to a specific memory file for an agent * * @param agentId - The agent's unique identifier * @param fileName - The memory file name * @returns Absolute path to the memory file */ private getFilePath; /** * Ensures an agent's directory structure exists * * @param agentId - The agent's unique identifier */ private ensureAgentDirectory; /** * Invalidates the cache for an agent * * @param agentId - The agent's unique identifier */ private invalidateCache; /** * Gets cached agent memory or loads from disk * * @param agentId - The agent's unique identifier * @returns Agent memory or null if not found */ private getCachedMemory; /** * Loads agent memory from disk * * @param agentId - The agent's unique identifier * @returns Agent memory or null if not initialized */ private loadAgentMemory; /** * Saves agent memory to disk * * @param agentId - The agent's unique identifier * @param memory - Memory data to save */ private saveAgentMemory; /** * v3 (M4): Public narrow wrapper around {@link saveAgentMemory} for the * supersession service. The supersession service mutates entries in * place (sets `superseded`/`supersededBy`/extends `evidence`) and needs * to persist via the canonical write path so cache invalidation and * atomic-file semantics are honored. * * Do NOT broaden this surface — full memory replacement should still go * through reinforceKnowledge / addRoleKnowledge / etc. which guard against * concurrent-write surprises. * * @param agentId - Agent whose memory store to persist * @param memory - Full memory object (already migrated + mutated by caller) */ saveSupersededMemory(agentId: string, memory: AgentMemory): Promise; /** * Finds the existing entry an incoming one duplicates: exact or * containment match first, else the nearest token-set neighbour in the * same category and tag scope at or above the near-duplicate threshold * (#914 P1). Superseded and expired entries are never candidates. * * @param knowledge - Existing knowledge entries * @param entry - Entry about to be written * @returns The match with its score, or null */ private findSimilarEntry; /** * Formats agent preferences for context output * * @param preferences - Agent preferences to format * @returns Formatted string */ private formatPreferences; /** * Initializes memory storage for a new agent * * @param agentId - Unique identifier for the agent * @param role - Agent's role (e.g., 'developer', 'qa', 'pm') * * @example * ```typescript * await memoryService.initializeAgent('frontend-dev-001', 'frontend-developer'); * ``` */ initializeAgent(agentId: string, role: string): Promise; /** * Marks role-knowledge entries that are task-completion summaries as * superseded, so default recall no longer returns them (#833). * * Older skills stored "[COMPLETED] Task completed by …" summaries here * (coerced from category=decision) and report-status mirrored * "Task completed: …" learnings into role knowledge. The entries stay in * memory.json for audit — superseded entries are only hidden — and the * summaries remain on their WorkItems. Idempotent. * * @param agentId - Agent's unique identifier * @returns Number of entries newly hidden * * @example * ```typescript * const hidden = await agentMemory.hideTaskCompletionLogs('dev-001'); * ``` */ hideTaskCompletionLogs(agentId: string): Promise; /** * Adds a new role knowledge entry * * The same text again merges into and reinforces the existing entry; the * same fact restated supersedes the old entry with the new wording (see * knowledge-similarity, #914 P1). * * @param agentId - Agent's unique identifier * @param entry - Knowledge entry (without id and createdAt) * @returns ID of the created or reinforced entry * * @example * ```typescript * const entryId = await memoryService.addRoleKnowledge('dev-001', { * category: 'best-practice', * content: 'Always validate user input before processing', * confidence: 0.6, * learnedFrom: 'TICKET-456' * }); * ``` */ addRoleKnowledge(agentId: string, entry: Omit): Promise; /** * Gets role knowledge entries, optionally filtered by category * * @param agentId - Agent's unique identifier * @param category - Optional category filter * @returns Array of knowledge entries sorted by confidence (descending) */ getRoleKnowledge(agentId: string, category?: RoleKnowledgeCategory): Promise; /** * Reinforces a knowledge entry by increasing its confidence * * @param agentId - Agent's unique identifier * @param entryId - ID of the entry to reinforce */ reinforceKnowledge(agentId: string, entryId: string): Promise; /** * Updates agent preferences (partial update supported) * * @param agentId - Agent's unique identifier * @param preferences - Partial preferences to update */ updatePreferences(agentId: string, preferences: Partial): Promise; /** * Gets the current agent preferences * * @param agentId - Agent's unique identifier * @returns Agent preferences or defaults */ getPreferences(agentId: string): Promise; /** * Records a task completion and updates performance metrics * * @param agentId - Agent's unique identifier * @param metrics - Task completion metrics */ recordTaskCompletion(agentId: string, metrics: TaskCompletionMetrics): Promise; /** * Records an error pattern encountered by the agent * * @param agentId - Agent's unique identifier * @param errorPattern - Description of the error pattern * @param resolution - Optional known resolution */ recordError(agentId: string, errorPattern: string, resolution?: string): Promise; /** * Gets the current performance metrics for an agent * * @param agentId - Agent's unique identifier * @returns Performance metrics */ getPerformanceMetrics(agentId: string): Promise; /** * Generates a context string for prompt injection * * Compiles relevant agent memories, preferences, and performance data * into a formatted string suitable for inclusion in agent prompts. * * @param agentId - Agent's unique identifier * @returns Formatted context string */ generateAgentContext(agentId: string): Promise; /** * Generate context for TL/orchestrator scheduling decisions. * * Returns performance memories for a specific agent — these are memories * about what an agent is good/bad at, intended for task assignment decisions. * Performance memories are NOT injected into the agent's own prompt. * * @param agentId - Agent whose performance memories to retrieve * @returns Formatted performance memory context for scheduling use */ generateSchedulingContext(agentId: string): Promise; /** * Prunes stale entries older than specified days * * Removes low-confidence entries that haven't been used recently. * * @param agentId - Agent's unique identifier * @param olderThanDays - Number of days threshold * @returns Number of entries removed */ pruneStaleEntries(agentId: string, olderThanDays: number): Promise; /** * Gets the complete agent memory object * * @param agentId - Agent's unique identifier * @returns Full agent memory or null if not initialized */ getAgentMemory(agentId: string): Promise; /** * Calculate effective score for a knowledge entry. * Combines confidence, recency, and verification status. * * effectiveScore = confidence × recencyFactor × verificationFactor * * @param entry - The knowledge entry to score * @returns Effective score (0-1) */ calculateEffectiveScore(entry: RoleKnowledgeEntry): number; /** * Run decay sweep: halve confidence of entries unused for 90+ days. * Remove entries unused for 180+ days with very low confidence. * Skip entries with lastVerifiedAt in last 30 days (recently validated). * * @param agentId - Agent's unique identifier * @returns Count of decayed and pruned entries */ runDecaySweep(agentId: string): Promise<{ decayed: number; pruned: number; }>; } //# sourceMappingURL=agent-memory.service.d.ts.map