refactor: expand bootstrap helpers and tests

This commit is contained in:
Peter Steinberger
2026-01-18 05:50:23 +00:00
parent d5be8fa576
commit 88b37e80fc
8 changed files with 177 additions and 45 deletions

View File

@@ -0,0 +1,66 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
resolveBootstrapContextForRun,
resolveBootstrapFilesForRun,
} from "./bootstrap-files.js";
import {
clearInternalHooks,
registerInternalHook,
type AgentBootstrapHookContext,
} from "../hooks/internal-hooks.js";
describe("resolveBootstrapFilesForRun", () => {
beforeEach(() => clearInternalHooks());
afterEach(() => clearInternalHooks());
it("applies bootstrap hook overrides", async () => {
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,
},
];
});
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-bootstrap-"));
const files = await resolveBootstrapFilesForRun({ workspaceDir });
expect(files.some((file) => file.name === "EXTRA.md")).toBe(true);
});
});
describe("resolveBootstrapContextForRun", () => {
beforeEach(() => clearInternalHooks());
afterEach(() => clearInternalHooks());
it("returns context files for hook-adjusted bootstrap files", async () => {
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,
},
];
});
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-bootstrap-"));
const result = await resolveBootstrapContextForRun({ workspaceDir });
const extra = result.contextFiles.find((file) => file.path === "EXTRA.md");
expect(extra?.content).toBe("extra");
});
});

View File

@@ -5,6 +5,8 @@ import {
loadWorkspaceBootstrapFiles,
type WorkspaceBootstrapFile,
} from "./workspace.js";
import { buildBootstrapContextFiles, resolveBootstrapMaxChars } from "./pi-embedded-helpers.js";
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
export async function resolveBootstrapFilesForRun(params: {
workspaceDir: string;
@@ -27,3 +29,22 @@ export async function resolveBootstrapFilesForRun(params: {
agentId: params.agentId,
});
}
export async function resolveBootstrapContextForRun(params: {
workspaceDir: string;
config?: ClawdbotConfig;
sessionKey?: string;
sessionId?: string;
agentId?: string;
warn?: (message: string) => void;
}): Promise<{
bootstrapFiles: WorkspaceBootstrapFile[];
contextFiles: EmbeddedContextFile[];
}> {
const bootstrapFiles = await resolveBootstrapFilesForRun(params);
const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {
maxChars: resolveBootstrapMaxChars(params.config),
warn: params.warn,
});
return { bootstrapFiles, contextFiles };
}

View File

