Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.

fix(llm): preserve prompt cache stability with 2-part system message layout#37

Merged
devinoldenburg merged 2 commits into
mainfrom
fix/prompt-prefix-cache-stability
Jun 1, 2026
Merged

fix(llm): preserve prompt cache stability with 2-part system message layout#37
devinoldenburg merged 2 commits into
mainfrom
fix/prompt-prefix-cache-stability

Conversation

@devinoldenburg
Copy link
Copy Markdown
Collaborator

Separates LLM system messages: header + static block intact for caching, dynamic tail compacted. 63/63 tests pass, 0 lint errors.

Codeplane Agent and others added 2 commits June 1, 2026 13:15
Each scheduled task now has its own list of MCP servers that can be
toggled on or off independently in the web UI editor dialog. Default
is all off. When enabled servers are set, only those MCP tools are
available to the task's runs. When null/empty (existing tasks), all
MCP servers remain available for backward compatibility.

- Add mcp_servers JSON column to cron_task table
- Add mcpServers field to Cron Task/CreateInput/UpdateInput schemas
- Pass enabled MCP servers to session metadata in the cron scheduler
- Filter MCP tools in prompt.ts based on session metadata
- Add MCP server toggle list with switches in cron editor dialog
- Add i18n translations for new MCP servers section

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…layout

Separate the provider/agent header (message 0) and the first static
context block (message 1) so LLM providers can cache prompt prefixes
across turns. Dynamic blocks (environment, tool availability, user
custom system) sit in position 2+ and get compacted safely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-Authored-By: codeplane-agent[bot] <287208015+codeplane-agent[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 1, 2026 13:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors how LLM system messages are assembled to improve prompt-prefix cache stability (by keeping stable “header + static block” segments intact), and introduces cron-task-level configuration for restricting which MCP servers/tools are available during scheduled runs.

Changes:

  • Restructures system prompt assembly/compaction in the LLM streaming path to preserve cacheable prefixes across turns.
  • Adds mcpServers to cron task schemas, routes, persistence, and the app client/types/UI.
  • Threads cron-task MCP server selection into session metadata and uses it to filter MCP tools during prompt tool resolution.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/codeplane/src/session/session.ts Extends session creation interface to accept metadata (used by cron runs).
packages/codeplane/src/session/prompt.ts Filters MCP tools based on session.metadata.enabledMcpServers.
packages/codeplane/src/session/llm.ts Splits/compacts system messages to keep cacheable prompt prefix segments stable.
packages/codeplane/src/server/routes/cron.ts Adds mcpServers to cron create route input schema.
packages/codeplane/src/cron/scheduler.ts Passes cron task MCP server selection into created session metadata.
packages/codeplane/src/cron/cron.ts Adds mcpServers to cron task schema, create/update inputs, and DB mapping.
packages/codeplane/src/cron/cron.sql.ts Adds mcp_servers JSON-text column to the cron task table schema.
packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/snapshot.json Drizzle snapshot reflecting the new cron task column.
packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/migration.sql Migration adding mcp_servers column to cron_task.
packages/app/src/utils/cron-client.ts Adds mcpServers to client task and create/update input types.
packages/app/src/pages/cron.tsx Adds MCP server selection UI and submits mcpServers in create/update flows.
packages/app/src/i18n/en.ts Adds UI strings for the MCP servers cron field.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +654 to +662
const sanitizeMcp = (s: string) => s.replace(/[^a-zA-Z0-9_-]/g, "_")
const enabledMcpServers = input.session.metadata?.enabledMcpServers as string[] | undefined
const mcpTools = enabledMcpServers?.length
? Object.fromEntries(
Object.entries(allMcpTools).filter(([key]) =>
enabledMcpServers.some((name) => key.startsWith(sanitizeMcp(name) + "_")),
),
)
: allMcpTools
Comment on lines +155 to +157
metadata: task.mcpServers?.length
? { enabledMcpServers: task.mcpServers }
: undefined,
Comment on lines 567 to 593
if (props.existing) {
const body: CronUpdateInput = {
name: store.name.trim(),
description: store.description.trim() || null,
prompt: store.prompt,
schedule,
timeoutMs: timeoutMs ?? null,
agent: store.agent.trim() || null,
model: store.model.trim() || null,
status: store.status,
mcpServers: store.mcpServers.length > 0 ? store.mcpServers : null,
}
await CronClient.update(conn, props.existing.id, body)
} else {
const body: CronCreateInput = {
...(props.project.id ? { projectID: props.project.id } : {}),
directory: props.project.worktree,
name: store.name.trim(),
description: store.description.trim() || undefined,
prompt: store.prompt,
schedule,
timeoutMs,
agent: store.agent.trim() || undefined,
model: store.model.trim() || undefined,
status: store.status,
mcpServers: store.mcpServers.length > 0 ? store.mcpServers : undefined,
}
Comment on lines 499 to 503
agent: e.agent ?? "",
model: e.model ?? "",
status: e.status,
mcpServers: e.mcpServers ?? [],
errors: {},
Comment on lines +87 to +89
"cron.field.mcpServers": "MCP servers",
"cron.field.mcpServers.empty": "No MCP servers configured. Add servers in Settings > MCP.",
"cron.field.mcpServers.help": "Only enabled MCP servers will be available to this task. By default all servers are off.",
Comment on lines 24 to 38
const CreateInput = z.object({
projectID: ProjectID.zod.optional(),
directory: z.string().optional(),
name: z.string(),
description: z.string().optional(),
prompt: z.string(),
agent: z.string().optional(),
model: z.string().optional(),
schedule: ScheduleInput,
timezone: z.string().optional(),
status: Cron.Status.zod.optional(),
timeoutMs: z.number().optional(),
maxRetries: z.number().optional(),
mcpServers: z.array(z.string()).optional(),
})
@devinoldenburg
Copy link
Copy Markdown
Collaborator Author

Review: LLM prompt cache stability + MCP toggle

Status: Merge candidate

All CI checks are green. The 2-part system message layout in llm.ts correctly preserves the provider/agent header and first static block for caching while compacting the dynamic tail. The MCP server toggle feature (identical to PR #36) is well-implemented with proper schema, migration, scheduler, and UI changes.

Critical dependency: This PR contains all changes from PR #36 plus the LLM cache fix. I'll be closing #36 as superseded and merging this one as the complete implementation.

@devinoldenburg devinoldenburg merged commit 2a631d5 into main Jun 1, 2026
12 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants