Skip to content

fix: dedupe skill bodies & skip synthetic skill messages in prompt cache#231

Merged
danny-avila merged 2 commits into
mainfrom
claude/skill-prime-dedupe
Jun 9, 2026
Merged

fix: dedupe skill bodies & skip synthetic skill messages in prompt cache#231
danny-avila merged 2 commits into
mainfrom
claude/skill-prime-dedupe

Conversation

@danny-avila

Copy link
Copy Markdown
Owner

Problem

When a skill is primed fresh on the current turn (LibreChat splices the SKILL.md body in just before the latest user message) and the same skill also appears earlier in history as a skill tool_call, formatAgentMessages reconstructs the historical body into a synthetic HumanMessage — so the body appears twice. Prompt-cache markers could then land on those synthetic skill-prime messages, pinning the cache to a duplicated/volatile prefix.

Changes

formatAgentMessagesskipSkillBodyNames

  • New optional skipSkillBodyNames?: Set<string> on FormatAgentMessagesOptions.
  • Historical skill tool_calls whose name is in the set are not reconstructed into a HumanMessage. Names absent from the set still reconstruct, preserving sticky re-priming across turns.
  • The caller passes the set of names primed fresh this turn (manual + always-apply).

prompt cache — skip synthetic skill/meta messages

  • New isSyntheticMetaMessage(message) helper: additional_kwargs.isMeta === true || additional_kwargs.source === 'skill'.
  • addCacheControl (Anthropic / OpenRouter) and addBedrockCacheControl no longer add fresh cache markers to synthetic skill/meta messages; the marker moves to the next real user message.
  • Stale markers are still stripped from those messages — only adding is suppressed. Tool-definition cache logic is unchanged.

Tests

  • formatAgentMessages.skills.test.ts: reconstruct skip when name is in skipSkillBodyNames, selective skip, empty-set no-op, non-tools-filtering path.
  • cache.test.ts: Anthropic + Bedrock skip synthetic skill HumanMessage, marker moves to the next real message, stale marker stripped without re-add, source-only detection.

78 tests pass across both files; lint + typecheck clean (only pre-existing unrelated warnings).

Companion

Paired with the LibreChat change that passes skipSkillBodyNames (manual + always-apply prime names) into formatAgentMessages.

formatAgentMessages: add optional `skipSkillBodyNames` so historical `skill`
tool_calls are not reconstructed into a HumanMessage when the same skill is
already primed fresh this turn — preventing the SKILL.md body from being
injected twice. Names absent from the set still reconstruct from history.

cache: add `isSyntheticMetaMessage` and skip those messages when anchoring
fresh prompt-cache markers in addCacheControl (Anthropic/OpenRouter) and
addBedrockCacheControl. Stale markers are still stripped; the marker moves to
the next real user message instead of pinning to a volatile skill prefix.

Adds tests for both behaviors.
@danny-avila

Copy link
Copy Markdown
Owner Author

Companion (consumer) PR: danny-avila/LibreChat#13610

@danny-avila

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5954edbdd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/messages/cache.ts
const needsCacheAdd =
userMessagesModified < 2 &&
isUserMessage &&
!isSyntheticMetaMessage(originalMessage) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply synthetic-message skip to stable-prefix cache path

This guard only covers the direct addCacheControl path; the stable-prefix path used by AgentContext.buildBodyWithPromptCacheDynamicTail still calls addCacheControlToStablePrefixMessages, whose fallback delegates to addCacheControlToRecentMessages without checking isSyntheticMetaMessage. When prompt caching has a dynamic tail (dynamic instructions/summary) and the stable prefix contains a reconstructed skill HumanMessage after a skill-only assistant tool call, the assistant-only pass can add no marker and the fallback will still cache-anchor the synthetic SKILL.md body, so the duplicated/volatile prefix this change is trying to avoid remains possible.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch — fixed in ddd1ddf. The guard now lives in addCacheControlToRecentMessages (the shared backend for addCacheControlToStablePrefixMessages), so both the assistant pass and the cacheable fallback skip synthetic skill/meta messages. Added a regression test that reproduces the dynamic-tail scenario: a skill-only (text-less) assistant tool call followed by a reconstructed skill HumanMessage in the stable prefix — the fallback now anchors the real user message instead of the SKILL.md body.

Addresses Codex P2: addCacheControlToStablePrefixMessages (the dynamic-tail
path used by AgentContext) delegates to addCacheControlToRecentMessages, whose
cacheable fallback did not check isSyntheticMetaMessage. When the stable prefix
held a reconstructed skill HumanMessage after a skill-only (text-less) assistant
tool call, the fallback could cache-anchor the synthetic SKILL.md body.

Guard canAddCache with !isSyntheticMetaMessage so both the assistant pass and
the cacheable fallback skip synthetic skill/meta messages. Adds a regression
test for the stable-prefix fallback.
@danny-avila

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danny-avila danny-avila merged commit 00ff0d5 into main Jun 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant