This commit is contained in:
Jason Brashear (AKA SEM) 2026-01-30 07:07:05 -05:00 committed by GitHub
commit e5019dc02c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 98 additions and 0 deletions

View File

@ -1,8 +1,16 @@
import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
DEFAULT_AGENTS_FILENAME,
DEFAULT_IDENTITY_FILENAME,
DEFAULT_MEMORY_ALT_FILENAME,
DEFAULT_MEMORY_DIR,
DEFAULT_MEMORY_FILENAME,
DEFAULT_SOUL_FILENAME,
DEFAULT_USER_FILENAME,
ensureAgentWorkspace,
loadWorkspaceBootstrapFiles,
} from "./workspace.js";
import { makeTempWorkspace, writeWorkspaceFile } from "../test-helpers/workspace.js";
@ -47,3 +55,62 @@ describe("loadWorkspaceBootstrapFiles", () => {
expect(memoryEntries).toHaveLength(0);
});
});
describe("ensureAgentWorkspace", () => {
it("creates memory directory with symlinks to identity files", async () => {
const tempDir = await makeTempWorkspace("moltbot-workspace-");
const result = await ensureAgentWorkspace({
dir: tempDir,
ensureBootstrapFiles: true,
});
// Check memory directory was created
expect(result.memoryDir).toBe(path.join(tempDir, DEFAULT_MEMORY_DIR));
const memoryDirStat = await fs.stat(result.memoryDir!);
expect(memoryDirStat.isDirectory()).toBe(true);
// Check symlinks exist in memory directory
const memoryFiles = await fs.readdir(result.memoryDir!);
expect(memoryFiles).toContain(DEFAULT_SOUL_FILENAME);
expect(memoryFiles).toContain(DEFAULT_USER_FILENAME);
expect(memoryFiles).toContain(DEFAULT_AGENTS_FILENAME);
expect(memoryFiles).toContain(DEFAULT_IDENTITY_FILENAME);
// Check symlinks point to correct files
const soulLink = await fs.readlink(path.join(result.memoryDir!, DEFAULT_SOUL_FILENAME));
expect(soulLink).toBe(`../${DEFAULT_SOUL_FILENAME}`);
});
it("does not create memory directory when ensureBootstrapFiles is false", async () => {
const tempDir = await makeTempWorkspace("moltbot-workspace-");
const result = await ensureAgentWorkspace({
dir: tempDir,
ensureBootstrapFiles: false,
});
expect(result.memoryDir).toBeUndefined();
const memoryDirPath = path.join(tempDir, DEFAULT_MEMORY_DIR);
await expect(fs.access(memoryDirPath)).rejects.toThrow();
});
it("does not overwrite existing files in memory directory", async () => {
const tempDir = await makeTempWorkspace("moltbot-workspace-");
const memoryDir = path.join(tempDir, DEFAULT_MEMORY_DIR);
await fs.mkdir(memoryDir, { recursive: true });
// Create an existing file in memory directory
const existingContent = "existing content";
await fs.writeFile(path.join(memoryDir, DEFAULT_SOUL_FILENAME), existingContent);
await ensureAgentWorkspace({
dir: tempDir,
ensureBootstrapFiles: true,
});
// Check existing file was not overwritten
const content = await fs.readFile(path.join(memoryDir, DEFAULT_SOUL_FILENAME), "utf-8");
expect(content).toBe(existingContent);
});
});

View File

@ -28,6 +28,7 @@ export const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
export const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
export const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
export const DEFAULT_MEMORY_ALT_FILENAME = "memory.md";
export const DEFAULT_MEMORY_DIR = "memory";
const TEMPLATE_DIR = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
@ -120,6 +121,7 @@ export async function ensureAgentWorkspace(params?: {
ensureBootstrapFiles?: boolean;
}): Promise<{
dir: string;
memoryDir?: string;
agentsPath?: string;
soulPath?: string;
toolsPath?: string;
@ -174,10 +176,39 @@ export async function ensureAgentWorkspace(params?: {
if (isBrandNewWorkspace) {
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
}
// Ensure memory/ directory exists and symlink identity files for memory search indexing
const memoryDir = path.join(dir, DEFAULT_MEMORY_DIR);
await fs.mkdir(memoryDir, { recursive: true });
// Symlink identity files into memory/ so they are indexed by memory search
const identityFilesToSymlink = [
{ source: soulPath, target: path.join(memoryDir, DEFAULT_SOUL_FILENAME) },
{ source: userPath, target: path.join(memoryDir, DEFAULT_USER_FILENAME) },
{ source: agentsPath, target: path.join(memoryDir, DEFAULT_AGENTS_FILENAME) },
{ source: identityPath, target: path.join(memoryDir, DEFAULT_IDENTITY_FILENAME) },
];
for (const { source, target } of identityFilesToSymlink) {
try {
await fs.access(target);
// Target already exists, skip
} catch {
// Target doesn't exist, create symlink
try {
const relativePath = path.relative(memoryDir, source);
await fs.symlink(relativePath, target);
} catch {
// Symlink creation failed (e.g., on Windows without privileges), skip silently
}
}
}
await ensureGitRepo(dir, isBrandNewWorkspace);
return {
dir,
memoryDir,
agentsPath,
soulPath,
toolsPath,