From d67b5705691ae12d4595f3960416bce7debdc131 Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:27:53 -0500 Subject: [PATCH] refactor: remove link and unlink (#233) --- src/cli/App.tsx | 107 +++---------------------- src/cli/commands/registry.ts | 16 +--- src/cli/components/ToolsetSelector.tsx | 10 ++- src/cli/components/WelcomeScreen.tsx | 9 +-- src/index.ts | 6 +- src/tools/toolset.ts | 14 +++- 6 files changed, 39 insertions(+), 123 deletions(-) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 6dd47f3..89b9e91 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -19,7 +19,6 @@ import { getClient } from "../agent/client"; import { setCurrentAgentId } from "../agent/context"; import type { AgentProvenance } from "../agent/create"; import { sendMessageStream } from "../agent/message"; -import { linkToolsToAgent, unlinkToolsFromAgent } from "../agent/modify"; import { SessionStats } from "../agent/stats"; import type { ApprovalContext } from "../permissions/analyzer"; import { permissionMode } from "../permissions/mode"; @@ -265,8 +264,7 @@ export default function App({ loadingState?: | "assembling" | "upserting" - | "linking" - | "unlinking" + | "updating_tools" | "importing" | "initializing" | "checking" @@ -382,7 +380,13 @@ export default function App({ string | null >("default"); const [currentToolset, setCurrentToolset] = useState< - "codex" | "codex_snake" | "default" | "gemini" | "gemini_snake" | null + | "codex" + | "codex_snake" + | "default" + | "gemini" + | "gemini_snake" + | "none" + | null >(null); const [llmConfig, setLlmConfig] = useState(null); const [agentName, setAgentName] = useState(null); @@ -1763,94 +1767,6 @@ export default function App({ return { submitted: true }; } - // Special handling for /link command - attach Letta Code tools - if (msg.trim() === "/link") { - const cmdId = uid("cmd"); - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: "Attaching Letta Code tools to agent...", - phase: "running", - }); - buffersRef.current.order.push(cmdId); - refreshDerived(); - - setCommandRunning(true); - - try { - const result = await linkToolsToAgent(agentId); - - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: result.message, - phase: "finished", - success: result.success, - }); - refreshDerived(); - } catch (error) { - const errorDetails = formatErrorDetails(error, agentId); - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: `Failed: ${errorDetails}`, - phase: "finished", - success: false, - }); - refreshDerived(); - } finally { - setCommandRunning(false); - } - return { submitted: true }; - } - - // Special handling for /unlink command - remove Letta Code tools - if (msg.trim() === "/unlink") { - const cmdId = uid("cmd"); - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: "Removing Letta Code tools from agent...", - phase: "running", - }); - buffersRef.current.order.push(cmdId); - refreshDerived(); - - setCommandRunning(true); - - try { - const result = await unlinkToolsFromAgent(agentId); - - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: result.message, - phase: "finished", - success: result.success, - }); - refreshDerived(); - } catch (error) { - const errorDetails = formatErrorDetails(error, agentId); - buffersRef.current.byId.set(cmdId, { - kind: "command", - id: cmdId, - input: msg, - output: `Failed: ${errorDetails}`, - phase: "finished", - success: false, - }); - refreshDerived(); - } finally { - setCommandRunning(false); - } - return { submitted: true }; - } - // Special handling for /rename command - rename the agent if (msg.trim().startsWith("/rename")) { const parts = msg.trim().split(/\s+/); @@ -3094,7 +3010,8 @@ ${recentCommits} | "codex_snake" | "default" | "gemini" - | "gemini_snake" = isOpenAIModel(selectedModel.handle ?? "") + | "gemini_snake" + | "none" = isOpenAIModel(selectedModel.handle ?? "") ? "codex" : isGeminiModel(selectedModel.handle ?? "") ? "gemini" @@ -3106,6 +3023,7 @@ ${recentCommits} | "default" | "gemini" | "gemini_snake" + | "none" | null = null; if (currentToolset !== targetToolset) { const { switchToolsetForModel } = await import("../tools/toolset"); @@ -3248,7 +3166,8 @@ ${recentCommits} | "codex_snake" | "default" | "gemini" - | "gemini_snake", + | "gemini_snake" + | "none", ) => { setToolsetSelectorOpen(false); diff --git a/src/cli/commands/registry.ts b/src/cli/commands/registry.ts index add3b04..6f2fce2 100644 --- a/src/cli/commands/registry.ts +++ b/src/cli/commands/registry.ts @@ -45,20 +45,6 @@ export const commands: Record = { return "Clearing credentials..."; }, }, - "/link": { - desc: "Attach Letta Code tools to current agent", - handler: () => { - // Handled specially in App.tsx to access agent ID and client - return "Attaching tools..."; - }, - }, - "/unlink": { - desc: "Remove Letta Code tools from current agent", - handler: () => { - // Handled specially in App.tsx to access agent ID and client - return "Removing tools..."; - }, - }, "/rename": { desc: "Rename the current agent", handler: () => { @@ -74,7 +60,7 @@ export const commands: Record = { }, }, "/toolset": { - desc: "Switch toolset", + desc: "Switch toolset (replaces /link and /unlink)", handler: () => { // Handled specially in App.tsx to access agent ID and client return "Opening toolset selector..."; diff --git a/src/cli/components/ToolsetSelector.tsx b/src/cli/components/ToolsetSelector.tsx index 981c909..d788374 100644 --- a/src/cli/components/ToolsetSelector.tsx +++ b/src/cli/components/ToolsetSelector.tsx @@ -8,7 +8,8 @@ type ToolsetId = | "codex_snake" | "default" | "gemini" - | "gemini_snake"; + | "gemini_snake" + | "none"; interface ToolsetOption { id: ToolsetId; @@ -99,6 +100,13 @@ const toolsets: ToolsetOption[] = [ "read_many_files", ], }, + { + id: "none", + label: "None (Disable Tools)", + description: "Remove all Letta Code tools from the agent", + tools: [], + isFeatured: true, + }, ]; interface ToolsetSelectorProps { diff --git a/src/cli/components/WelcomeScreen.tsx b/src/cli/components/WelcomeScreen.tsx index 006aca0..712d757 100644 --- a/src/cli/components/WelcomeScreen.tsx +++ b/src/cli/components/WelcomeScreen.tsx @@ -48,8 +48,7 @@ function getAuthMethod(): "url" | "api-key" | "oauth" { type LoadingState = | "assembling" | "upserting" - | "linking" - | "unlinking" + | "updating_tools" | "importing" | "initializing" | "checking" @@ -207,10 +206,8 @@ function getLoadingMessage( return "Assembling tools..."; case "upserting": return "Upserting tools..."; - case "linking": - return "Attaching tools..."; - case "unlinking": - return "Removing tools..."; + case "updating_tools": + return "Updating tools..."; case "importing": return "Importing agent..."; case "checking": diff --git a/src/index.ts b/src/index.ts index 6faae3c..6ee5b2d 100755 --- a/src/index.ts +++ b/src/index.ts @@ -421,6 +421,7 @@ async function main() { const shouldLink = values.link as boolean | undefined; const shouldUnlink = values.unlink as boolean | undefined; + // Validate --link/--unlink flags require --agent // Validate --link/--unlink flags require --agent if (shouldLink || shouldUnlink) { if (!specifiedAgentId) { @@ -481,8 +482,7 @@ async function main() { | "selecting" | "assembling" | "upserting" - | "linking" - | "unlinking" + | "updating_tools" | "importing" | "initializing" | "checking" @@ -591,7 +591,7 @@ async function main() { process.exit(1); } - setLoadingState(shouldLink ? "linking" : "unlinking"); + setLoadingState("updating_tools"); const { linkToolsToAgent, unlinkToolsFromAgent } = await import( "./agent/modify" ); diff --git a/src/tools/toolset.ts b/src/tools/toolset.ts index adfa365..0ef2e9c 100644 --- a/src/tools/toolset.ts +++ b/src/tools/toolset.ts @@ -30,7 +30,8 @@ export type ToolsetName = | "codex_snake" | "default" | "gemini" - | "gemini_snake"; + | "gemini_snake" + | "none"; // Server-side/base tools that should stay attached regardless of Letta toolset export const BASE_TOOL_NAMES = ["memory", "web_search"]; @@ -130,7 +131,10 @@ export async function forceToolsetSwitch( clearTools(); // Load the appropriate toolset - if (toolsetName === "codex") { + if (toolsetName === "none") { + // Just clear tools + clearTools(); + } else if (toolsetName === "codex") { await loadSpecificTools([...CODEX_TOOLS]); } else if (toolsetName === "codex_snake") { await loadTools("openai/gpt-4"); @@ -146,9 +150,11 @@ export async function forceToolsetSwitch( const client = await getClient(); await upsertToolsToServer(client); - // Remove old Letta tools and add new ones + // Remove old Letta tools and add new ones (or just remove if none) await unlinkToolsFromAgent(agentId); - await linkToolsToAgent(agentId); + if (toolsetName !== "none") { + await linkToolsToAgent(agentId); + } // Ensure base memory tool uses memory_apply_patch instead of legacy memory try {