Skip to content

✂️ fix: Deduplicate Skill Bodies Across Fresh Primes and History#13610

Merged
danny-avila merged 1 commit into
devfrom
claude/festive-driscoll-1db3f7
Jun 9, 2026
Merged

✂️ fix: Deduplicate Skill Bodies Across Fresh Primes and History#13610
danny-avila merged 1 commit into
devfrom
claude/festive-driscoll-1db3f7

Conversation

@danny-avila

Copy link
Copy Markdown
Owner

Problem

A skill that is primed fresh this turn (manual $-popover or always-apply frontmatter) and also present in history as a skill tool_call had its SKILL.md body injected twice:

  1. injectSkillPrimes splices the fresh body in before the latest user message, and
  2. formatAgentMessages reconstructs the historical body from the skill tool_call.

Repro: turn 1 invoke a skill manually/model-side, turn 2 have the same skill active as always-apply → two SKILL.md bodies, and prompt-cache markers could pin to the duplicated synthetic prefix.

Changes

  • packages/api — new collectFreshSkillPrimeNames({ manualSkillPrimes, alwaysApplySkillPrimes }) helper returning the union of names primed this turn (TS, unit-tested).
  • api/server/controllers/agents/client.js — build that set and pass it as skipSkillBodyNames to formatAgentMessages for both the initialMessages and memoryMessages paths. A skill primed this turn reconstructs once (the fresh prime); names not primed this turn still reconstruct from history, preserving sticky manual re-priming. initialSessions / skill-file availability untouched.

Dependency

The skipSkillBodyNames option requires a build of @librechat/agents that supports it (companion PR on danny-avila/agents). The currently-published dist silently ignores the unknown option key until upgraded — safe to merge independently.

Tests

  • packages/api/src/agents/__tests__/skills.test.ts: collectFreshSkillPrimeNames union, manual+always-apply dedupe, empty/undefined inputs, one-sided inputs. 150 tests pass; lint + typecheck clean.

When a skill is primed fresh this turn (manual $-popover or always-apply) AND
also appears in history as a `skill` tool_call, its SKILL.md body was injected
twice — once by injectSkillPrimes and once reconstructed by formatAgentMessages.

- add `collectFreshSkillPrimeNames` helper (packages/api) — union of manual +
  always-apply prime names
- client.js: pass the set as `skipSkillBodyNames` to formatAgentMessages for
  both the initialMessages and memoryMessages paths so the body reconstructs
  once. Names not primed this turn still reconstruct (sticky manual re-prime).

Requires `@librechat/agents` with `skipSkillBodyNames` support; the published
dist silently ignores the unknown option until upgraded.
Copilot AI review requested due to automatic review settings June 9, 2026 03:48
@danny-avila

Copy link
Copy Markdown
Owner Author

Companion (SDK) PR: danny-avila/agents#231 — adds the skipSkillBodyNames option this PR relies on.

@danny-avila

Copy link
Copy Markdown
Owner Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 prevents duplicated SKILL.md body injection when the same skill is (a) primed fresh in the current turn (manual $ popover and/or always-apply frontmatter) and (b) also exists in history as a skill tool_call that would otherwise be reconstructed by formatAgentMessages.

Changes:

  • Add collectFreshSkillPrimeNames() helper to compute the union of skill names primed in the current turn (manual + always-apply).
  • Pass the resulting set as skipSkillBodyNames into formatAgentMessages so historical reconstruction doesn’t re-inject bodies already injected via fresh priming.
  • Add unit tests covering union, dedupe, and empty/undefined input handling for the helper.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/api/src/agents/skills.ts Adds collectFreshSkillPrimeNames() to compute the current-turn primed skill name set for dedupe behavior.
packages/api/src/agents/tests/skills.test.ts Adds unit tests for collectFreshSkillPrimeNames() covering union/dedupe/empty inputs.
api/server/controllers/agents/client.js Uses the helper and forwards skipSkillBodyNames to formatAgentMessages for both initial and memory formatting paths.

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

@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: d078b5f71d

ℹ️ 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".

? {
...(needsDeepSeekFormat ? { provider: Providers.DEEPSEEK } : {}),
...(freshSkillPrimeNames.size > 0
? { skipSkillBodyNames: freshSkillPrimeNames }

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 Update agents dependency for skipSkillBodyNames

In deployments built from this repo/lockfile, npm ci still installs @librechat/agents 3.2.31 (package-lock.json resolves that exact version), and the commit notes that version silently ignores skipSkillBodyNames. When a fresh always-apply/manual skill is also present in historical skill tool calls, this option therefore has no effect and the duplicate SKILL.md body problem remains until the companion agents build is actually pulled into the lockfile.

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.

Correct, and intentional — this is the cross-repo sequencing noted under Dependency in the PR description. The runtime behavior is gated on the companion SDK PR (danny-avila/agents#231), which adds the skipSkillBodyNames option. Until that ships and @librechat/agents is bumped in package.json + the lockfile, the published 3.2.31 dist safely ignores the unknown option (no regression, no effect).

Sequencing: merge & release agents#231 → bump @librechat/agents here → this PR's dedupe takes effect. I'll push the dependency bump as a follow-up commit once the SDK version is published; happy to convert this to a draft until then if you'd prefer it not merge ahead of the bump.

@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. Swish!

ℹ️ 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 changed the title fix: prevent duplicate skill bodies for always-apply + historical primes ✂️ fix: Deduplicate Skill Bodies Across Fresh Primes and History Jun 9, 2026
@danny-avila danny-avila changed the base branch from main to dev June 9, 2026 21:06
@danny-avila danny-avila merged commit 793cbd4 into dev Jun 9, 2026
20 of 21 checks passed
@danny-avila danny-avila deleted the claude/festive-driscoll-1db3f7 branch June 9, 2026 21:16
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.

2 participants