diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f369f55d33..34afa1bc61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,10 @@ Docs: https://docs.openclaw.ai - macOS/exec approvals: harden exec-host request HMAC verification to use a timing-safe compare and keep malformed or truncated signatures fail-closed in focused IPC auth coverage. - Gateway/exec approvals: surface requested env override keys in gateway-host approval prompts so operators can review surviving env context without inheriting noisy base host env. +### Fixes + +- Agents/bootstrap warnings: move bootstrap truncation warnings out of the system prompt and into the per-turn prompt body so prompt-cache reuse stays stable when truncation warnings appear or disappear. (#48753) Thanks @scoootscooob and @obviyus. + ## 2026.3.13 ### Changes diff --git a/src/agents/bootstrap-budget.test.ts b/src/agents/bootstrap-budget.test.ts index bee7a2d9036..a4d65cc964c 100644 --- a/src/agents/bootstrap-budget.test.ts +++ b/src/agents/bootstrap-budget.test.ts @@ -6,8 +6,10 @@ import { buildBootstrapTruncationReportMeta, buildBootstrapTruncationSignature, formatBootstrapTruncationWarningLines, + prependBootstrapPromptWarning, resolveBootstrapWarningSignaturesSeen, } from "./bootstrap-budget.js"; +import { buildAgentSystemPrompt } from "./system-prompt.js"; import type { WorkspaceBootstrapFile } from "./workspace.js"; describe("buildBootstrapInjectionStats", () => { @@ -104,6 +106,34 @@ describe("analyzeBootstrapBudget", () => { }); describe("bootstrap prompt warnings", () => { + it("prepends warning details to the turn prompt instead of mutating the system prompt", () => { + const prompt = prependBootstrapPromptWarning("Please continue.", [ + "AGENTS.md: 200 raw -> 0 injected", + ]); + expect(prompt).toContain("[Bootstrap truncation warning]"); + expect(prompt).toContain("Treat Project Context as partial"); + expect(prompt).toContain("- AGENTS.md: 200 raw -> 0 injected"); + expect(prompt).toContain("Please continue."); + }); + + it("preserves raw prompt whitespace when prepending warning details", () => { + const prompt = prependBootstrapPromptWarning(" indented\nkeep tail ", [ + "AGENTS.md: 200 raw -> 0 injected", + ]); + + expect(prompt.endsWith(" indented\nkeep tail ")).toBe(true); + }); + + it("preserves exact heartbeat prompts without warning prefixes", () => { + const heartbeatPrompt = "Read HEARTBEAT.md. Reply HEARTBEAT_OK."; + + expect( + prependBootstrapPromptWarning(heartbeatPrompt, ["AGENTS.md: 200 raw -> 0 injected"], { + preserveExactPrompt: heartbeatPrompt, + }), + ).toBe(heartbeatPrompt); + }); + it("resolves seen signatures from report history or legacy single signature", () => { expect( resolveBootstrapWarningSignaturesSeen({ @@ -394,4 +424,35 @@ describe("bootstrap prompt warnings", () => { expect(meta.promptWarningSignature).toBeTruthy(); expect(meta.warningSignaturesSeen?.length).toBeGreaterThan(0); }); + + it("improves cache-relevant system prompt stability versus legacy warning injection", () => { + const contextFiles = [{ path: "AGENTS.md", content: "Follow AGENTS guidance." }]; + const warningLines = ["AGENTS.md: 200 raw -> 0 injected"]; + const stableSystemPrompt = buildAgentSystemPrompt({ + workspaceDir: "/tmp/openclaw", + contextFiles, + }); + const optimizedTurns = [stableSystemPrompt, stableSystemPrompt, stableSystemPrompt]; + const injectLegacyWarning = (prompt: string, lines: string[]) => { + const warningBlock = [ + "⚠ Bootstrap truncation warning:", + ...lines.map((line) => `- ${line}`), + "", + ].join("\n"); + return prompt.replace("## AGENTS.md", `${warningBlock}## AGENTS.md`); + }; + const legacyTurns = [ + injectLegacyWarning(optimizedTurns[0] ?? "", warningLines), + optimizedTurns[1] ?? "", + injectLegacyWarning(optimizedTurns[2] ?? "", warningLines), + ]; + const cacheHitRate = (turns: string[]) => { + const hits = turns.slice(1).filter((turn, index) => turn === turns[index]).length; + return hits / Math.max(1, turns.length - 1); + }; + + expect(cacheHitRate(legacyTurns)).toBe(0); + expect(cacheHitRate(optimizedTurns)).toBe(1); + expect(optimizedTurns[0]).not.toContain("⚠ Bootstrap truncation warning:"); + }); }); diff --git a/src/agents/bootstrap-budget.ts b/src/agents/bootstrap-budget.ts index ddfd4fb5d06..4d5c3ff6f58 100644 --- a/src/agents/bootstrap-budget.ts +++ b/src/agents/bootstrap-budget.ts @@ -330,6 +330,29 @@ export function buildBootstrapPromptWarning(params: { }; } +export function prependBootstrapPromptWarning( + prompt: string, + warningLines?: string[], + options?: { + preserveExactPrompt?: string; + }, +): string { + const normalizedLines = (warningLines ?? []).map((line) => line.trim()).filter(Boolean); + if (normalizedLines.length === 0) { + return prompt; + } + if (options?.preserveExactPrompt && prompt === options.preserveExactPrompt) { + return prompt; + } + const warningBlock = [ + "[Bootstrap truncation warning]", + "Some workspace bootstrap files were truncated before injection.", + "Treat Project Context as partial and read the relevant files directly if details seem missing.", + ...normalizedLines.map((line) => `- ${line}`), + ].join("\n"); + return prompt ? `${warningBlock}\n\n${prompt}` : warningBlock; +} + export function buildBootstrapTruncationReportMeta(params: { analysis: BootstrapBudgetAnalysis; warningMode: BootstrapPromptWarningMode; diff --git a/src/agents/cli-runner.test.ts b/src/agents/cli-runner.test.ts index ec1b0b09ac8..e77ac021fd7 100644 --- a/src/agents/cli-runner.test.ts +++ b/src/agents/cli-runner.test.ts @@ -5,10 +5,25 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { runCliAgent } from "./cli-runner.js"; import { resolveCliNoOutputTimeoutMs } from "./cli-runner/helpers.js"; +import type { EmbeddedContextFile } from "./pi-embedded-helpers.js"; +import type { WorkspaceBootstrapFile } from "./workspace.js"; const supervisorSpawnMock = vi.fn(); const enqueueSystemEventMock = vi.fn(); const requestHeartbeatNowMock = vi.fn(); +const hoisted = vi.hoisted(() => { + type BootstrapContext = { + bootstrapFiles: WorkspaceBootstrapFile[]; + contextFiles: EmbeddedContextFile[]; + }; + + return { + resolveBootstrapContextForRunMock: vi.fn<() => Promise>(async () => ({ + bootstrapFiles: [], + contextFiles: [], + })), + }; +}); vi.mock("../process/supervisor/index.js", () => ({ getProcessSupervisor: () => ({ @@ -28,6 +43,11 @@ vi.mock("../infra/heartbeat-wake.js", () => ({ requestHeartbeatNow: (...args: unknown[]) => requestHeartbeatNowMock(...args), })); +vi.mock("./bootstrap-files.js", () => ({ + makeBootstrapWarn: () => () => {}, + resolveBootstrapContextForRun: hoisted.resolveBootstrapContextForRunMock, +})); + type MockRunExit = { reason: | "manual-cancel" @@ -61,6 +81,10 @@ describe("runCliAgent with process supervisor", () => { supervisorSpawnMock.mockClear(); enqueueSystemEventMock.mockClear(); requestHeartbeatNowMock.mockClear(); + hoisted.resolveBootstrapContextForRunMock.mockReset().mockResolvedValue({ + bootstrapFiles: [], + contextFiles: [], + }); }); it("runs CLI through supervisor and returns payload", async () => { @@ -107,6 +131,62 @@ describe("runCliAgent with process supervisor", () => { expect(input.scopeKey).toContain("thread-123"); }); + it("prepends bootstrap warnings to the CLI prompt body", async () => { + supervisorSpawnMock.mockResolvedValueOnce( + createManagedRun({ + reason: "exit", + exitCode: 0, + exitSignal: null, + durationMs: 50, + stdout: "ok", + stderr: "", + timedOut: false, + noOutputTimedOut: false, + }), + ); + hoisted.resolveBootstrapContextForRunMock.mockResolvedValueOnce({ + bootstrapFiles: [ + { + name: "AGENTS.md", + path: "/tmp/AGENTS.md", + content: "A".repeat(200), + missing: false, + }, + ], + contextFiles: [{ path: "AGENTS.md", content: "A".repeat(20) }], + }); + + await runCliAgent({ + sessionId: "s1", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp", + config: { + agents: { + defaults: { + bootstrapMaxChars: 50, + bootstrapTotalMaxChars: 50, + }, + }, + } satisfies OpenClawConfig, + prompt: "hi", + provider: "codex-cli", + model: "gpt-5.2-codex", + timeoutMs: 1_000, + runId: "run-warning", + cliSessionId: "thread-123", + }); + + const input = supervisorSpawnMock.mock.calls[0]?.[0] as { + argv?: string[]; + input?: string; + }; + const promptCarrier = [input.input ?? "", ...(input.argv ?? [])].join("\n"); + + expect(promptCarrier).toContain("[Bootstrap truncation warning]"); + expect(promptCarrier).toContain("- AGENTS.md: 200 raw -> 20 injected"); + expect(promptCarrier).toContain("hi"); + }); + it("fails with timeout when no-output watchdog trips", async () => { supervisorSpawnMock.mockResolvedValueOnce( createManagedRun({ diff --git a/src/agents/cli-runner.ts b/src/agents/cli-runner.ts index 93cebc6dd14..9056668e087 100644 --- a/src/agents/cli-runner.ts +++ b/src/agents/cli-runner.ts @@ -15,6 +15,7 @@ import { buildBootstrapInjectionStats, buildBootstrapPromptWarning, buildBootstrapTruncationReportMeta, + prependBootstrapPromptWarning, } from "./bootstrap-budget.js"; import { makeBootstrapWarn, resolveBootstrapContextForRun } from "./bootstrap-files.js"; import { resolveCliBackendConfig } from "./cli-backends.js"; @@ -162,7 +163,6 @@ export async function runCliAgent(params: { docsPath: docsPath ?? undefined, tools: [], contextFiles, - bootstrapTruncationWarningLines: bootstrapPromptWarning.lines, modelDisplay, agentId: sessionAgentId, }); @@ -218,7 +218,9 @@ export async function runCliAgent(params: { let imagePaths: string[] | undefined; let cleanupImages: (() => Promise) | undefined; - let prompt = params.prompt; + let prompt = prependBootstrapPromptWarning(params.prompt, bootstrapPromptWarning.lines, { + preserveExactPrompt: heartbeatPrompt, + }); if (params.images && params.images.length > 0) { const imagePayload = await writeCliImages(params.images); imagePaths = imagePayload.paths; diff --git a/src/agents/cli-runner/helpers.ts b/src/agents/cli-runner/helpers.ts index 7f0598cfaab..96ec35540be 100644 --- a/src/agents/cli-runner/helpers.ts +++ b/src/agents/cli-runner/helpers.ts @@ -48,7 +48,6 @@ export function buildSystemPrompt(params: { docsPath?: string; tools: AgentTool[]; contextFiles?: EmbeddedContextFile[]; - bootstrapTruncationWarningLines?: string[]; modelDisplay: string; agentId?: string; }) { @@ -92,7 +91,6 @@ export function buildSystemPrompt(params: { userTime, userTimeFormat, contextFiles: params.contextFiles, - bootstrapTruncationWarningLines: params.bootstrapTruncationWarningLines, ttsHint, memoryCitationsMode: params.config?.memory?.citations, }); diff --git a/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test.ts b/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test.ts index e67bb20d88d..fa2bb58fbbc 100644 --- a/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test.ts +++ b/src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test.ts @@ -18,16 +18,27 @@ import type { IngestBatchResult, IngestResult, } from "../../../context-engine/types.js"; +import type { EmbeddedContextFile } from "../../pi-embedded-helpers.js"; import { createHostSandboxFsBridge } from "../../test-helpers/host-sandbox-fs-bridge.js"; import { createPiToolsSandboxContext } from "../../test-helpers/pi-tools-sandbox-context.js"; +import type { WorkspaceBootstrapFile } from "../../workspace.js"; const hoisted = vi.hoisted(() => { + type BootstrapContext = { + bootstrapFiles: WorkspaceBootstrapFile[]; + contextFiles: EmbeddedContextFile[]; + }; const spawnSubagentDirectMock = vi.fn(); const createAgentSessionMock = vi.fn(); const sessionManagerOpenMock = vi.fn(); const resolveSandboxContextMock = vi.fn(); const subscribeEmbeddedPiSessionMock = vi.fn(); const acquireSessionWriteLockMock = vi.fn(); + const resolveBootstrapContextForRunMock = vi.fn<() => Promise>(async () => ({ + bootstrapFiles: [], + contextFiles: [], + })); + const getGlobalHookRunnerMock = vi.fn<() => unknown>(() => undefined); const sessionManager = { getLeafEntry: vi.fn(() => null), branch: vi.fn(), @@ -42,6 +53,8 @@ const hoisted = vi.hoisted(() => { resolveSandboxContextMock, subscribeEmbeddedPiSessionMock, acquireSessionWriteLockMock, + resolveBootstrapContextForRunMock, + getGlobalHookRunnerMock, sessionManager, }; }); @@ -80,7 +93,7 @@ vi.mock("../../pi-embedded-subscribe.js", () => ({ })); vi.mock("../../../plugins/hook-runner-global.js", () => ({ - getGlobalHookRunner: () => undefined, + getGlobalHookRunner: hoisted.getGlobalHookRunnerMock, })); vi.mock("../../../infra/machine-name.js", () => ({ @@ -94,7 +107,7 @@ vi.mock("../../../infra/net/undici-global-dispatcher.js", () => ({ vi.mock("../../bootstrap-files.js", () => ({ makeBootstrapWarn: () => () => {}, - resolveBootstrapContextForRun: async () => ({ bootstrapFiles: [], contextFiles: [] }), + resolveBootstrapContextForRun: hoisted.resolveBootstrapContextForRunMock, })); vi.mock("../../skills.js", () => ({ @@ -269,6 +282,11 @@ function resetEmbeddedAttemptHarness( hoisted.acquireSessionWriteLockMock.mockReset().mockResolvedValue({ release: async () => {}, }); + hoisted.resolveBootstrapContextForRunMock.mockReset().mockResolvedValue({ + bootstrapFiles: [], + contextFiles: [], + }); + hoisted.getGlobalHookRunnerMock.mockReset().mockReturnValue(undefined); hoisted.sessionManager.getLeafEntry.mockReset().mockReturnValue(null); hoisted.sessionManager.branch.mockReset(); hoisted.sessionManager.resetLeaf.mockReset(); @@ -291,7 +309,11 @@ async function cleanupTempPaths(tempPaths: string[]) { } function createDefaultEmbeddedSession(params?: { - prompt?: (session: MutableSession) => Promise; + prompt?: ( + session: MutableSession, + prompt: string, + options?: { images?: unknown[] }, + ) => Promise; }): MutableSession { const session: MutableSession = { sessionId: "embedded-session", @@ -303,9 +325,9 @@ function createDefaultEmbeddedSession(params?: { session.messages = [...messages]; }, }, - prompt: async () => { + prompt: async (prompt, options) => { if (params?.prompt) { - await params.prompt(session); + await params.prompt(session, prompt, options); return; } session.messages = [ @@ -450,6 +472,90 @@ describe("runEmbeddedAttempt sessions_spawn workspace inheritance", () => { }); }); +describe("runEmbeddedAttempt bootstrap warning prompt assembly", () => { + const tempPaths: string[] = []; + + beforeEach(() => { + resetEmbeddedAttemptHarness({ + subscribeImpl: createSubscriptionMock, + }); + }); + + afterEach(async () => { + await cleanupTempPaths(tempPaths); + }); + + it("keeps bootstrap warnings in the sent prompt after hook prepend context", async () => { + const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-warning-workspace-")); + const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-warning-agent-dir-")); + const sessionFile = path.join(workspaceDir, "session.jsonl"); + tempPaths.push(workspaceDir, agentDir); + await fs.writeFile(sessionFile, "", "utf8"); + + hoisted.resolveBootstrapContextForRunMock.mockResolvedValue({ + bootstrapFiles: [ + { + name: "AGENTS.md", + path: path.join(workspaceDir, "AGENTS.md"), + content: "A".repeat(200), + missing: false, + }, + ], + contextFiles: [{ path: "AGENTS.md", content: "A".repeat(20) }], + }); + hoisted.getGlobalHookRunnerMock.mockReturnValue({ + hasHooks: (hookName: string) => hookName === "before_prompt_build", + runBeforePromptBuild: async () => ({ prependContext: "hook context" }), + }); + + let seenPrompt = ""; + hoisted.createAgentSessionMock.mockImplementation(async () => ({ + session: createDefaultEmbeddedSession({ + prompt: async (session, prompt) => { + seenPrompt = prompt; + session.messages = [ + ...session.messages, + { role: "assistant", content: "done", timestamp: 2 }, + ]; + }, + }), + })); + + const result = await runEmbeddedAttempt({ + sessionId: "embedded-session", + sessionKey: "agent:main:main", + sessionFile, + workspaceDir, + agentDir, + config: { + agents: { + defaults: { + bootstrapMaxChars: 50, + bootstrapTotalMaxChars: 50, + }, + }, + }, + prompt: "hello", + timeoutMs: 10_000, + runId: "run-warning", + provider: "openai", + modelId: "gpt-test", + model: testModel, + authStorage: {} as AuthStorage, + modelRegistry: {} as ModelRegistry, + thinkLevel: "off", + senderIsOwner: true, + disableMessageTool: true, + }); + + expect(result.promptError).toBeNull(); + expect(seenPrompt).toContain("hook context"); + expect(seenPrompt).toContain("[Bootstrap truncation warning]"); + expect(seenPrompt).toContain("- AGENTS.md: 200 raw -> 20 injected"); + expect(seenPrompt).toContain("hello"); + }); +}); + describe("runEmbeddedAttempt cache-ttl tracking after compaction", () => { const tempPaths: string[] = []; diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index dc9df12865d..73b7d0fbff6 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -41,6 +41,7 @@ import { buildBootstrapPromptWarning, buildBootstrapTruncationReportMeta, buildBootstrapInjectionStats, + prependBootstrapPromptWarning, } from "../../bootstrap-budget.js"; import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../../bootstrap-files.js"; import { createCacheTrace } from "../../cache-trace.js"; @@ -1665,6 +1666,9 @@ export async function runEmbeddedAttempt( }); const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined; const ownerDisplay = resolveOwnerDisplaySetting(params.config); + const heartbeatPrompt = isDefaultAgent + ? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt) + : undefined; const appendPrompt = buildEmbeddedSystemPrompt({ workspaceDir: effectiveWorkspace, @@ -1675,9 +1679,7 @@ export async function runEmbeddedAttempt( ownerDisplay: ownerDisplay.ownerDisplay, ownerDisplaySecret: ownerDisplay.ownerDisplaySecret, reasoningTagHint, - heartbeatPrompt: isDefaultAgent - ? resolveHeartbeatPrompt(params.config?.agents?.defaults?.heartbeat?.prompt) - : undefined, + heartbeatPrompt, skillsPrompt, docsPath: docsPath ?? undefined, ttsHint, @@ -1694,7 +1696,6 @@ export async function runEmbeddedAttempt( userTime, userTimeFormat, contextFiles, - bootstrapTruncationWarningLines: bootstrapPromptWarning.lines, memoryCitationsMode: params.config?.memory?.citations, }); const systemPromptReport = buildSystemPromptReport({ @@ -2378,7 +2379,13 @@ export async function runEmbeddedAttempt( // Run before_prompt_build hooks to allow plugins to inject prompt context. // Legacy compatibility: before_agent_start is also checked for context fields. - let effectivePrompt = params.prompt; + let effectivePrompt = prependBootstrapPromptWarning( + params.prompt, + bootstrapPromptWarning.lines, + { + preserveExactPrompt: heartbeatPrompt, + }, + ); const hookCtx = { agentId: hookAgentId, sessionKey: params.sessionKey, @@ -2397,7 +2404,7 @@ export async function runEmbeddedAttempt( }); { if (hookResult?.prependContext) { - effectivePrompt = `${hookResult.prependContext}\n\n${params.prompt}`; + effectivePrompt = `${hookResult.prependContext}\n\n${effectivePrompt}`; log.debug( `hooks: prepended context to prompt (${hookResult.prependContext.length} chars)`, ); diff --git a/src/agents/pi-embedded-runner/system-prompt.ts b/src/agents/pi-embedded-runner/system-prompt.ts index ac2662f127f..ef246d1af23 100644 --- a/src/agents/pi-embedded-runner/system-prompt.ts +++ b/src/agents/pi-embedded-runner/system-prompt.ts @@ -51,7 +51,6 @@ export function buildEmbeddedSystemPrompt(params: { userTime?: string; userTimeFormat?: ResolvedTimeFormat; contextFiles?: EmbeddedContextFile[]; - bootstrapTruncationWarningLines?: string[]; memoryCitationsMode?: MemoryCitationsMode; }): string { return buildAgentSystemPrompt({ @@ -81,7 +80,6 @@ export function buildEmbeddedSystemPrompt(params: { userTime: params.userTime, userTimeFormat: params.userTimeFormat, contextFiles: params.contextFiles, - bootstrapTruncationWarningLines: params.bootstrapTruncationWarningLines, memoryCitationsMode: params.memoryCitationsMode, }); } diff --git a/src/agents/system-prompt.test.ts b/src/agents/system-prompt.test.ts index 3877f6fed21..b20a9524941 100644 --- a/src/agents/system-prompt.test.ts +++ b/src/agents/system-prompt.test.ts @@ -534,16 +534,13 @@ describe("buildAgentSystemPrompt", () => { ); }); - it("renders bootstrap truncation warning even when no context files are injected", () => { + it("omits project context when no context files are injected", () => { const prompt = buildAgentSystemPrompt({ workspaceDir: "/tmp/openclaw", - bootstrapTruncationWarningLines: ["AGENTS.md: 200 raw -> 0 injected"], contextFiles: [], }); - expect(prompt).toContain("# Project Context"); - expect(prompt).toContain("⚠ Bootstrap truncation warning:"); - expect(prompt).toContain("- AGENTS.md: 200 raw -> 0 injected"); + expect(prompt).not.toContain("# Project Context"); }); it("summarizes the message tool when available", () => { diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 848222b7880..5f4ee932bd7 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -202,7 +202,6 @@ export function buildAgentSystemPrompt(params: { userTime?: string; userTimeFormat?: ResolvedTimeFormat; contextFiles?: EmbeddedContextFile[]; - bootstrapTruncationWarningLines?: string[]; skillsPrompt?: string; heartbeatPrompt?: string; docsPath?: string; @@ -614,13 +613,10 @@ export function buildAgentSystemPrompt(params: { } const contextFiles = params.contextFiles ?? []; - const bootstrapTruncationWarningLines = (params.bootstrapTruncationWarningLines ?? []).filter( - (line) => line.trim().length > 0, - ); const validContextFiles = contextFiles.filter( (file) => typeof file.path === "string" && file.path.trim().length > 0, ); - if (validContextFiles.length > 0 || bootstrapTruncationWarningLines.length > 0) { + if (validContextFiles.length > 0) { lines.push("# Project Context", ""); if (validContextFiles.length > 0) { const hasSoulFile = validContextFiles.some((file) => { @@ -636,13 +632,6 @@ export function buildAgentSystemPrompt(params: { } lines.push(""); } - if (bootstrapTruncationWarningLines.length > 0) { - lines.push("⚠ Bootstrap truncation warning:"); - for (const warningLine of bootstrapTruncationWarningLines) { - lines.push(`- ${warningLine}`); - } - lines.push(""); - } for (const file of validContextFiles) { lines.push(`## ${file.path}`, "", file.content, ""); }