mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
perf: trim agent workspace imports
This commit is contained in:
@@ -3,8 +3,8 @@ import type { Dirent } from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope.js";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope-config.js";
|
||||
import type { OpenClawConfig } from "../../../../src/config/config.js";
|
||||
import { resolveMemoryBackendConfig } from "./backend-config.js";
|
||||
|
||||
@@ -54,6 +54,23 @@ const customQmdCollections = (
|
||||
const customCollectionPaths = (resolved: ResolvedMemoryBackendConfig): string[] =>
|
||||
customQmdCollections(resolved).map((collection) => collection.path);
|
||||
|
||||
let fixtureRoot: string;
|
||||
let fixtureId = 0;
|
||||
|
||||
beforeAll(async () => {
|
||||
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qmd-backend-config-"));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.rm(fixtureRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
async function createFixtureDir(name: string): Promise<string> {
|
||||
const dir = path.join(fixtureRoot, `${name}-${fixtureId++}`);
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
describe("resolveMemoryBackendConfig", () => {
|
||||
it("defaults to builtin backend when config missing", () => {
|
||||
const cfg = { agents: { defaults: { workspace: "/tmp/memory-test" } } } as OpenClawConfig;
|
||||
@@ -261,65 +278,57 @@ describe("resolveMemoryBackendConfig", () => {
|
||||
});
|
||||
|
||||
it("keeps symlinked workspace paths agent-scoped when deciding custom collection names", async () => {
|
||||
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qmd-backend-config-"));
|
||||
const tmpRoot = await createFixtureDir("symlinked-workspace");
|
||||
const workspaceDir = path.join(tmpRoot, "workspace");
|
||||
const workspaceAliasDir = path.join(tmpRoot, "workspace-alias");
|
||||
try {
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.symlink(workspaceDir, workspaceAliasDir);
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { workspace: workspaceDir },
|
||||
list: [{ id: "main", default: true, workspace: workspaceDir }],
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.symlink(workspaceDir, workspaceAliasDir);
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { workspace: workspaceDir },
|
||||
list: [{ id: "main", default: true, workspace: workspaceDir }],
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
includeDefaultMemory: false,
|
||||
paths: [{ path: workspaceAliasDir, name: "workspace", pattern: "**/*.md" }],
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
includeDefaultMemory: false,
|
||||
paths: [{ path: workspaceAliasDir, name: "workspace", pattern: "**/*.md" }],
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
|
||||
const names = collectionNames(resolved);
|
||||
expect(names.has("workspace-main")).toBe(true);
|
||||
expect(names.has("workspace")).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
|
||||
const names = collectionNames(resolved);
|
||||
expect(names.has("workspace-main")).toBe(true);
|
||||
expect(names.has("workspace")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps unresolved child paths under a symlinked workspace agent-scoped", async () => {
|
||||
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qmd-backend-config-"));
|
||||
const tmpRoot = await createFixtureDir("symlinked-child");
|
||||
const realRootDir = path.join(tmpRoot, "real-root");
|
||||
const aliasRootDir = path.join(tmpRoot, "alias-root");
|
||||
const workspaceDir = path.join(realRootDir, "workspace");
|
||||
const workspaceAliasDir = path.join(aliasRootDir, "workspace");
|
||||
try {
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.symlink(realRootDir, aliasRootDir);
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { workspace: workspaceDir },
|
||||
list: [{ id: "main", default: true, workspace: workspaceDir }],
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
await fs.symlink(realRootDir, aliasRootDir);
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: { workspace: workspaceDir },
|
||||
list: [{ id: "main", default: true, workspace: workspaceDir }],
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
includeDefaultMemory: false,
|
||||
paths: [
|
||||
{ path: path.join(workspaceAliasDir, "notes"), name: "notes", pattern: "**/*.md" },
|
||||
],
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
includeDefaultMemory: false,
|
||||
paths: [
|
||||
{ path: path.join(workspaceAliasDir, "notes"), name: "notes", pattern: "**/*.md" },
|
||||
],
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
|
||||
const names = collectionNames(resolved);
|
||||
expect(names.has("notes-main")).toBe(true);
|
||||
expect(names.has("notes")).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(tmpRoot, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
|
||||
const names = collectionNames(resolved);
|
||||
expect(names.has("notes-main")).toBe(true);
|
||||
expect(names.has("notes")).toBe(false);
|
||||
});
|
||||
|
||||
it("resolves qmd update timeout overrides", () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope.js";
|
||||
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope-config.js";
|
||||
import { parseDurationMs } from "../../../../src/cli/parse-duration.js";
|
||||
import type { OpenClawConfig } from "../../../../src/config/config.js";
|
||||
import type { SessionSendPolicyConfig } from "../../../../src/config/types.base.js";
|
||||
|
||||
@@ -5,11 +5,10 @@ import type {
|
||||
AgentDefaultsConfig,
|
||||
} from "../config/types.agent-defaults.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
|
||||
import { readStringValue } from "../shared/string-coerce.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "./workspace.js";
|
||||
import { resolveDefaultAgentWorkspaceDir } from "./workspace-default.js";
|
||||
|
||||
type AgentEntry = NonNullable<NonNullable<OpenClawConfig["agents"]>["list"]>[number];
|
||||
|
||||
@@ -36,12 +35,16 @@ export type ResolvedAgentConfig = {
|
||||
tools?: AgentEntry["tools"];
|
||||
};
|
||||
|
||||
let log: ReturnType<typeof createSubsystemLogger> | null = null;
|
||||
let defaultAgentWarned = false;
|
||||
|
||||
function getLog(): ReturnType<typeof createSubsystemLogger> {
|
||||
log ??= createSubsystemLogger("agent-scope");
|
||||
return log;
|
||||
function warnMultipleDefaultAgents(): void {
|
||||
void import("../logging/subsystem.js")
|
||||
.then(({ createSubsystemLogger }) => {
|
||||
createSubsystemLogger("agent-scope").warn(
|
||||
"Multiple agents marked default=true; using the first entry as default.",
|
||||
);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
}
|
||||
|
||||
/** Strip null bytes from paths to prevent ENOTDIR errors. */
|
||||
@@ -83,7 +86,7 @@ export function resolveDefaultAgentId(cfg: OpenClawConfig): string {
|
||||
const defaults = agents.filter((agent) => agent?.default);
|
||||
if (defaults.length > 1 && !defaultAgentWarned) {
|
||||
defaultAgentWarned = true;
|
||||
getLog().warn("Multiple agents marked default=true; using the first entry as default.");
|
||||
warnMultipleDefaultAgents();
|
||||
}
|
||||
const chosen = (defaults[0] ?? agents[0])?.id?.trim();
|
||||
return normalizeAgentId(chosen || DEFAULT_AGENT_ID);
|
||||
|
||||
18
src/agents/workspace-default.ts
Normal file
18
src/agents/workspace-default.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
|
||||
export function resolveDefaultAgentWorkspaceDir(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
homedir: () => string = os.homedir,
|
||||
): string {
|
||||
const home = resolveRequiredHomeDir(env, homedir);
|
||||
const profile = env.OPENCLAW_PROFILE?.trim();
|
||||
if (profile && normalizeOptionalLowercaseString(profile) !== "default") {
|
||||
return path.join(home, ".openclaw", `workspace-${profile}`);
|
||||
}
|
||||
return path.join(home, ".openclaw", "workspace");
|
||||
}
|
||||
|
||||
export const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir();
|
||||
@@ -1,32 +1,21 @@
|
||||
import syncFs from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { openBoundaryFile } from "../infra/boundary-file-read.js";
|
||||
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
|
||||
import {
|
||||
CANONICAL_ROOT_MEMORY_FILENAME,
|
||||
exactWorkspaceEntryExists,
|
||||
} from "../memory/root-memory-files.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { isCronSessionKey, isSubagentSessionKey } from "../routing/session-key.js";
|
||||
import { normalizeOptionalLowercaseString, readStringValue } from "../shared/string-coerce.js";
|
||||
import { readStringValue } from "../shared/string-coerce.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace-default.js";
|
||||
import { resolveWorkspaceTemplateDir } from "./workspace-templates.js";
|
||||
|
||||
export function resolveDefaultAgentWorkspaceDir(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
homedir: () => string = os.homedir,
|
||||
): string {
|
||||
const home = resolveRequiredHomeDir(env, homedir);
|
||||
const profile = env.OPENCLAW_PROFILE?.trim();
|
||||
if (profile && normalizeOptionalLowercaseString(profile) !== "default") {
|
||||
return path.join(home, ".openclaw", `workspace-${profile}`);
|
||||
}
|
||||
return path.join(home, ".openclaw", "workspace");
|
||||
}
|
||||
|
||||
export const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir();
|
||||
export {
|
||||
DEFAULT_AGENT_WORKSPACE_DIR,
|
||||
resolveDefaultAgentWorkspaceDir,
|
||||
} from "./workspace-default.js";
|
||||
export const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
|
||||
export const DEFAULT_SOUL_FILENAME = "SOUL.md";
|
||||
export const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveAgentWorkspaceDir } from "../../agents/agent-scope.js";
|
||||
import { resolveAgentWorkspaceDir } from "../../agents/agent-scope-config.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { resolveMemoryBackendConfig } from "./backend-config.js";
|
||||
import { isQmdScopeAllowed } from "./qmd-scope.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { resolveAgentWorkspaceDir } from "../../agents/agent-scope.js";
|
||||
import { resolveAgentWorkspaceDir } from "../../agents/agent-scope-config.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import type { SessionSendPolicyConfig } from "../../config/types.base.js";
|
||||
import type {
|
||||
|
||||
Reference in New Issue
Block a user