refactor: remove --continue flag (now redundant) (#1458)
Co-authored-by: Letta Code <noreply@letta.com>
This commit is contained in:
@@ -61,7 +61,6 @@ const SCENARIOS: ScenarioConfig[] = [
|
|||||||
args: [
|
args: [
|
||||||
"-p",
|
"-p",
|
||||||
"What is 3+3? Reply with just the number.",
|
"What is 3+3? Reply with just the number.",
|
||||||
"--continue",
|
|
||||||
"--yolo",
|
"--yolo",
|
||||||
"--output-format",
|
"--output-format",
|
||||||
"json",
|
"json",
|
||||||
@@ -73,7 +72,6 @@ const SCENARIOS: ScenarioConfig[] = [
|
|||||||
args: [
|
args: [
|
||||||
"-p",
|
"-p",
|
||||||
"What is 5+5? Reply with just the number.",
|
"What is 5+5? Reply with just the number.",
|
||||||
"--continue",
|
|
||||||
"--yolo",
|
"--yolo",
|
||||||
"--output-format",
|
"--output-format",
|
||||||
"json",
|
"json",
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ const TOOL_CALL_COMMIT_DEFER_MS = 50;
|
|||||||
const ANIMATION_RESUME_HYSTERESIS_ROWS = 2;
|
const ANIMATION_RESUME_HYSTERESIS_ROWS = 2;
|
||||||
|
|
||||||
// Eager approval checking is now CONDITIONAL (LET-7101):
|
// Eager approval checking is now CONDITIONAL (LET-7101):
|
||||||
// - Enabled when resuming a session (--resume, --continue, or startupApprovals exist)
|
// - Enabled when resuming a session (--resume or startupApprovals exist)
|
||||||
// - Disabled for normal messages (lazy recovery handles edge cases)
|
// - Disabled for normal messages (lazy recovery handles edge cases)
|
||||||
// This saves ~2s latency per message in the common case.
|
// This saves ~2s latency per message in the common case.
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,6 @@ export const CLI_FLAG_CATALOG = {
|
|||||||
mode: "both",
|
mode: "both",
|
||||||
help: { description: "Show current directory, skills, and pinned agents" },
|
help: { description: "Show current directory, skills, and pinned agents" },
|
||||||
},
|
},
|
||||||
continue: {
|
|
||||||
parser: { type: "boolean", short: "c" },
|
|
||||||
mode: "both",
|
|
||||||
help: {
|
|
||||||
description: "Resume last session (agent + conversation) directly",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
resume: {
|
resume: {
|
||||||
parser: { type: "boolean", short: "r" },
|
parser: { type: "boolean", short: "r" },
|
||||||
mode: "interactive",
|
mode: "interactive",
|
||||||
|
|||||||
@@ -367,7 +367,6 @@ export async function handleHeadlessCommand(
|
|||||||
console.error(
|
console.error(
|
||||||
"Error: --resume is for interactive mode only (opens conversation selector).\n" +
|
"Error: --resume is for interactive mode only (opens conversation selector).\n" +
|
||||||
"In headless mode, use:\n" +
|
"In headless mode, use:\n" +
|
||||||
" --continue Resume the last session (agent + conversation)\n" +
|
|
||||||
" --conversation <id> Resume a specific conversation by ID",
|
" --conversation <id> Resume a specific conversation by ID",
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -382,7 +381,6 @@ export async function handleHeadlessCommand(
|
|||||||
let specifiedAgentId = values.agent;
|
let specifiedAgentId = values.agent;
|
||||||
const specifiedAgentName = values.name;
|
const specifiedAgentName = values.name;
|
||||||
let specifiedConversationId = values.conversation;
|
let specifiedConversationId = values.conversation;
|
||||||
const shouldContinue = values.continue;
|
|
||||||
const forceNew = values["new-agent"];
|
const forceNew = values["new-agent"];
|
||||||
const systemPromptPreset = values.system;
|
const systemPromptPreset = values.system;
|
||||||
const systemCustom = values["system-custom"];
|
const systemCustom = values["system-custom"];
|
||||||
@@ -509,10 +507,6 @@ export async function handleHeadlessCommand(
|
|||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
if (shouldContinue) {
|
|
||||||
console.error("Error: --from-agent cannot be used with --continue");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
if (forceNew) {
|
if (forceNew) {
|
||||||
console.error("Error: --from-agent cannot be used with --new-agent");
|
console.error("Error: --from-agent cannot be used with --new-agent");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -543,20 +537,12 @@ export async function handleHeadlessCommand(
|
|||||||
when: fromAfFile,
|
when: fromAfFile,
|
||||||
message: "--conversation cannot be used with --import",
|
message: "--conversation cannot be used with --import",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
when: shouldContinue,
|
|
||||||
message: "--conversation cannot be used with --continue",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
validateFlagConflicts({
|
validateFlagConflicts({
|
||||||
guard: forceNewConversation,
|
guard: forceNewConversation,
|
||||||
checks: [
|
checks: [
|
||||||
{
|
|
||||||
when: shouldContinue,
|
|
||||||
message: "--new cannot be used with --continue",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
when: specifiedConversationId,
|
when: specifiedConversationId,
|
||||||
message: "--new cannot be used with --conversation",
|
message: "--new cannot be used with --conversation",
|
||||||
@@ -586,10 +572,6 @@ export async function handleHeadlessCommand(
|
|||||||
when: specifiedAgentName,
|
when: specifiedAgentName,
|
||||||
message: "--import cannot be used with --name",
|
message: "--import cannot be used with --name",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
when: shouldContinue,
|
|
||||||
message: "--import cannot be used with --continue",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
when: forceNew,
|
when: forceNew,
|
||||||
message: "--import cannot be used with --new-agent",
|
message: "--import cannot be used with --new-agent",
|
||||||
@@ -860,14 +842,7 @@ export async function handleHeadlessCommand(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 6: --continue with no agent found → error
|
// Priority 6: Fresh user with no LRU - create default agent
|
||||||
if (!agent && shouldContinue) {
|
|
||||||
console.error("No recent session found in .letta/ or ~/.letta.");
|
|
||||||
console.error("Run 'letta' to get started.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority 7: Fresh user with no LRU - create default agent
|
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
const { ensureDefaultAgents } = await import("./agent/defaults");
|
const { ensureDefaultAgents } = await import("./agent/defaults");
|
||||||
const defaultAgent = await ensureDefaultAgents(client, {
|
const defaultAgent = await ensureDefaultAgents(client, {
|
||||||
@@ -886,11 +861,7 @@ export async function handleHeadlessCommand(
|
|||||||
markMilestone("HEADLESS_AGENT_RESOLVED");
|
markMilestone("HEADLESS_AGENT_RESOLVED");
|
||||||
|
|
||||||
// Check if we're resuming an existing agent (not creating a new one)
|
// Check if we're resuming an existing agent (not creating a new one)
|
||||||
const isResumingAgent = !!(
|
const isResumingAgent = !!(specifiedAgentId || (!forceNew && !fromAfFile));
|
||||||
specifiedAgentId ||
|
|
||||||
shouldContinue ||
|
|
||||||
(!forceNew && !fromAfFile)
|
|
||||||
);
|
|
||||||
|
|
||||||
// If resuming, always refresh model settings from presets to keep
|
// If resuming, always refresh model settings from presets to keep
|
||||||
// preset-derived fields in sync, then apply optional command-line
|
// preset-derived fields in sync, then apply optional command-line
|
||||||
@@ -1111,45 +1082,6 @@ export async function handleHeadlessCommand(
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (shouldContinue) {
|
|
||||||
// Try to resume the last conversation for this agent
|
|
||||||
await settingsManager.loadLocalProjectSettings();
|
|
||||||
const lastSession =
|
|
||||||
settingsManager.getLocalLastSession(process.cwd()) ??
|
|
||||||
settingsManager.getGlobalLastSession();
|
|
||||||
|
|
||||||
if (lastSession && lastSession.agentId === agent.id) {
|
|
||||||
if (lastSession.conversationId === "default") {
|
|
||||||
// "default" is always valid - just use it directly
|
|
||||||
conversationId = "default";
|
|
||||||
} else {
|
|
||||||
// Verify the conversation still exists
|
|
||||||
try {
|
|
||||||
debugLog(
|
|
||||||
"conversations",
|
|
||||||
`retrieve(${lastSession.conversationId}) [headless lastSession resume]`,
|
|
||||||
);
|
|
||||||
await client.conversations.retrieve(lastSession.conversationId);
|
|
||||||
conversationId = lastSession.conversationId;
|
|
||||||
} catch {
|
|
||||||
// Conversation no longer exists - error with helpful message
|
|
||||||
console.error(
|
|
||||||
`Attempting to resume conversation ${lastSession.conversationId}, but conversation was not found.`,
|
|
||||||
);
|
|
||||||
console.error(
|
|
||||||
"Resume the default conversation with 'letta -p ...', view recent conversations with 'letta --resume', or start a new conversation with 'letta -p ... --new'.",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No matching session - error with helpful message
|
|
||||||
console.error("No previous session found for this agent to resume.");
|
|
||||||
console.error(
|
|
||||||
"Resume the default conversation with 'letta -p ...', or start a new conversation with 'letta -p ... --new'.",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else if (forceNewConversation) {
|
} else if (forceNewConversation) {
|
||||||
// --new flag: create a new conversation (for concurrent sessions)
|
// --new flag: create a new conversation (for concurrent sessions)
|
||||||
const conversation = await client.conversations.create({
|
const conversation = await client.conversations.create({
|
||||||
|
|||||||
159
src/index.ts
159
src/index.ts
@@ -68,7 +68,6 @@ USAGE
|
|||||||
# interactive TUI
|
# interactive TUI
|
||||||
letta Resume last conversation for this project
|
letta Resume last conversation for this project
|
||||||
letta --new Create a new conversation (for concurrent sessions)
|
letta --new Create a new conversation (for concurrent sessions)
|
||||||
letta --continue Resume last session (agent + conversation) directly
|
|
||||||
letta --resume Open agent selector UI to pick agent/conversation
|
letta --resume Open agent selector UI to pick agent/conversation
|
||||||
letta --new-agent Create a new agent directly (skip profile selector)
|
letta --new-agent Create a new agent directly (skip profile selector)
|
||||||
letta --agent <id> Open a specific agent by ID
|
letta --agent <id> Open a specific agent by ID
|
||||||
@@ -452,8 +451,6 @@ async function main(): Promise<void> {
|
|||||||
process.exit(result.success ? 0 : 1);
|
process.exit(result.success ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --continue: Resume last session (agent + conversation) automatically
|
|
||||||
const shouldContinue = values.continue ?? false;
|
|
||||||
// --resume: Open agent selector UI after loading
|
// --resume: Open agent selector UI after loading
|
||||||
const shouldResume = values.resume ?? false;
|
const shouldResume = values.resume ?? false;
|
||||||
let specifiedConversationId = values.conversation ?? null; // Specific conversation to resume
|
let specifiedConversationId = values.conversation ?? null; // Specific conversation to resume
|
||||||
@@ -655,20 +652,12 @@ async function main(): Promise<void> {
|
|||||||
when: shouldResume,
|
when: shouldResume,
|
||||||
message: "--conversation cannot be used with --resume",
|
message: "--conversation cannot be used with --resume",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
when: shouldContinue,
|
|
||||||
message: "--conversation cannot be used with --continue",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
validateFlagConflicts({
|
validateFlagConflicts({
|
||||||
guard: forceNewConversation,
|
guard: forceNewConversation,
|
||||||
checks: [
|
checks: [
|
||||||
{
|
|
||||||
when: shouldContinue,
|
|
||||||
message: "--new cannot be used with --continue",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
when: specifiedConversationId,
|
when: specifiedConversationId,
|
||||||
message: "--new cannot be used with --conversation",
|
message: "--new cannot be used with --conversation",
|
||||||
@@ -957,7 +946,6 @@ async function main(): Promise<void> {
|
|||||||
const App = AppModule.default;
|
const App = AppModule.default;
|
||||||
|
|
||||||
function LoadingApp({
|
function LoadingApp({
|
||||||
continueSession,
|
|
||||||
forceNew,
|
forceNew,
|
||||||
initBlocks,
|
initBlocks,
|
||||||
baseTools,
|
baseTools,
|
||||||
@@ -969,7 +957,6 @@ async function main(): Promise<void> {
|
|||||||
fromAfFile,
|
fromAfFile,
|
||||||
isRegistryImport,
|
isRegistryImport,
|
||||||
}: {
|
}: {
|
||||||
continueSession: boolean;
|
|
||||||
forceNew: boolean;
|
forceNew: boolean;
|
||||||
initBlocks?: string[];
|
initBlocks?: string[];
|
||||||
baseTools?: string[];
|
baseTools?: string[];
|
||||||
@@ -1273,59 +1260,6 @@ async function main(): Promise<void> {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================================================================
|
|
||||||
// TOP-LEVEL PATH: --continue
|
|
||||||
// Resume last session directly (local → global fallback)
|
|
||||||
// =====================================================================
|
|
||||||
if (continueSession) {
|
|
||||||
const localSession = settingsManager.getLocalLastSession(
|
|
||||||
process.cwd(),
|
|
||||||
);
|
|
||||||
const localAgentId = localSession?.agentId ?? localSettings.lastAgent;
|
|
||||||
|
|
||||||
// Try local LRU first
|
|
||||||
if (localAgentId) {
|
|
||||||
try {
|
|
||||||
await client.agents.retrieve(localAgentId);
|
|
||||||
setSelectedGlobalAgentId(localAgentId);
|
|
||||||
if (localSession?.conversationId) {
|
|
||||||
setSelectedConversationId(localSession.conversationId);
|
|
||||||
}
|
|
||||||
setLoadingState("assembling");
|
|
||||||
return;
|
|
||||||
} catch {
|
|
||||||
// Local agent doesn't exist, try global
|
|
||||||
setFailedAgentMessage(
|
|
||||||
`Unable to locate agent ${localAgentId} in .letta/, checking global (~/.letta)`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("No recent agent in .letta/, using global (~/.letta)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try global LRU
|
|
||||||
const globalSession = settingsManager.getGlobalLastSession();
|
|
||||||
const globalAgentId = globalSession?.agentId;
|
|
||||||
if (globalAgentId) {
|
|
||||||
try {
|
|
||||||
await client.agents.retrieve(globalAgentId);
|
|
||||||
setSelectedGlobalAgentId(globalAgentId);
|
|
||||||
if (globalSession?.conversationId) {
|
|
||||||
setSelectedConversationId(globalSession.conversationId);
|
|
||||||
}
|
|
||||||
setLoadingState("assembling");
|
|
||||||
return;
|
|
||||||
} catch {
|
|
||||||
// Global agent also doesn't exist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No valid agent found anywhere
|
|
||||||
console.error("No recent session found in .letta/ or ~/.letta.");
|
|
||||||
console.error("Run 'letta' to get started.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// DEFAULT PATH: No special flags
|
// DEFAULT PATH: No special flags
|
||||||
// Check local LRU → global LRU → selector → create default
|
// Check local LRU → global LRU → selector → create default
|
||||||
@@ -1429,7 +1363,6 @@ async function main(): Promise<void> {
|
|||||||
forceNew,
|
forceNew,
|
||||||
agentIdArg,
|
agentIdArg,
|
||||||
fromAfFile,
|
fromAfFile,
|
||||||
continueSession,
|
|
||||||
shouldResume,
|
shouldResume,
|
||||||
specifiedConversationId,
|
specifiedConversationId,
|
||||||
]);
|
]);
|
||||||
@@ -1488,18 +1421,6 @@ async function main(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 4: Try global settings if --continue flag
|
|
||||||
if (!resumingAgentId && continueSession && settings.lastAgent) {
|
|
||||||
try {
|
|
||||||
await client.agents.retrieve(settings.lastAgent);
|
|
||||||
resumingAgentId = settings.lastAgent;
|
|
||||||
} catch {
|
|
||||||
// Global agent doesn't exist - show selector
|
|
||||||
setLoadingState("selecting_global");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set resuming state early so loading messages are accurate
|
// Set resuming state early so loading messages are accurate
|
||||||
@@ -1670,22 +1591,6 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Priority 6: Try to reuse global lastAgent if --continue flag is passed
|
|
||||||
// Note: If global lastAgent retrieval failed in early validation (with --continue),
|
|
||||||
// we already showed selector and returned. This is a safety fallback.
|
|
||||||
if (!agent && continueSession && settings.lastAgent) {
|
|
||||||
try {
|
|
||||||
agent = await client.agents.retrieve(settings.lastAgent);
|
|
||||||
} catch (error) {
|
|
||||||
// Agent disappeared - show selector instead of silently creating
|
|
||||||
console.error(
|
|
||||||
`Previous agent ${settings.lastAgent} not found (error: ${JSON.stringify(error)})`,
|
|
||||||
);
|
|
||||||
setLoadingState("selecting_global");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All paths should have resolved to an agent by now
|
// All paths should have resolved to an agent by now
|
||||||
// If not, it's an unexpected state - error out instead of auto-creating
|
// If not, it's an unexpected state - error out instead of auto-creating
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
@@ -1724,14 +1629,12 @@ async function main(): Promise<void> {
|
|||||||
// Check if we're resuming an existing agent
|
// Check if we're resuming an existing agent
|
||||||
// We're resuming if:
|
// We're resuming if:
|
||||||
// 1. We specified an agent ID via --agent flag (agentIdArg)
|
// 1. We specified an agent ID via --agent flag (agentIdArg)
|
||||||
// 2. We used --resume flag (continueSession)
|
// 2. We're reusing a project agent (detected early as resumingAgentId)
|
||||||
// 3. We're reusing a project agent (detected early as resumingAgentId)
|
// 3. We retrieved an agent from LRU (detected by checking if agent already existed)
|
||||||
// 4. We retrieved an agent from LRU (detected by checking if agent already existed)
|
|
||||||
const isResumingProject = !shouldCreateNew && !!resumingAgentId;
|
const isResumingProject = !shouldCreateNew && !!resumingAgentId;
|
||||||
const isReusingExistingAgent =
|
const isReusingExistingAgent =
|
||||||
!shouldCreateNew && !fromAfFile && agent && agent.id;
|
!shouldCreateNew && !fromAfFile && agent && agent.id;
|
||||||
const resuming = !!(
|
const resuming = !!(
|
||||||
continueSession ||
|
|
||||||
agentIdArg ||
|
agentIdArg ||
|
||||||
isResumingProject ||
|
isResumingProject ||
|
||||||
isReusingExistingAgent
|
isReusingExistingAgent
|
||||||
@@ -1829,7 +1732,6 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
// Debug: log resume flag status
|
// Debug: log resume flag status
|
||||||
if (isDebugEnabled()) {
|
if (isDebugEnabled()) {
|
||||||
debugLog("startup", "shouldContinue=%o", shouldContinue);
|
|
||||||
debugLog("startup", "shouldResume=%o", shouldResume);
|
debugLog("startup", "shouldResume=%o", shouldResume);
|
||||||
debugLog(
|
debugLog(
|
||||||
"startup",
|
"startup",
|
||||||
@@ -1865,60 +1767,6 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} else if (shouldContinue) {
|
|
||||||
// Try to load the last session for this agent
|
|
||||||
const lastSession =
|
|
||||||
settingsManager.getLocalLastSession(process.cwd()) ??
|
|
||||||
settingsManager.getGlobalLastSession();
|
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
debugLog("startup", "lastSession=%s", JSON.stringify(lastSession));
|
|
||||||
debugLog("startup", "agent.id=%s", agent.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resumedSuccessfully = false;
|
|
||||||
if (lastSession && lastSession.agentId === agent.id) {
|
|
||||||
// Try to resume the exact last conversation
|
|
||||||
// If it no longer exists, fall back to creating new
|
|
||||||
try {
|
|
||||||
// Load message history and pending approvals from the conversation
|
|
||||||
setLoadingState("checking");
|
|
||||||
const data = await getResumeData(
|
|
||||||
client,
|
|
||||||
agent,
|
|
||||||
lastSession.conversationId,
|
|
||||||
);
|
|
||||||
// Only set state after validation succeeds
|
|
||||||
conversationIdToUse = lastSession.conversationId;
|
|
||||||
setResumedExistingConversation(true);
|
|
||||||
setResumeData(data);
|
|
||||||
resumedSuccessfully = true;
|
|
||||||
} catch (error) {
|
|
||||||
// Only treat 404/422 as "not found", rethrow other errors
|
|
||||||
if (
|
|
||||||
error instanceof APIError &&
|
|
||||||
(error.status === 404 || error.status === 422)
|
|
||||||
) {
|
|
||||||
// Conversation no longer exists, will create new below
|
|
||||||
console.warn(
|
|
||||||
`Previous conversation ${lastSession.conversationId} not found, creating new`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resumedSuccessfully) {
|
|
||||||
// No valid session to resume - error with helpful message
|
|
||||||
console.error(
|
|
||||||
`Attempting to resume conversation ${lastSession?.conversationId ?? "(unknown)"}, but conversation was not found.`,
|
|
||||||
);
|
|
||||||
console.error(
|
|
||||||
"Resume the default conversation with 'letta', view recent conversations with 'letta --resume', or start a new conversation with 'letta --new'.",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else if (selectedConversationId) {
|
} else if (selectedConversationId) {
|
||||||
// Conversation selected from --resume selector or auto-restored from local project settings
|
// Conversation selected from --resume selector or auto-restored from local project settings
|
||||||
try {
|
try {
|
||||||
@@ -2049,7 +1897,6 @@ async function main(): Promise<void> {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
continueSession,
|
|
||||||
forceNew,
|
forceNew,
|
||||||
userRequestedNewAgent,
|
userRequestedNewAgent,
|
||||||
agentIdArg,
|
agentIdArg,
|
||||||
@@ -2059,7 +1906,6 @@ async function main(): Promise<void> {
|
|||||||
loadingState,
|
loadingState,
|
||||||
selectedGlobalAgentId,
|
selectedGlobalAgentId,
|
||||||
validatedAgent,
|
validatedAgent,
|
||||||
shouldContinue,
|
|
||||||
resumeAgentId,
|
resumeAgentId,
|
||||||
selectedConversationId,
|
selectedConversationId,
|
||||||
]);
|
]);
|
||||||
@@ -2182,7 +2028,6 @@ async function main(): Promise<void> {
|
|||||||
markMilestone("REACT_RENDER_START");
|
markMilestone("REACT_RENDER_START");
|
||||||
render(
|
render(
|
||||||
React.createElement(LoadingApp, {
|
React.createElement(LoadingApp, {
|
||||||
continueSession: shouldContinue,
|
|
||||||
forceNew: forceNew,
|
forceNew: forceNew,
|
||||||
initBlocks: initBlocks,
|
initBlocks: initBlocks,
|
||||||
baseTools: baseTools,
|
baseTools: baseTools,
|
||||||
|
|||||||
@@ -328,27 +328,3 @@ describe("Startup Flow - Integration", () => {
|
|||||||
{ timeout: 190000 },
|
{ timeout: 190000 },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// --continue Tests (depend on LRU state, harder to isolate)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
describe("Startup Flow - Continue Flag", () => {
|
|
||||||
test(
|
|
||||||
"--continue with no LRU shows error",
|
|
||||||
async () => {
|
|
||||||
const result = await runCli(
|
|
||||||
["--continue", "-p", "Say OK", "--output-format", "json"],
|
|
||||||
{
|
|
||||||
timeoutMs: 60000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Either succeeds (LRU exists) or fails with specific error
|
|
||||||
if (result.exitCode !== 0) {
|
|
||||||
expect(result.stderr).toContain("No recent session found");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ timeout: 70000 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const releaseNotes: Record<string, string> = {
|
|||||||
→ Read more: https://docs.letta.com/letta-code/changelog#0134`,
|
→ Read more: https://docs.letta.com/letta-code/changelog#0134`,
|
||||||
"0.13.0": `🎁 **Letta Code 0.13.0: Introducing Conversations!**
|
"0.13.0": `🎁 **Letta Code 0.13.0: Introducing Conversations!**
|
||||||
→ Letta Code now starts a new conversation on each startup (memory is shared across all conversations)
|
→ Letta Code now starts a new conversation on each startup (memory is shared across all conversations)
|
||||||
→ Use **/resume** to switch conversations, or run **letta --continue** to continue an existing conversation
|
→ Use **/resume** to switch conversations, or run **letta --conv <id>** to continue a specific conversation
|
||||||
→ Read more: https://docs.letta.com/letta-code/changelog#0130`,
|
→ Read more: https://docs.letta.com/letta-code/changelog#0130`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ describe("shared CLI arg schema", () => {
|
|||||||
test("rendered OPTIONS help is generated from catalog metadata", () => {
|
test("rendered OPTIONS help is generated from catalog metadata", () => {
|
||||||
const help = renderCliOptionsHelp();
|
const help = renderCliOptionsHelp();
|
||||||
expect(help).toContain("-h, --help");
|
expect(help).toContain("-h, --help");
|
||||||
expect(help).toContain("-c, --continue");
|
|
||||||
expect(help).toContain("--memfs-startup <m>");
|
expect(help).toContain("--memfs-startup <m>");
|
||||||
expect(help).toContain("Default: text");
|
expect(help).toContain("Default: text");
|
||||||
expect(help).not.toContain("--run");
|
expect(help).not.toContain("--run");
|
||||||
@@ -139,18 +138,9 @@ describe("shared CLI arg schema", () => {
|
|||||||
|
|
||||||
test("supports short aliases used by headless and interactive modes", () => {
|
test("supports short aliases used by headless and interactive modes", () => {
|
||||||
const parsed = parseCliArgs(
|
const parsed = parseCliArgs(
|
||||||
preprocessCliArgs([
|
preprocessCliArgs(["node", "script", "-p", "hello", "-C", "conv-123"]),
|
||||||
"node",
|
|
||||||
"script",
|
|
||||||
"-p",
|
|
||||||
"hello",
|
|
||||||
"-c",
|
|
||||||
"-C",
|
|
||||||
"conv-123",
|
|
||||||
]),
|
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
expect(parsed.values.continue).toBe(true);
|
|
||||||
expect(parsed.values.conversation).toBe("conv-123");
|
expect(parsed.values.conversation).toBe("conv-123");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -95,15 +95,6 @@ describe("Startup Flow - Flag Conflicts", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("--conversation conflicts with --continue", async () => {
|
|
||||||
const result = await runCli(["--conversation", "conv-123", "--continue"], {
|
|
||||||
expectExit: 1,
|
|
||||||
});
|
|
||||||
expect(result.stderr).toContain(
|
|
||||||
"--conversation cannot be used with --continue",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("--conversation conflicts with --import", async () => {
|
test("--conversation conflicts with --import", async () => {
|
||||||
const result = await runCli(
|
const result = await runCli(
|
||||||
["--conversation", "conv-123", "--import", "test.af"],
|
["--conversation", "conv-123", "--import", "test.af"],
|
||||||
@@ -197,14 +188,6 @@ describe("Startup Flow - Smoke", () => {
|
|||||||
expect(result.stderr).not.toContain("Unknown option '--memfs-startup'");
|
expect(result.stderr).not.toContain("Unknown option '--memfs-startup'");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("-c alias for --continue is accepted", async () => {
|
|
||||||
const result = await runCli(["-p", "Say OK", "-c"], {
|
|
||||||
expectExit: 1,
|
|
||||||
});
|
|
||||||
expect(result.stderr).toContain("Missing LETTA_API_KEY");
|
|
||||||
expect(result.stderr).not.toContain("Unknown option '-c'");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("-C alias for --conversation is accepted", async () => {
|
test("-C alias for --conversation is accepted", async () => {
|
||||||
const result = await runCli(["-p", "Say OK", "-C", "conv-123"], {
|
const result = await runCli(["-p", "Say OK", "-C", "conv-123"], {
|
||||||
expectExit: 1,
|
expectExit: 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user