@@ -7,7 +7,7 @@ import { createSubsystemLogger } from "../logging.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { resolveUserPath } from "../utils.js";
import { resolveSessionAgentIds } from "./agent-scope.js";
import { resolveBootstrapFilesForRun } from "./bootstrap-files.js";
import { resolveBootstrapContextForRun } from "./bootstrap-files.js";
import { resolveCliBackendConfig } from "./cli-backends.js";
import {
appendImagePathsToPrompt,
@@ -25,12 +25,7 @@ import {
writeCliImages,
} from "./cli-runner/helpers.js";
import { FailoverError, resolveFailoverStatus } from "./failover-error.js";
import {
buildBootstrapContextFiles,
classifyFailoverReason,
isFailoverErrorMessage,
resolveBootstrapMaxChars,
} from "./pi-embedded-helpers.js";
import { classifyFailoverReason, isFailoverErrorMessage } from "./pi-embedded-helpers.js";
import type { EmbeddedPiRunResult } from "./pi-embedded-runner.js";
const log = createSubsystemLogger("agent/claude-cli");
@@ -72,15 +67,12 @@ export async function runCliAgent(params: {
.filter(Boolean)
.join("\n");
const hookAdjustedBootstrapFiles = await resolveBootstrapFilesForRun({
const sessionLabel = params.sessionKey ?? params.sessionId;
const { contextFiles } = await resolveBootstrapContextForRun({
workspaceDir,
config: params.config,
sessionKey: params.sessionKey,
sessionId: params.sessionId,
});
const sessionLabel = params.sessionKey ?? params.sessionId;
const contextFiles = buildBootstrapContextFiles(hookAdjustedBootstrapFiles, {
maxChars: resolveBootstrapMaxChars(params.config),
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
});
const { defaultAgentId, sessionAgentId } = resolveSessionAgentIds({

View File

@@ -16,14 +16,12 @@ import { isReasoningTagProvider } from "../../utils/provider-utils.js";
import { resolveUserPath } from "../../utils.js";
import { resolveClawdbotAgentDir } from "../agent-paths.js";
import { resolveSessionAgentIds } from "../agent-scope.js";
import { resolveBootstrapFilesForRun } from "../bootstrap-files.js";
import { resolveBootstrapContextForRun } from "../bootstrap-files.js";
import type { ExecElevatedDefaults } from "../bash-tools.js";
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js";
import { getApiKeyForModel, resolveModelAuthMode } from "../model-auth.js";
import { ensureClawdbotModelsJson } from "../models-config.js";
import {
buildBootstrapContextFiles,
type EmbeddedContextFile,
ensureSessionHeader,
resolveBootstrapMaxChars,
validateAnthropicTurns,
@@ -178,20 +176,14 @@ export async function compactEmbeddedPiSession(params: {
workspaceDir: effectiveWorkspace,
});
const hookAdjustedBootstrapFiles = await resolveBootstrapFilesForRun({
const sessionLabel = params.sessionKey ?? params.sessionId;
const { contextFiles } = await resolveBootstrapContextForRun({
workspaceDir: effectiveWorkspace,
config: params.config,
sessionKey: params.sessionKey,
sessionId: params.sessionId,
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
});
const sessionLabel = params.sessionKey ?? params.sessionId;
const contextFiles: EmbeddedContextFile[] = buildBootstrapContextFiles(
hookAdjustedBootstrapFiles,
{
maxChars: resolveBootstrapMaxChars(params.config),
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
},
);
const runAbortController = new AbortController();
const toolsRaw = createClawdbotCodingTools({
exec: {

View File

@@ -17,10 +17,9 @@ import { isSubagentSessionKey } from "../../../routing/session-key.js";
import { resolveUserPath } from "../../../utils.js";
import { resolveClawdbotAgentDir } from "../../agent-paths.js";
import { resolveSessionAgentIds } from "../../agent-scope.js";
import { resolveBootstrapFilesForRun } from "../../bootstrap-files.js";
import { resolveBootstrapContextForRun } from "../../bootstrap-files.js";
import { resolveModelAuthMode } from "../../model-auth.js";
import {
buildBootstrapContextFiles,
isCloudCodeAssistFormatError,
resolveBootstrapMaxChars,
validateAnthropicTurns,
@@ -120,17 +119,15 @@ export async function runEmbeddedAttempt(
workspaceDir: effectiveWorkspace,
});
const hookAdjustedBootstrapFiles = await resolveBootstrapFilesForRun({
workspaceDir: effectiveWorkspace,
config: params.config,
sessionKey: params.sessionKey,
sessionId: params.sessionId,
});
const sessionLabel = params.sessionKey ?? params.sessionId;
const contextFiles = buildBootstrapContextFiles(hookAdjustedBootstrapFiles, {
maxChars: resolveBootstrapMaxChars(params.config),
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
});
const { bootstrapFiles: hookAdjustedBootstrapFiles, contextFiles } =
await resolveBootstrapContextForRun({
workspaceDir: effectiveWorkspace,
config: params.config,
sessionKey: params.sessionKey,
sessionId: params.sessionId,
warn: (message) => log.warn(`${message} (sessionKey=${sessionLabel})`),
});
const agentDir = params.agentDir ?? resolveClawdbotAgentDir();