( agentId?: string, )
| 1492 | * without leaking skills from other agent contexts. |
| 1493 | */ |
| 1494 | export function createSkillAttachmentIfNeeded( |
| 1495 | agentId?: string, |
| 1496 | ): AttachmentMessage | null { |
| 1497 | const invokedSkills = getInvokedSkillsForAgent(agentId) |
| 1498 | |
| 1499 | if (invokedSkills.size === 0) { |
| 1500 | return null |
| 1501 | } |
| 1502 | |
| 1503 | // Sorted most-recent-first so budget pressure drops the least-relevant skills. |
| 1504 | // Per-skill truncation keeps the head of each file (where setup/usage |
| 1505 | // instructions typically live) rather than dropping whole skills. |
| 1506 | let usedTokens = 0 |
| 1507 | const skills = Array.from(invokedSkills.values()) |
| 1508 | .sort((a, b) => b.invokedAt - a.invokedAt) |
| 1509 | .map(skill => ({ |
| 1510 | name: skill.skillName, |
| 1511 | path: skill.skillPath, |
| 1512 | content: truncateToTokens( |
| 1513 | skill.content, |
| 1514 | POST_COMPACT_MAX_TOKENS_PER_SKILL, |
| 1515 | ), |
| 1516 | })) |
| 1517 | .filter(skill => { |
| 1518 | const tokens = roughTokenCountEstimation(skill.content) |
| 1519 | if (usedTokens + tokens > POST_COMPACT_SKILLS_TOKEN_BUDGET) { |
| 1520 | return false |
| 1521 | } |
| 1522 | usedTokens += tokens |
| 1523 | return true |
| 1524 | }) |
| 1525 | |
| 1526 | if (skills.length === 0) { |
| 1527 | return null |
| 1528 | } |
| 1529 | |
| 1530 | return createAttachmentMessage({ |
| 1531 | type: 'invoked_skills', |
| 1532 | skills, |
| 1533 | }) |
| 1534 | } |
| 1535 | |
| 1536 | /** |
| 1537 | * Creates a plan_mode attachment if the user is currently in plan mode. |
no test coverage detected