refactor: remove link and unlink (#233)

This commit is contained in:
Kian Jones
2025-12-16 12:27:53 -05:00
committed by GitHub
parent 0630ac4508
commit d67b570569
6 changed files with 39 additions and 123 deletions

View File

@@ -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<LlmConfig | null>(null);
const [agentName, setAgentName] = useState<string | null>(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);

View File

@@ -45,20 +45,6 @@ export const commands: Record<string, Command> = {
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<string, Command> = {
},
},
"/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...";

View File

@@ -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 {

View File

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

View File

@@ -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"
);

View File

@@ -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 {