feat: add toolset flag (#116)
This commit is contained in:
40
README.md
40
README.md
@@ -168,6 +168,7 @@ letta --agent <id> --unlink # Remove Letta Code tools from agent, then start
|
||||
While in a session, you can use these commands:
|
||||
- `/agent` - Show current agent link
|
||||
- `/model` - Switch models
|
||||
- `/toolset` - Switch toolsets (codex/default)
|
||||
- `/rename` - Rename the current agent
|
||||
- `/stream` - Toggle token streaming on/off
|
||||
- `/link` - Attach Letta Code tools to current agent (enables Read, Write, Edit, Bash, etc.)
|
||||
@@ -194,31 +195,32 @@ letta --agent <id> --unlink # Remove Letta Code tools
|
||||
|
||||
When you attach tools with `/link` or `--link`, they are added to the agent with approval rules enabled (human-in-the-loop). This means the agent can use these tools, but you'll be prompted to approve each tool call. Use permission modes to control approval behavior (see Permissions section below).
|
||||
|
||||
#### Available Tools
|
||||
### Toolsets
|
||||
|
||||
Letta Code provides the following tools for filesystem and shell operations:
|
||||
Letta Code includes different toolsets optimized for different model providers:
|
||||
|
||||
**File Operations:**
|
||||
- **Read** - Read files from the filesystem, supports offset/limit for large files
|
||||
- **Write** - Write or overwrite files, creates directories automatically
|
||||
- **Edit** - Perform exact string replacements in files (single edit)
|
||||
- **MultiEdit** - Perform multiple find-and-replace operations in a single file efficiently
|
||||
- **LS** - List files and directories, supports ignore patterns
|
||||
1. **Default Toolset** (Anthropic-optimized, best for Claude models)
|
||||
2. **Codex Toolset** (OpenAI-optimized, best for GPT models)
|
||||
|
||||
**Search & Discovery:**
|
||||
- **Glob** - Fast file pattern matching with glob patterns (`**/*.js`, `src/**/*.ts`)
|
||||
- **Grep** - Powerful search using ripgrep, supports regex and various output modes
|
||||
**Automatic Selection:**
|
||||
When you specify a model, Letta Code automatically selects the appropriate toolset:
|
||||
```bash
|
||||
letta --model haiku # Loads default toolset
|
||||
letta --model gpt-5-codex # Loads codex toolset
|
||||
```
|
||||
|
||||
**Shell Operations:**
|
||||
- **Bash** - Execute shell commands in a persistent session with timeout support
|
||||
- **BashOutput** - Retrieve output from background bash shells
|
||||
- **KillBash** - Terminate running background bash shells
|
||||
**Manual Override:**
|
||||
You can force a specific toolset regardless of model:
|
||||
```bash
|
||||
# CLI flag (at startup)
|
||||
letta --model haiku --toolset codex # Use Codex-style tools with Claude Haiku
|
||||
letta --model gpt-5-codex --toolset default # Use Anthropic-style tools with GPT-5-Codex
|
||||
|
||||
**Task Management:**
|
||||
- **TodoWrite** - Create and manage structured task lists for tracking progress
|
||||
- **ExitPlanMode** - Signal completion of planning phase and readiness to implement
|
||||
# Interactive command (during session)
|
||||
/toolset # Opens toolset selector
|
||||
```
|
||||
|
||||
All tools support approval rules and permission modes for safe execution. See the Permissions section for details on controlling tool access.
|
||||
The `/model` command automatically switches toolsets when you change models. Use `/toolset` if you want to manually override the automatic selection.
|
||||
|
||||
### Headless Mode
|
||||
```bash
|
||||
|
||||
@@ -35,6 +35,7 @@ export async function handleHeadlessCommand(
|
||||
new: { type: "boolean" },
|
||||
agent: { type: "string", short: "a" },
|
||||
model: { type: "string", short: "m" },
|
||||
toolset: { type: "string" },
|
||||
prompt: { type: "boolean", short: "p" },
|
||||
"output-format": { type: "string" },
|
||||
// Additional flags from index.ts that need to be filtered out
|
||||
|
||||
49
src/index.ts
49
src/index.ts
@@ -31,6 +31,7 @@ OPTIONS
|
||||
-c, --continue Resume previous session (uses global lastAgent, deprecated)
|
||||
-a, --agent <id> Use a specific agent ID
|
||||
-m, --model <id> Model ID or handle (e.g., "opus" or "anthropic/claude-opus-4-1-20250805")
|
||||
--toolset <name> Force toolset: "codex" or "default" (overrides model-based auto-selection)
|
||||
-p, --prompt Headless prompt mode
|
||||
--output-format <fmt> Output format for headless mode (text, json, stream-json)
|
||||
Default: text
|
||||
@@ -70,6 +71,26 @@ EXAMPLES
|
||||
console.log(usage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to determine which model identifier to pass to loadTools()
|
||||
* based on user's model and/or toolset preferences.
|
||||
*/
|
||||
function getModelForToolLoading(
|
||||
specifiedModel?: string,
|
||||
specifiedToolset?: "codex" | "default",
|
||||
): string | undefined {
|
||||
// If toolset is explicitly specified, use a dummy model from that provider
|
||||
// to trigger the correct toolset loading logic
|
||||
if (specifiedToolset === "codex") {
|
||||
return "openai/gpt-4";
|
||||
}
|
||||
if (specifiedToolset === "default") {
|
||||
return "anthropic/claude-sonnet-4";
|
||||
}
|
||||
// Otherwise, use the specified model (or undefined for auto-detection)
|
||||
return specifiedModel;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Initialize settings manager (loads settings once into memory)
|
||||
await settingsManager.initialize();
|
||||
@@ -97,6 +118,7 @@ async function main() {
|
||||
"fresh-blocks": { type: "boolean" },
|
||||
agent: { type: "string", short: "a" },
|
||||
model: { type: "string", short: "m" },
|
||||
toolset: { type: "string" },
|
||||
prompt: { type: "boolean", short: "p" },
|
||||
run: { type: "boolean" },
|
||||
tools: { type: "string" },
|
||||
@@ -151,10 +173,23 @@ async function main() {
|
||||
const freshBlocks = (values["fresh-blocks"] as boolean | undefined) ?? false;
|
||||
const specifiedAgentId = (values.agent as string | undefined) ?? null;
|
||||
const specifiedModel = (values.model as string | undefined) ?? undefined;
|
||||
const specifiedToolset = (values.toolset as string | undefined) ?? undefined;
|
||||
const skillsDirectory = (values.skills as string | undefined) ?? undefined;
|
||||
const sleeptimeFlag = (values.sleeptime as boolean | undefined) ?? undefined;
|
||||
const isHeadless = values.prompt || values.run || !process.stdin.isTTY;
|
||||
|
||||
// Validate toolset if provided
|
||||
if (
|
||||
specifiedToolset &&
|
||||
specifiedToolset !== "codex" &&
|
||||
specifiedToolset !== "default"
|
||||
) {
|
||||
console.error(
|
||||
`Error: Invalid toolset "${specifiedToolset}". Must be "codex" or "default".`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check if API key is configured
|
||||
const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
|
||||
const baseURL =
|
||||
@@ -270,8 +305,12 @@ async function main() {
|
||||
}
|
||||
|
||||
if (isHeadless) {
|
||||
// For headless mode, load tools synchronously (respecting model when provided)
|
||||
await loadTools(specifiedModel);
|
||||
// For headless mode, load tools synchronously (respecting model/toolset when provided)
|
||||
const modelForTools = getModelForToolLoading(
|
||||
specifiedModel,
|
||||
specifiedToolset as "codex" | "default" | undefined,
|
||||
);
|
||||
await loadTools(modelForTools);
|
||||
const client = await getClient();
|
||||
await upsertToolsToServer(client);
|
||||
|
||||
@@ -293,6 +332,7 @@ async function main() {
|
||||
freshBlocks,
|
||||
agentIdArg,
|
||||
model,
|
||||
toolset,
|
||||
skillsDirectory,
|
||||
}: {
|
||||
continueSession: boolean;
|
||||
@@ -300,6 +340,7 @@ async function main() {
|
||||
freshBlocks: boolean;
|
||||
agentIdArg: string | null;
|
||||
model?: string;
|
||||
toolset?: "codex" | "default";
|
||||
skillsDirectory?: string;
|
||||
}) {
|
||||
const [loadingState, setLoadingState] = useState<
|
||||
@@ -319,7 +360,8 @@ async function main() {
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
setLoadingState("assembling");
|
||||
await loadTools(model);
|
||||
const modelForTools = getModelForToolLoading(model, toolset);
|
||||
await loadTools(modelForTools);
|
||||
|
||||
setLoadingState("upserting");
|
||||
const client = await getClient();
|
||||
@@ -499,6 +541,7 @@ async function main() {
|
||||
freshBlocks: freshBlocks,
|
||||
agentIdArg: specifiedAgentId,
|
||||
model: specifiedModel,
|
||||
toolset: specifiedToolset as "codex" | "default" | undefined,
|
||||
skillsDirectory: skillsDirectory,
|
||||
}),
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user