Allow extensions to modify system prompt in before_agent_start

- Add systemPrompt to BeforeAgentStartEvent so extensions can see current prompt
- Change systemPromptAppend to systemPrompt in BeforeAgentStartEventResult for full replacement
- Extensions can now chain modifications (each sees the result of previous)
- Update ssh.ts to replace local cwd with remote cwd in system prompt
- Update pirate.ts, claude-rules.ts, preset.ts to use new API

fixes #575
This commit is contained in:
Mario Zechner 2026-01-08 19:54:34 +01:00
parent 0774db2e5a
commit 17cb328ca1
10 changed files with 65 additions and 27 deletions

View file

@ -38,7 +38,7 @@ import type {
/** Combined result from all before_agent_start handlers */
interface BeforeAgentStartCombinedResult {
messages?: NonNullable<BeforeAgentStartEventResult["message"]>[];
systemPromptAppend?: string;
systemPrompt?: string;
}
export type ExtensionErrorListener = (error: ExtensionError) => void;
@ -433,11 +433,13 @@ export class ExtensionRunner {
async emitBeforeAgentStart(
prompt: string,
images?: ImageContent[],
images: ImageContent[] | undefined,
systemPrompt: string,
): Promise<BeforeAgentStartCombinedResult | undefined> {
const ctx = this.createContext();
const messages: NonNullable<BeforeAgentStartEventResult["message"]>[] = [];
const systemPromptAppends: string[] = [];
let currentSystemPrompt = systemPrompt;
let systemPromptModified = false;
for (const ext of this.extensions) {
const handlers = ext.handlers.get("before_agent_start");
@ -445,7 +447,12 @@ export class ExtensionRunner {
for (const handler of handlers) {
try {
const event: BeforeAgentStartEvent = { type: "before_agent_start", prompt, images };
const event: BeforeAgentStartEvent = {
type: "before_agent_start",
prompt,
images,
systemPrompt: currentSystemPrompt,
};
const handlerResult = await handler(event, ctx);
if (handlerResult) {
@ -453,8 +460,9 @@ export class ExtensionRunner {
if (result.message) {
messages.push(result.message);
}
if (result.systemPromptAppend) {
systemPromptAppends.push(result.systemPromptAppend);
if (result.systemPrompt !== undefined) {
currentSystemPrompt = result.systemPrompt;
systemPromptModified = true;
}
}
} catch (err) {
@ -470,10 +478,10 @@ export class ExtensionRunner {
}
}
if (messages.length > 0 || systemPromptAppends.length > 0) {
if (messages.length > 0 || systemPromptModified) {
return {
messages: messages.length > 0 ? messages : undefined,
systemPromptAppend: systemPromptAppends.length > 0 ? systemPromptAppends.join("\n\n") : undefined,
systemPrompt: systemPromptModified ? currentSystemPrompt : undefined,
};
}

View file

@ -349,6 +349,7 @@ export interface BeforeAgentStartEvent {
type: "before_agent_start";
prompt: string;
images?: ImageContent[];
systemPrompt: string;
}
/** Fired when an agent loop starts */
@ -504,7 +505,8 @@ export interface ToolResultEventResult {
export interface BeforeAgentStartEventResult {
message?: Pick<CustomMessage, "customType" | "content" | "display" | "details">;
systemPromptAppend?: string;
/** Replace the system prompt for this turn. If multiple extensions return this, they are chained. */
systemPrompt?: string;
}
export interface SessionBeforeSwitchResult {