Download src/tools/AgentTool/agentMemory.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 5.85 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/tools/AgentTool/agentMemory.ts
- Command line
-
hf download hf://chenbhao/codev/src/tools/AgentTool/agentMemory.ts
-
curl -L -o agentMemory.ts https://huggingface.co/chenbhao/codev/resolve/main/src/tools/AgentTool/agentMemory.ts
5.85 kB
| import { join, normalize, sep } from 'path' | |
| import { getProjectRoot } from '../../bootstrap/state.js' | |
| import { | |
| buildMemoryPrompt, | |
| ensureMemoryDirExists, | |
| } from '../../memdir/memdir.js' | |
| import { getMemoryBaseDir } from '../../memdir/paths.js' | |
| import { getCwd } from '../../utils/cwd.js' | |
| import { findCanonicalGitRoot } from '../../utils/git.js' | |
| import { sanitizePath } from '../../utils/path.js' | |
| // Persistent agent memory scope: 'user' (~/.claude/agent-memory/), 'project' (.claude/agent-memory/), or 'local' (.claude/agent-memory-local/) | |
| export type AgentMemoryScope = 'user' | 'project' | 'local' | |
| /** | |
| * Sanitize an agent type name for use as a directory name. | |
| * Replaces colons (invalid on Windows, used in plugin-namespaced agent | |
| * types like "my-plugin:my-agent") with dashes. | |
| */ | |
| function sanitizeAgentTypeForPath(agentType: string): string { | |
| return agentType.replace(/:/g, '-') | |
| } | |
| /** | |
| * Returns the local agent memory directory, which is project-specific and not checked into VCS. | |
| * When CLAUDE_CODE_REMOTE_MEMORY_DIR is set, persists to the mount with project namespacing. | |
| * Otherwise, uses <cwd>/.claude/agent-memory-local/<agentType>/. | |
| */ | |
| function getLocalAgentMemoryDir(dirName: string): string { | |
| if (process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR) { | |
| return ( | |
| join( | |
| process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR, | |
| 'projects', | |
| sanitizePath( | |
| findCanonicalGitRoot(getProjectRoot()) ?? getProjectRoot(), | |
| ), | |
| 'agent-memory-local', | |
| dirName, | |
| ) + sep | |
| ) | |
| } | |
| return join(getCwd(), '.claude', 'agent-memory-local', dirName) + sep | |
| } | |
| /** | |
| * Returns the agent memory directory for a given agent type and scope. | |
| * - 'user' scope: <memoryBase>/agent-memory/<agentType>/ | |
| * - 'project' scope: <cwd>/.claude/agent-memory/<agentType>/ | |
| * - 'local' scope: see getLocalAgentMemoryDir() | |
| */ | |
| export function getAgentMemoryDir( | |
| agentType: string, | |
| scope: AgentMemoryScope, | |
| ): string { | |
| const dirName = sanitizeAgentTypeForPath(agentType) | |
| switch (scope) { | |
| case 'project': | |
| return join(getCwd(), '.claude', 'agent-memory', dirName) + sep | |
| case 'local': | |
| return getLocalAgentMemoryDir(dirName) | |
| case 'user': | |
| return join(getMemoryBaseDir(), 'agent-memory', dirName) + sep | |
| } | |
| } | |
| // Check if file is within an agent memory directory (any scope). | |
| export function isAgentMemoryPath(absolutePath: string): boolean { | |
| // SECURITY: Normalize to prevent path traversal bypasses via .. segments | |
| const normalizedPath = normalize(absolutePath) | |
| const memoryBase = getMemoryBaseDir() | |
| // User scope: check memory base (may be custom dir or config home) | |
| if (normalizedPath.startsWith(join(memoryBase, 'agent-memory') + sep)) { | |
| return true | |
| } | |
| // Project scope: always cwd-based (not redirected) | |
| if ( | |
| normalizedPath.startsWith(join(getCwd(), '.claude', 'agent-memory') + sep) | |
| ) { | |
| return true | |
| } | |
| // Local scope: persisted to mount when CLAUDE_CODE_REMOTE_MEMORY_DIR is set, otherwise cwd-based | |
| if (process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR) { | |
| if ( | |
| normalizedPath.includes(sep + 'agent-memory-local' + sep) && | |
| normalizedPath.startsWith( | |
| join(process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR, 'projects') + sep, | |
| ) | |
| ) { | |
| return true | |
| } | |
| } else if ( | |
| normalizedPath.startsWith( | |
| join(getCwd(), '.claude', 'agent-memory-local') + sep, | |
| ) | |
| ) { | |
| return true | |
| } | |
| return false | |
| } | |
| /** | |
| * Returns the agent memory file path for a given agent type and scope. | |
| */ | |
| export function getAgentMemoryEntrypoint( | |
| agentType: string, | |
| scope: AgentMemoryScope, | |
| ): string { | |
| return join(getAgentMemoryDir(agentType, scope), 'MEMORY.md') | |
| } | |
| export function getMemoryScopeDisplay( | |
| memory: AgentMemoryScope | undefined, | |
| ): string { | |
| switch (memory) { | |
| case 'user': | |
| return `User (${join(getMemoryBaseDir(), 'agent-memory')}/)` | |
| case 'project': | |
| return 'Project (.claude/agent-memory/)' | |
| case 'local': | |
| return `Local (${getLocalAgentMemoryDir('...')})` | |
| default: | |
| return 'None' | |
| } | |
| } | |
| /** | |
| * Load persistent memory for an agent with memory enabled. | |
| * Creates the memory directory if needed and returns a prompt with memory contents. | |
| * | |
| * @param agentType The agent's type name (used as directory name) | |
| * @param scope 'user' for ~/.claude/agent-memory/ or 'project' for .claude/agent-memory/ | |
| */ | |
| export function loadAgentMemoryPrompt( | |
| agentType: string, | |
| scope: AgentMemoryScope, | |
| ): string { | |
| let scopeNote: string | |
| switch (scope) { | |
| case 'user': | |
| scopeNote = | |
| '- Since this memory is user-scope, keep learnings general since they apply across all projects' | |
| break | |
| case 'project': | |
| scopeNote = | |
| '- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project' | |
| break | |
| case 'local': | |
| scopeNote = | |
| '- Since this memory is local-scope (not checked into version control), tailor your memories to this project and machine' | |
| break | |
| } | |
| const memoryDir = getAgentMemoryDir(agentType, scope) | |
| // Fire-and-forget: this runs at agent-spawn time inside a sync | |
| // getSystemPrompt() callback (called from React render in AgentDetail.tsx, | |
| // so it cannot be async). The spawned agent won't try to Write until after | |
| // a full API round-trip, by which time mkdir will have completed. Even if | |
| // it hasn't, FileWriteTool does its own mkdir of the parent directory. | |
| void ensureMemoryDirExists(memoryDir) | |
| const coworkExtraGuidelines = | |
| process.env.CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES | |
| return buildMemoryPrompt({ | |
| displayName: 'Persistent Agent Memory', | |
| memoryDir, | |
| extraGuidelines: | |
| coworkExtraGuidelines && coworkExtraGuidelines.trim().length > 0 | |
| ? [scopeNote, coworkExtraGuidelines] | |
| : [scopeNote], | |
| }) | |
| } | |