fix(cli): preserve prompt hooks in history reseed

This commit is contained in:
Ayaan Zaidi
2026-04-26 08:48:02 +05:30
parent 0ba28c0911
commit 12e4841d96
2 changed files with 64 additions and 12 deletions

View File

@@ -8,6 +8,7 @@ import {
createReplyOperation,
replyRunRegistry,
} from "../auto-reply/reply/reply-run-registry.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
import { runPreparedCliAgent } from "./cli-runner.js";
import {
@@ -18,6 +19,7 @@ import {
} from "./cli-runner.test-support.js";
import { executePreparedCliRun } from "./cli-runner/execute.js";
import { resolveCliNoOutputTimeoutMs } from "./cli-runner/helpers.js";
import { prepareCliRunContext } from "./cli-runner/prepare.js";
import * as sessionHistoryModule from "./cli-runner/session-history.js";
import { MAX_CLI_SESSION_HISTORY_MESSAGES } from "./cli-runner/session-history.js";
import type { PreparedCliRunContext } from "./cli-runner/types.js";
@@ -752,6 +754,55 @@ describe("runCliAgent reliability", () => {
historySpy.mockRestore();
}
});
it("builds fresh-session history reseed prompts from hook-mutated prompts", async () => {
const { dir, sessionFile } = createSessionFile({
history: [{ role: "user", content: "earlier ask" }],
});
const config: OpenClawConfig = {
agents: {
defaults: {
workspace: dir,
cliBackends: {
"codex-cli": {
command: "codex",
args: ["exec"],
output: "text",
input: "arg",
sessionMode: "existing",
},
},
},
},
};
const hookRunner = {
hasHooks: vi.fn((hookName: string) => hookName === "before_prompt_build"),
runBeforePromptBuild: vi.fn(async () => ({ prependContext: "hook context" })),
runBeforeAgentStart: vi.fn(async () => undefined),
};
mockGetGlobalHookRunner.mockReturnValue(hookRunner as never);
try {
const context = await prepareCliRunContext({
sessionId: "s1",
sessionFile,
workspaceDir: dir,
config,
prompt: "current ask",
provider: "codex-cli",
model: "gpt-5.4",
timeoutMs: 1_000,
runId: "run-history-hook",
});
expect(context.params.prompt).toBe("hook context\n\ncurrent ask");
expect(context.openClawHistoryPrompt).toContain("User: earlier ask");
expect(context.openClawHistoryPrompt).toContain("hook context");
expect(context.openClawHistoryPrompt).toContain("current ask");
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
});
describe("resolveCliNoOutputTimeoutMs", () => {

View File

@@ -259,16 +259,17 @@ export async function prepareCliRunContext(
`cli session reset: provider=${params.provider} reason=${reusableCliSession.invalidatedReason}`,
);
}
const openClawHistoryPrompt = buildCliSessionHistoryPrompt({
messages: loadCliSessionHistoryMessages({
let openClawHistoryMessages: unknown[] | undefined;
const loadOpenClawHistoryMessages = () => {
openClawHistoryMessages ??= loadCliSessionHistoryMessages({
sessionId: params.sessionId,
sessionFile: params.sessionFile,
sessionKey: params.sessionKey,
agentId: params.agentId,
config: params.config,
}),
prompt: params.prompt,
});
});
return openClawHistoryMessages;
};
const heartbeatPrompt = resolveHeartbeatPromptForSystemPrompt({
config: params.config,
agentId: sessionAgentId,
@@ -323,13 +324,7 @@ export async function prepareCliRunContext(
try {
const hookResult = await resolvePromptBuildHookResult({
prompt: params.prompt,
messages: loadCliSessionHistoryMessages({
sessionId: params.sessionId,
sessionFile: params.sessionFile,
sessionKey: params.sessionKey,
agentId: params.agentId,
config: params.config,
}),
messages: loadOpenClawHistoryMessages(),
hookCtx: {
runId: params.runId,
agentId: sessionAgentId,
@@ -365,6 +360,12 @@ export async function prepareCliRunContext(
cliBackendLog.warn(`cli prompt-build hook preparation failed: ${String(error)}`);
}
}
const openClawHistoryPrompt = reusableCliSession.sessionId
? undefined
: buildCliSessionHistoryPrompt({
messages: loadOpenClawHistoryMessages(),
prompt: preparedPrompt,
});
systemPrompt = applyPluginTextReplacements(systemPrompt, backendResolved.textTransforms?.input);
const systemPromptReport = buildSystemPromptReport({
source: "run",