fix: avoid duplicate prompt context

This commit is contained in:
Peter Steinberger
2026-01-08 00:01:40 +00:00
parent 8b4bcc6b7a
commit badc1602c8
3 changed files with 64 additions and 44 deletions

View File

@@ -18,6 +18,7 @@
- Auto-reply: removed `autoReply` from Discord/Slack/Telegram channel configs; use `requireMention` instead (Telegram topics now support `requireMention` overrides). - Auto-reply: removed `autoReply` from Discord/Slack/Telegram channel configs; use `requireMention` instead (Telegram topics now support `requireMention` overrides).
### Fixes ### Fixes
- Agent: avoid duplicating context/skills when SDK rebuilds the system prompt. (#418)
- Signal: reconnect SSE monitor with abortable backoff; log stream errors. Thanks @nexty5870 for PR #430. - Signal: reconnect SSE monitor with abortable backoff; log stream errors. Thanks @nexty5870 for PR #430.
- Gateway: pass resolved provider as messageProvider for agent runs so provider-specific tools are available. Thanks @imfing for PR #389. - Gateway: pass resolved provider as messageProvider for agent runs so provider-specific tools are available. Thanks @imfing for PR #389.
- Discord/Telegram: add per-request retry policy with configurable delays and docs. - Discord/Telegram: add per-request retry policy with configurable delays and docs.

View File

@@ -1,10 +1,14 @@
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core"; import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
import { SessionManager } from "@mariozechner/pi-coding-agent"; import {
buildSystemPrompt,
SessionManager,
} from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox"; import { Type } from "@sinclair/typebox";
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { import {
applyGoogleTurnOrderingFix, applyGoogleTurnOrderingFix,
buildEmbeddedSandboxInfo, buildEmbeddedSandboxInfo,
createSystemPromptAppender,
splitSdkTools, splitSdkTools,
} from "./pi-embedded-runner.js"; } from "./pi-embedded-runner.js";
import type { SandboxContext } from "./sandbox.js"; import type { SandboxContext } from "./sandbox.js";
@@ -105,6 +109,22 @@ describe("splitSdkTools", () => {
}); });
}); });
describe("createSystemPromptAppender", () => {
it("appends without duplicating context files", () => {
const sentinel = "CONTEXT_SENTINEL_42";
const defaultPrompt = buildSystemPrompt({
cwd: "/tmp",
contextFiles: [{ path: "/tmp/AGENTS.md", content: sentinel }],
});
const appender = createSystemPromptAppender("APPEND_SECTION");
const finalPrompt = appender(defaultPrompt);
const occurrences = finalPrompt.split(sentinel).length - 1;
expect(typeof appender).toBe("function");
expect(occurrences).toBe(1);
expect(finalPrompt).toContain("APPEND_SECTION");
});
});
describe("applyGoogleTurnOrderingFix", () => { describe("applyGoogleTurnOrderingFix", () => {
const makeAssistantFirst = () => const makeAssistantFirst = () =>
[ [

View File

@@ -10,7 +10,6 @@ import type {
} from "@mariozechner/pi-agent-core"; } from "@mariozechner/pi-agent-core";
import type { Api, AssistantMessage, Model } from "@mariozechner/pi-ai"; import type { Api, AssistantMessage, Model } from "@mariozechner/pi-ai";
import { import {
buildSystemPrompt,
createAgentSession, createAgentSession,
discoverAuthStorage, discoverAuthStorage,
discoverModels, discoverModels,
@@ -492,6 +491,16 @@ export function buildEmbeddedSandboxInfo(
}; };
} }
export function createSystemPromptAppender(
appendPrompt: string,
): (defaultPrompt: string) => string {
const trimmed = appendPrompt.trim();
if (!trimmed) {
return (defaultPrompt) => defaultPrompt;
}
return (defaultPrompt) => `${defaultPrompt}\n\n${appendPrompt}`;
}
const BUILT_IN_TOOL_NAMES = new Set(["read", "bash", "edit", "write"]); const BUILT_IN_TOOL_NAMES = new Set(["read", "bash", "edit", "write"]);
type AnyAgentTool = AgentTool; type AnyAgentTool = AgentTool;
@@ -775,28 +784,23 @@ export async function compactEmbeddedPiSession(params: {
params.config?.agent?.userTimezone, params.config?.agent?.userTimezone,
); );
const userTime = formatUserTime(new Date(), userTimezone); const userTime = formatUserTime(new Date(), userTimezone);
const systemPrompt = buildSystemPrompt({ const appendPrompt = buildAgentSystemPromptAppend({
appendPrompt: buildAgentSystemPromptAppend({ workspaceDir: effectiveWorkspace,
workspaceDir: effectiveWorkspace, defaultThinkLevel: params.thinkLevel,
defaultThinkLevel: params.thinkLevel, extraSystemPrompt: params.extraSystemPrompt,
extraSystemPrompt: params.extraSystemPrompt, ownerNumbers: params.ownerNumbers,
ownerNumbers: params.ownerNumbers, reasoningTagHint,
reasoningTagHint, heartbeatPrompt: resolveHeartbeatPrompt(
heartbeatPrompt: resolveHeartbeatPrompt( params.config?.agent?.heartbeat?.prompt,
params.config?.agent?.heartbeat?.prompt, ),
), runtimeInfo,
runtimeInfo, sandboxInfo,
sandboxInfo, toolNames: tools.map((tool) => tool.name),
toolNames: tools.map((tool) => tool.name), modelAliasLines: buildModelAliasLines(params.config),
modelAliasLines: buildModelAliasLines(params.config), userTimezone,
userTimezone, userTime,
userTime,
}),
contextFiles,
skills: promptSkills,
cwd: effectiveWorkspace,
tools,
}); });
const systemPrompt = createSystemPromptAppender(appendPrompt);
// Pre-warm session file to bring it into OS page cache // Pre-warm session file to bring it into OS page cache
await prewarmSessionFile(params.sessionFile); await prewarmSessionFile(params.sessionFile);
@@ -1100,28 +1104,23 @@ export async function runEmbeddedPiAgent(params: {
params.config?.agent?.userTimezone, params.config?.agent?.userTimezone,
); );
const userTime = formatUserTime(new Date(), userTimezone); const userTime = formatUserTime(new Date(), userTimezone);
const systemPrompt = buildSystemPrompt({ const appendPrompt = buildAgentSystemPromptAppend({
appendPrompt: buildAgentSystemPromptAppend({ workspaceDir: effectiveWorkspace,
workspaceDir: effectiveWorkspace, defaultThinkLevel: thinkLevel,
defaultThinkLevel: thinkLevel, extraSystemPrompt: params.extraSystemPrompt,
extraSystemPrompt: params.extraSystemPrompt, ownerNumbers: params.ownerNumbers,
ownerNumbers: params.ownerNumbers, reasoningTagHint,
reasoningTagHint, heartbeatPrompt: resolveHeartbeatPrompt(
heartbeatPrompt: resolveHeartbeatPrompt( params.config?.agent?.heartbeat?.prompt,
params.config?.agent?.heartbeat?.prompt, ),
), runtimeInfo,
runtimeInfo, sandboxInfo,
sandboxInfo, toolNames: tools.map((tool) => tool.name),
toolNames: tools.map((tool) => tool.name), modelAliasLines: buildModelAliasLines(params.config),
modelAliasLines: buildModelAliasLines(params.config), userTimezone,
userTimezone, userTime,
userTime,
}),
contextFiles,
skills: promptSkills,
cwd: effectiveWorkspace,
tools,
}); });
const systemPrompt = createSystemPromptAppender(appendPrompt);
// Pre-warm session file to bring it into OS page cache // Pre-warm session file to bring it into OS page cache
await prewarmSessionFile(params.sessionFile); await prewarmSessionFile(params.sessionFile);