fix: auto-create missing skills blocks when resuming older agents (#559)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-01-15 19:18:37 -08:00
committed by GitHub
parent 4f9e7e406a
commit c6d43a1a25
3 changed files with 77 additions and 2 deletions

View File

@@ -144,3 +144,66 @@ export async function getDefaultMemoryBlocks(): Promise<CreateBlock[]> {
}
return cachedMemoryBlocks;
}
/**
* Ensure an agent has the required skills blocks (skills, loaded_skills).
* If missing, creates them with default values and attaches to the agent.
* This is needed for backwards compatibility with agents created before skills were added.
*
* @param agentId - The agent ID to check/update
* @returns Array of block labels that were created (empty if all existed)
*/
export async function ensureSkillsBlocks(agentId: string): Promise<string[]> {
const { getClient } = await import("./client");
const client = await getClient();
// Get current blocks on the agent
// Response may be paginated or an array depending on SDK version
const blocksResponse = await client.agents.blocks.list(agentId);
const blocks = Array.isArray(blocksResponse)
? blocksResponse
: (blocksResponse as { items?: Array<{ label?: string }> }).items || [];
const existingLabels = new Set(blocks.map((b) => b.label));
const createdLabels: string[] = [];
// Check each required skills block
for (const label of ISOLATED_BLOCK_LABELS) {
if (existingLabels.has(label)) {
continue;
}
// Load the default block definition from .mdx
const content = MEMORY_PROMPTS[`${label}.mdx`];
if (!content) {
console.warn(`Missing embedded prompt file for ${label}.mdx`);
continue;
}
const { frontmatter, body } = parseMdxFrontmatter(content);
const blockData: CreateBlock = {
label,
value: body,
read_only: true, // Skills blocks are read-only (managed by Skill tool)
};
if (frontmatter.description) {
blockData.description = frontmatter.description;
}
if (frontmatter.limit) {
const limit = parseInt(frontmatter.limit, 10);
if (!Number.isNaN(limit) && limit > 0) {
blockData.limit = limit;
}
}
// Create the block and attach to agent
const createdBlock = await client.blocks.create(blockData);
await client.agents.blocks.attach(createdBlock.id, { agent_id: agentId });
createdLabels.push(label);
}
return createdLabels;
}

View File

@@ -16,7 +16,7 @@ import {
import { getClient } from "./agent/client";
import { initializeLoadedSkillsFlag, setAgentContext } from "./agent/context";
import { createAgent } from "./agent/create";
import { ISOLATED_BLOCK_LABELS } from "./agent/memory";
import { ensureSkillsBlocks, ISOLATED_BLOCK_LABELS } from "./agent/memory";
import { sendMessageStream } from "./agent/message";
import { getModelUpdateArgs } from "./agent/model";
import { SessionStats } from "./agent/stats";
@@ -529,6 +529,12 @@ export async function handleHeadlessCommand(
conversationId,
});
// Ensure the agent has the required skills blocks (for backwards compatibility)
const createdBlocks = await ensureSkillsBlocks(agent.id);
if (createdBlocks.length > 0) {
console.log("Created missing skills blocks for agent compatibility");
}
// Set agent context for tools that need it (e.g., Skill tool, Task tool)
setAgentContext(agent.id, skillsDirectory);
await initializeLoadedSkillsFlag();

View File

@@ -7,7 +7,7 @@ import { getResumeData, type ResumeData } from "./agent/check-approval";
import { getClient } from "./agent/client";
import { initializeLoadedSkillsFlag, setAgentContext } from "./agent/context";
import type { AgentProvenance } from "./agent/create";
import { ISOLATED_BLOCK_LABELS } from "./agent/memory";
import { ensureSkillsBlocks, ISOLATED_BLOCK_LABELS } from "./agent/memory";
import { LETTA_CLOUD_API_URL } from "./auth/oauth";
import { ConversationSelector } from "./cli/components/ConversationSelector";
import type { ApprovalRequest } from "./cli/helpers/stream";
@@ -1227,6 +1227,12 @@ async function main(): Promise<void> {
settingsManager.updateLocalProjectSettings({ lastAgent: agent.id });
settingsManager.updateSettings({ lastAgent: agent.id });
// Ensure the agent has the required skills blocks (for backwards compatibility)
const createdBlocks = await ensureSkillsBlocks(agent.id);
if (createdBlocks.length > 0) {
console.log("Created missing skills blocks for agent compatibility");
}
// Set agent context for tools that need it (e.g., Skill tool)
setAgentContext(agent.id, skillsDirectory);
await initializeLoadedSkillsFlag();