Files
openclaw/src/agents/bootstrap-files.test.ts
Jose E Velez 0c8fa63b93 feat: lightweight bootstrap context mode for heartbeat/cron runs (openclaw#26064) thanks @jose-velez
Verified:
- pnpm build
- pnpm check (fails on pre-existing unrelated repo issues in extensions/diffs and src/agents/tools/nodes-tool.test.ts)
- pnpm vitest run src/agents/bootstrap-files.test.ts src/infra/heartbeat-runner.model-override.test.ts src/cli/cron-cli.test.ts
- pnpm test:macmini (fails on pre-existing extensions/diffs import errors; touched suites pass)

Co-authored-by: jose-velez <10926182+jose-velez@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-01 20:13:24 -06:00

130 lines
4.2 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
clearInternalHooks,
registerInternalHook,
type AgentBootstrapHookContext,
} from "../hooks/internal-hooks.js";
import { makeTempWorkspace } from "../test-helpers/workspace.js";
import { resolveBootstrapContextForRun, resolveBootstrapFilesForRun } from "./bootstrap-files.js";
import type { WorkspaceBootstrapFile } from "./workspace.js";
function registerExtraBootstrapFileHook() {
registerInternalHook("agent:bootstrap", (event) => {
const context = event.context as AgentBootstrapHookContext;
context.bootstrapFiles = [
...context.bootstrapFiles,
{
name: "EXTRA.md",
path: path.join(context.workspaceDir, "EXTRA.md"),
content: "extra",
missing: false,
} as unknown as WorkspaceBootstrapFile,
];
});
}
function registerMalformedBootstrapFileHook() {
registerInternalHook("agent:bootstrap", (event) => {
const context = event.context as AgentBootstrapHookContext;
context.bootstrapFiles = [
...context.bootstrapFiles,
{
name: "EXTRA.md",
filePath: path.join(context.workspaceDir, "BROKEN.md"),
content: "broken",
missing: false,
} as unknown as WorkspaceBootstrapFile,
{
name: "EXTRA.md",
path: 123,
content: "broken",
missing: false,
} as unknown as WorkspaceBootstrapFile,
{
name: "EXTRA.md",
path: " ",
content: "broken",
missing: false,
} as unknown as WorkspaceBootstrapFile,
];
});
}
describe("resolveBootstrapFilesForRun", () => {
beforeEach(() => clearInternalHooks());
afterEach(() => clearInternalHooks());
it("applies bootstrap hook overrides", async () => {
registerExtraBootstrapFileHook();
const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
const files = await resolveBootstrapFilesForRun({ workspaceDir });
expect(files.some((file) => file.path === path.join(workspaceDir, "EXTRA.md"))).toBe(true);
});
it("drops malformed hook files with missing/invalid paths", async () => {
registerMalformedBootstrapFileHook();
const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
const warnings: string[] = [];
const files = await resolveBootstrapFilesForRun({
workspaceDir,
warn: (message) => warnings.push(message),
});
expect(
files.every((file) => typeof file.path === "string" && file.path.trim().length > 0),
).toBe(true);
expect(warnings).toHaveLength(3);
expect(warnings[0]).toContain('missing or invalid "path" field');
});
});
describe("resolveBootstrapContextForRun", () => {
beforeEach(() => clearInternalHooks());
afterEach(() => clearInternalHooks());
it("returns context files for hook-adjusted bootstrap files", async () => {
registerExtraBootstrapFileHook();
const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
const result = await resolveBootstrapContextForRun({ workspaceDir });
const extra = result.contextFiles.find(
(file) => file.path === path.join(workspaceDir, "EXTRA.md"),
);
expect(extra?.content).toBe("extra");
});
it("uses heartbeat-only bootstrap files in lightweight heartbeat mode", async () => {
const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
await fs.writeFile(path.join(workspaceDir, "HEARTBEAT.md"), "check inbox", "utf8");
await fs.writeFile(path.join(workspaceDir, "SOUL.md"), "persona", "utf8");
const files = await resolveBootstrapFilesForRun({
workspaceDir,
contextMode: "lightweight",
runKind: "heartbeat",
});
expect(files.length).toBeGreaterThan(0);
expect(files.every((file) => file.name === "HEARTBEAT.md")).toBe(true);
});
it("keeps bootstrap context empty in lightweight cron mode", async () => {
const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
await fs.writeFile(path.join(workspaceDir, "HEARTBEAT.md"), "check inbox", "utf8");
const files = await resolveBootstrapFilesForRun({
workspaceDir,
contextMode: "lightweight",
runKind: "cron",
});
expect(files).toEqual([]);
});
});