fix: isolate skills blocks per conversation (#545)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-01-14 18:12:37 -08:00
committed by GitHub
parent 9fd6260a7f
commit db33f942b3
6 changed files with 20 additions and 3 deletions

View File

@@ -4,7 +4,7 @@
"": {
"name": "@letta-ai/letta-code",
"dependencies": {
"@letta-ai/letta-client": "1.6.5",
"@letta-ai/letta-client": "1.6.6",
"glob": "^13.0.0",
"ink-link": "^5.0.0",
"open": "^10.2.0",
@@ -36,7 +36,7 @@
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
"@letta-ai/letta-client": ["@letta-ai/letta-client@1.6.5", "", {}, "sha512-LrJz7xqqYXaoAC6XXUZ/K+YgrCLzoEIRwPCXoOsiN/ehl7hl9kiz1Mw/iYSCK7ZpXS6fnyaIbIwGfxWeCl8z5g=="],
"@letta-ai/letta-client": ["@letta-ai/letta-client@1.6.6", "", {}, "sha512-kD0x5SvUrSSLLG3aF0wcXp6iA52UxyEXPA+kr9LV7PLhLwys5pYC00QWflvHmPB5MPnnmYA9oBaEbP3SHIEv/w=="],
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],

View File

@@ -30,7 +30,7 @@
"access": "public"
},
"dependencies": {
"@letta-ai/letta-client": "1.6.5",
"@letta-ai/letta-client": "1.6.6",
"glob": "^13.0.0",
"ink-link": "^5.0.0",
"open": "^10.2.0"

View File

@@ -39,6 +39,13 @@ export type MemoryBlockLabel = (typeof MEMORY_BLOCK_LABELS)[number];
*/
export const READ_ONLY_BLOCK_LABELS = ["skills", "loaded_skills"] as const;
/**
* Block labels that should be isolated per-conversation.
* When creating a conversation, these blocks are copied from the agent's blocks
* to create conversation-specific versions, preventing cross-conversation state pollution.
*/
export const ISOLATED_BLOCK_LABELS = ["skills", "loaded_skills"] as const;
/**
* Check if a block label is a project-level block
*/

View File

@@ -37,6 +37,7 @@ import { getResumeData } from "../agent/check-approval";
import { getClient } from "../agent/client";
import { getCurrentAgentId, setCurrentAgentId } from "../agent/context";
import { type AgentProvenance, createAgent } from "../agent/create";
import { ISOLATED_BLOCK_LABELS } from "../agent/memory";
import { sendMessageStream } from "../agent/message";
import { getModelDisplayName, getModelInfo } from "../agent/model";
import { SessionStats } from "../agent/stats";
@@ -2893,6 +2894,7 @@ export default function App({
// User can /resume to get back to a previous conversation if needed
const newConversation = await client.conversations.create({
agent_id: targetAgentId,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
const targetConversationId = newConversation.id;
@@ -3936,6 +3938,7 @@ export default function App({
// Create a new conversation for the current agent
const conversation = await client.conversations.create({
agent_id: agentId,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
// Update conversationId state
@@ -4008,6 +4011,7 @@ export default function App({
// Also create a new conversation since messages were cleared
const conversation = await client.conversations.create({
agent_id: agentId,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
setConversationId(conversation.id);
settingsManager.setLocalLastSession(
@@ -7554,6 +7558,7 @@ Plan file path: ${planFilePath}`;
const client = await getClient();
const conversation = await client.conversations.create({
agent_id: agentId,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
setConversationId(conversation.id);
settingsManager.setLocalLastSession(

View File

@@ -15,6 +15,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 { sendMessageStream } from "./agent/message";
import { getModelUpdateArgs } from "./agent/model";
import { SessionStats } from "./agent/stats";
@@ -446,6 +447,7 @@ export async function handleHeadlessCommand(
// This ensures isolated message history per CLI invocation
const conversation = await client.conversations.create({
agent_id: agent.id,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
const conversationId = conversation.id;

View File

@@ -6,6 +6,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 { LETTA_CLOUD_API_URL } from "./auth/oauth";
import type { ApprovalRequest } from "./cli/helpers/stream";
import { ProfileSelectionInline } from "./cli/profile-selection";
@@ -1331,6 +1332,7 @@ async function main(): Promise<void> {
// No valid session to resume for this agent, create new
const conversation = await client.conversations.create({
agent_id: agent.id,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
conversationIdToUse = conversation.id;
}
@@ -1339,6 +1341,7 @@ async function main(): Promise<void> {
// This ensures each CLI session has isolated message history
const conversation = await client.conversations.create({
agent_id: agent.id,
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
});
conversationIdToUse = conversation.id;
}