fix(plugins): hook systemPrompt gets collected then thrown away (#14583) (#14602)

* fix(plugins): apply before_agent_start hook systemPrompt to session (#14583)

* fix(plugins): apply legacy systemPrompt override and add changelog credit

---------

Co-authored-by: yinghaosang <yinghaosang@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
yinghaosang
2026-02-23 07:58:21 +08:00
committed by GitHub
parent 970062872f
commit a66b98a9da
3 changed files with 60 additions and 1 deletions

View File

@@ -183,6 +183,7 @@ Docs: https://docs.openclaw.ai
- Agents/Transcripts: validate assistant tool-call names (syntax/length + registered tool allowlist) before transcript persistence and during replay sanitization so malformed failover tool names no longer poison sessions with repeated provider HTTP 400 errors. (#23324) Thanks @johnsantry. - Agents/Transcripts: validate assistant tool-call names (syntax/length + registered tool allowlist) before transcript persistence and during replay sanitization so malformed failover tool names no longer poison sessions with repeated provider HTTP 400 errors. (#23324) Thanks @johnsantry.
- Agents/Mistral: sanitize tool-call IDs in the embedded agent loop and generate strict provider-safe pending tool-call IDs, preventing Mistral strict9 `HTTP 400` failures on tool continuations. (#23698) Thanks @echoVic. - Agents/Mistral: sanitize tool-call IDs in the embedded agent loop and generate strict provider-safe pending tool-call IDs, preventing Mistral strict9 `HTTP 400` failures on tool continuations. (#23698) Thanks @echoVic.
- Agents/Compaction: strip stale assistant usage snapshots from pre-compaction turns when replaying history after a compaction summary so context-token estimation no longer reuses pre-compaction totals and immediately re-triggers destructive follow-up compactions. (#19127) Thanks @tedwatson. - Agents/Compaction: strip stale assistant usage snapshots from pre-compaction turns when replaying history after a compaction summary so context-token estimation no longer reuses pre-compaction totals and immediately re-triggers destructive follow-up compactions. (#19127) Thanks @tedwatson.
- Agents/Hooks: honor legacy `before_agent_start` `systemPrompt` values in the embedded prompt-build path so plugin-provided system-prompt overrides are applied instead of being silently ignored. (#13475, #14583) Thanks @yinghaosang and @mushuiyu422.
- Agents/Replies: emit a default completion acknowledgement (`✅ Done.`) only for direct/private tool-only completions with no final assistant text, while suppressing synthetic acknowledgements for channel/group sessions and runs that already delivered output via messaging tools. (#22834) Thanks @Oldshue. - Agents/Replies: emit a default completion acknowledgement (`✅ Done.`) only for direct/private tool-only completions with no final assistant text, while suppressing synthetic acknowledgements for channel/group sessions and runs that already delivered output via messaging tools. (#22834) Thanks @Oldshue.
- Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero. - Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero.
- Agents/Subagents: make announce call timeouts configurable via `agents.defaults.subagents.announceTimeoutMs` and restore a 60s default to prevent false timeout failures on slower announce paths. (#22719) Thanks @Valadon. - Agents/Subagents: make announce call timeouts configurable via `agents.defaults.subagents.announceTimeoutMs` and restore a 60s default to prevent false timeout failures on slower announce paths. (#22719) Thanks @Valadon.

View File

@@ -560,7 +560,7 @@ export async function runEmbeddedAttempt(
tools, tools,
}); });
const systemPromptOverride = createSystemPromptOverride(appendPrompt); const systemPromptOverride = createSystemPromptOverride(appendPrompt);
const systemPromptText = systemPromptOverride(); let systemPromptText = systemPromptOverride();
const sessionLock = await acquireSessionWriteLock({ const sessionLock = await acquireSessionWriteLock({
sessionFile: params.sessionFile, sessionFile: params.sessionFile,
@@ -1038,6 +1038,13 @@ export async function runEmbeddedAttempt(
`hooks: prepended context to prompt (${hookResult.prependContext.length} chars)`, `hooks: prepended context to prompt (${hookResult.prependContext.length} chars)`,
); );
} }
const legacySystemPrompt =
typeof hookResult?.systemPrompt === "string" ? hookResult.systemPrompt.trim() : "";
if (legacySystemPrompt) {
applySystemPromptOverrideToSession(activeSession, legacySystemPrompt);
systemPromptText = legacySystemPrompt;
log.debug(`hooks: applied systemPrompt override (${legacySystemPrompt.length} chars)`);
}
} }
log.debug(`embedded run prompt start: runId=${params.runId} sessionId=${params.sessionId}`); log.debug(`embedded run prompt start: runId=${params.runId} sessionId=${params.sessionId}`);

View File

@@ -0,0 +1,51 @@
import type { AgentSession } from "@mariozechner/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import { applySystemPromptOverrideToSession, createSystemPromptOverride } from "./system-prompt.js";
function createMockSession() {
const setSystemPrompt = vi.fn();
const session = {
agent: { setSystemPrompt },
} as unknown as AgentSession;
return { session, setSystemPrompt };
}
describe("applySystemPromptOverrideToSession", () => {
it("applies a string override to the session system prompt", () => {
const { session, setSystemPrompt } = createMockSession();
const prompt = "You are a helpful assistant with custom context.";
applySystemPromptOverrideToSession(session, prompt);
expect(setSystemPrompt).toHaveBeenCalledWith(prompt);
const mutable = session as unknown as { _baseSystemPrompt?: string };
expect(mutable._baseSystemPrompt).toBe(prompt);
});
it("trims whitespace from string overrides", () => {
const { session, setSystemPrompt } = createMockSession();
applySystemPromptOverrideToSession(session, " padded prompt ");
expect(setSystemPrompt).toHaveBeenCalledWith("padded prompt");
});
it("applies a function override to the session system prompt", () => {
const { session, setSystemPrompt } = createMockSession();
const override = createSystemPromptOverride("function-based prompt");
applySystemPromptOverrideToSession(session, override);
expect(setSystemPrompt).toHaveBeenCalledWith("function-based prompt");
});
it("sets _rebuildSystemPrompt that returns the override", () => {
const { session } = createMockSession();
applySystemPromptOverrideToSession(session, "rebuild test");
const mutable = session as unknown as {
_rebuildSystemPrompt?: (toolNames: string[]) => string;
};
expect(mutable._rebuildSystemPrompt?.(["tool1"])).toBe("rebuild test");
});
});