feat: improve --resume and --continue CLI flags (#555)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -3910,8 +3910,9 @@ export default function App({
|
||||
return { submitted: true };
|
||||
}
|
||||
|
||||
// Special handling for /clear command - start new conversation
|
||||
if (msg.trim() === "/clear") {
|
||||
// Special handling for /clear and /new commands - start new conversation
|
||||
// (/new used to create a new agent, now it's just an alias for /clear)
|
||||
if (msg.trim() === "/clear" || msg.trim() === "/new") {
|
||||
const cmdId = uid("cmd");
|
||||
buffersRef.current.byId.set(cmdId, {
|
||||
kind: "command",
|
||||
@@ -4553,11 +4554,6 @@ export default function App({
|
||||
}
|
||||
|
||||
// Special handling for /new command - create new agent dialog
|
||||
if (msg.trim() === "/new") {
|
||||
setActiveOverlay("new");
|
||||
return { submitted: true };
|
||||
}
|
||||
|
||||
// Special handling for /pin command - pin current agent to project (or globally with -g)
|
||||
if (msg.trim() === "/pin" || msg.trim().startsWith("/pin ")) {
|
||||
const argsStr = msg.trim().slice(4).trim();
|
||||
@@ -7407,6 +7403,10 @@ Plan file path: ${planFilePath}`;
|
||||
await handleAgentSelect(id);
|
||||
}}
|
||||
onCancel={closeOverlay}
|
||||
onCreateNewAgent={() => {
|
||||
closeOverlay();
|
||||
setActiveOverlay("new");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -96,11 +96,11 @@ export const commands: Record<string, Command> = {
|
||||
|
||||
// === Page 2: Agent management (order 20-29) ===
|
||||
"/new": {
|
||||
desc: "Create a new agent and switch to it",
|
||||
desc: "Start a new conversation (same as /clear)",
|
||||
order: 20,
|
||||
handler: () => {
|
||||
// Handled specially in App.tsx
|
||||
return "Creating new agent...";
|
||||
// Handled specially in App.tsx - same as /clear
|
||||
return "Starting new conversation...";
|
||||
},
|
||||
},
|
||||
"/pin": {
|
||||
|
||||
@@ -16,6 +16,8 @@ interface AgentSelectorProps {
|
||||
currentAgentId: string;
|
||||
onSelect: (agentId: string) => void;
|
||||
onCancel: () => void;
|
||||
/** Called when user presses N to create a new agent */
|
||||
onCreateNewAgent?: () => void;
|
||||
/** The command that triggered this selector (e.g., "/agents" or "/resume") */
|
||||
command?: string;
|
||||
}
|
||||
@@ -111,6 +113,7 @@ export function AgentSelector({
|
||||
currentAgentId,
|
||||
onSelect,
|
||||
onCancel,
|
||||
onCreateNewAgent,
|
||||
command = "/agents",
|
||||
}: AgentSelectorProps) {
|
||||
const terminalWidth = useTerminalWidth();
|
||||
@@ -557,6 +560,9 @@ export function AgentSelector({
|
||||
}
|
||||
loadPinnedAgents();
|
||||
}
|
||||
} else if (input === "n" || input === "N") {
|
||||
// Create new agent
|
||||
onCreateNewAgent?.();
|
||||
} else if (activeTab !== "pinned" && input && !key.ctrl && !key.meta) {
|
||||
// Type to search (list tabs only)
|
||||
setSearchInput((prev) => prev + input);
|
||||
@@ -794,7 +800,7 @@ export function AgentSelector({
|
||||
: activeTab === "letta-code"
|
||||
? `Page ${lettaCodePage + 1}${lettaCodeHasMore ? "+" : `/${lettaCodeTotalPages || 1}`}${lettaCodeLoadingMore ? " (loading...)" : ""}`
|
||||
: `Page ${allPage + 1}${allHasMore ? "+" : `/${allTotalPages || 1}`}${allLoadingMore ? " (loading...)" : ""}`;
|
||||
const hintsText = `Enter select · ↑↓ navigate · ←→ page · Tab switch${activeTab === "pinned" ? " · P unpin" : " · Type to search"} · Esc cancel`;
|
||||
const hintsText = `Enter select · ↑↓ navigate · ←→ page · Tab switch${activeTab === "pinned" ? " · P unpin" : " · Type to search"}${onCreateNewAgent ? " · N new" : ""} · Esc cancel`;
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { Box, Text, useInput } from "ink";
|
||||
import { useState } from "react";
|
||||
import { DEFAULT_AGENT_NAME } from "../../constants";
|
||||
import { useTerminalWidth } from "../hooks/useTerminalWidth";
|
||||
import { colors } from "./colors";
|
||||
import { PasteAwareTextInput } from "./PasteAwareTextInput";
|
||||
import { validateAgentName } from "./PinDialog";
|
||||
|
||||
// Horizontal line character (matches other selectors)
|
||||
const SOLID_LINE = "─";
|
||||
|
||||
interface NewAgentDialogProps {
|
||||
onSubmit: (name: string) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export function NewAgentDialog({ onSubmit, onCancel }: NewAgentDialogProps) {
|
||||
const terminalWidth = useTerminalWidth();
|
||||
const solidLine = SOLID_LINE.repeat(Math.max(terminalWidth, 10));
|
||||
const [nameInput, setNameInput] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
|
||||
@@ -45,25 +51,37 @@ export function NewAgentDialog({ onSubmit, onCancel }: NewAgentDialogProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" paddingY={1}>
|
||||
<Box marginBottom={1}>
|
||||
<Text color={colors.approval.header} bold>
|
||||
Create new agent
|
||||
</Text>
|
||||
</Box>
|
||||
<Box flexDirection="column">
|
||||
{/* Command header */}
|
||||
<Text dimColor>{"> /agents"}</Text>
|
||||
<Text dimColor>{solidLine}</Text>
|
||||
|
||||
<Box marginBottom={1}>
|
||||
<Text dimColor>
|
||||
<Box height={1} />
|
||||
|
||||
{/* Title */}
|
||||
<Text bold color={colors.selector.title}>
|
||||
Create new agent
|
||||
</Text>
|
||||
|
||||
<Box height={1} />
|
||||
|
||||
{/* Description */}
|
||||
<Box paddingLeft={2}>
|
||||
<Text>
|
||||
Enter a name for your new agent, or press Enter for default.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Box marginBottom={1}>
|
||||
<Box height={1} />
|
||||
|
||||
{/* Input field */}
|
||||
<Box flexDirection="column">
|
||||
<Box paddingLeft={2}>
|
||||
<Text>Agent name:</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text color={colors.approval.header}>> </Text>
|
||||
<Text color={colors.selector.itemHighlighted}>{">"}</Text>
|
||||
<Text> </Text>
|
||||
<PasteAwareTextInput
|
||||
value={nameInput}
|
||||
onChange={(val) => {
|
||||
@@ -77,13 +95,16 @@ export function NewAgentDialog({ onSubmit, onCancel }: NewAgentDialogProps) {
|
||||
</Box>
|
||||
|
||||
{error && (
|
||||
<Box marginBottom={1}>
|
||||
<Box paddingLeft={2} marginTop={1}>
|
||||
<Text color="red">{error}</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box>
|
||||
<Text dimColor>Press Enter to create • Esc to cancel</Text>
|
||||
<Box height={1} />
|
||||
|
||||
{/* Footer hints */}
|
||||
<Box paddingLeft={2}>
|
||||
<Text dimColor>Enter create · Esc cancel</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -66,6 +66,8 @@ export async function handleHeadlessCommand(
|
||||
options: {
|
||||
// Flags used in headless mode
|
||||
continue: { type: "boolean", short: "c" },
|
||||
resume: { type: "boolean", short: "r" },
|
||||
conversation: { type: "string" },
|
||||
new: { type: "boolean" },
|
||||
agent: { type: "string", short: "a" },
|
||||
model: { type: "string", short: "m" },
|
||||
@@ -167,9 +169,21 @@ export async function handleHeadlessCommand(
|
||||
|
||||
const client = await getClient();
|
||||
|
||||
// Check for --resume flag (interactive only)
|
||||
if (values.resume) {
|
||||
console.error(
|
||||
"Error: --resume is for interactive mode only (opens conversation selector).\n" +
|
||||
"In headless mode, use:\n" +
|
||||
" --continue Resume the last session (agent + conversation)\n" +
|
||||
" --conversation <id> Resume a specific conversation by ID",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Resolve agent (same logic as interactive mode)
|
||||
let agent: AgentState | null = null;
|
||||
const specifiedAgentId = values.agent as string | undefined;
|
||||
const specifiedConversationId = values.conversation as string | undefined;
|
||||
const shouldContinue = values.continue as boolean | undefined;
|
||||
const forceNew = values.new as boolean | undefined;
|
||||
const systemPromptPreset = values.system as string | undefined;
|
||||
@@ -444,13 +458,55 @@ export async function handleHeadlessCommand(
|
||||
}
|
||||
}
|
||||
|
||||
// Always create a new conversation on startup for headless mode too
|
||||
// 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;
|
||||
// Determine which conversation to use
|
||||
let conversationId: string;
|
||||
|
||||
if (specifiedConversationId) {
|
||||
// User specified a conversation to resume
|
||||
try {
|
||||
await client.conversations.retrieve(specifiedConversationId);
|
||||
conversationId = specifiedConversationId;
|
||||
} catch {
|
||||
console.error(`Error: Conversation ${specifiedConversationId} not found`);
|
||||
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) {
|
||||
// Verify the conversation still exists
|
||||
try {
|
||||
await client.conversations.retrieve(lastSession.conversationId);
|
||||
conversationId = lastSession.conversationId;
|
||||
} catch {
|
||||
// Conversation no longer exists, create new
|
||||
const conversation = await client.conversations.create({
|
||||
agent_id: agent.id,
|
||||
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
|
||||
});
|
||||
conversationId = conversation.id;
|
||||
}
|
||||
} else {
|
||||
// No matching session, create new conversation
|
||||
const conversation = await client.conversations.create({
|
||||
agent_id: agent.id,
|
||||
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
|
||||
});
|
||||
conversationId = conversation.id;
|
||||
}
|
||||
} else {
|
||||
// Default: create a new conversation
|
||||
// This ensures isolated message history per CLI invocation
|
||||
const conversation = await client.conversations.create({
|
||||
agent_id: agent.id,
|
||||
isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
|
||||
});
|
||||
conversationId = conversation.id;
|
||||
}
|
||||
|
||||
// Save session (agent + conversation) to both project and global settings
|
||||
await settingsManager.loadLocalProjectSettings();
|
||||
@@ -546,6 +602,7 @@ export async function handleHeadlessCommand(
|
||||
subtype: "init",
|
||||
session_id: sessionId,
|
||||
agent_id: agent.id,
|
||||
conversation_id: conversationId,
|
||||
model: agent.llm_config?.model ?? "",
|
||||
tools:
|
||||
agent.tools?.map((t) => t.name).filter((n): n is string => !!n) || [],
|
||||
@@ -1360,6 +1417,7 @@ export async function handleHeadlessCommand(
|
||||
num_turns: stats.usage.stepCount,
|
||||
result: resultText,
|
||||
agent_id: agent.id,
|
||||
conversation_id: conversationId,
|
||||
usage: {
|
||||
prompt_tokens: stats.usage.promptTokens,
|
||||
completion_tokens: stats.usage.completionTokens,
|
||||
@@ -1395,6 +1453,7 @@ export async function handleHeadlessCommand(
|
||||
num_turns: stats.usage.stepCount,
|
||||
result: resultText,
|
||||
agent_id: agent.id,
|
||||
conversation_id: conversationId,
|
||||
run_ids: Array.from(allRunIds),
|
||||
usage: {
|
||||
prompt_tokens: stats.usage.promptTokens,
|
||||
@@ -1435,6 +1494,7 @@ async function runBidirectionalMode(
|
||||
subtype: "init",
|
||||
session_id: sessionId,
|
||||
agent_id: agent.id,
|
||||
conversation_id: conversationId,
|
||||
model: agent.llm_config?.model,
|
||||
tools: agent.tools?.map((t) => t.name) || [],
|
||||
cwd: process.cwd(),
|
||||
@@ -1898,6 +1958,7 @@ async function runBidirectionalMode(
|
||||
num_turns: numTurns,
|
||||
result: resultText,
|
||||
agent_id: agent.id,
|
||||
conversation_id: conversationId,
|
||||
run_ids: [],
|
||||
usage: null,
|
||||
uuid: `result-${agent.id}-${Date.now()}`,
|
||||
|
||||
116
src/index.ts
116
src/index.ts
@@ -9,6 +9,7 @@ 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 { ConversationSelector } from "./cli/components/ConversationSelector";
|
||||
import type { ApprovalRequest } from "./cli/helpers/stream";
|
||||
import { ProfileSelectionInline } from "./cli/profile-selection";
|
||||
import { permissionMode } from "./permissions/mode";
|
||||
@@ -30,6 +31,8 @@ Letta Code is a general purpose CLI for interacting with Letta agents
|
||||
USAGE
|
||||
# interactive TUI
|
||||
letta Resume from profile or create new agent (shows selector)
|
||||
letta --continue Resume last session (agent + conversation) directly
|
||||
letta --resume Open agent selector UI to pick agent/conversation
|
||||
letta --new Create a new agent directly (skip profile selector)
|
||||
letta --agent <id> Open a specific agent by ID
|
||||
|
||||
@@ -43,6 +46,8 @@ OPTIONS
|
||||
-h, --help Show this help and exit
|
||||
-v, --version Print version and exit
|
||||
--info Show current directory, skills, and pinned agents
|
||||
--continue Resume last session (agent + conversation) directly
|
||||
-r, --resume Open agent selector UI after loading
|
||||
--new Create new agent directly (skip profile selection)
|
||||
--init-blocks <list> Comma-separated memory blocks to initialize when using --new (e.g., "persona,skills")
|
||||
--base-tools <list> Comma-separated base tools to attach when using --new (e.g., "memory,web_search,conversation_search")
|
||||
@@ -415,16 +420,10 @@ async function main(): Promise<void> {
|
||||
process.exit(result.success ? 0 : 1);
|
||||
}
|
||||
|
||||
// Check for deprecated --continue flag
|
||||
if (values.continue) {
|
||||
console.error(
|
||||
"Error: --continue is deprecated. Did you mean --resume (-r)?\n" +
|
||||
" --resume resumes your last session (agent + conversation)",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const shouldResume = (values.resume as boolean | undefined) ?? false; // Resume last session
|
||||
// --continue: Resume last session (agent + conversation) automatically
|
||||
const shouldContinue = (values.continue as boolean | undefined) ?? false;
|
||||
// --resume: Open agent selector UI after loading
|
||||
const shouldResume = (values.resume as boolean | undefined) ?? false;
|
||||
const specifiedConversationId =
|
||||
(values.conversation as string | undefined) ?? null; // Specific conversation to resume
|
||||
const forceNew = (values.new as boolean | undefined) ?? false;
|
||||
@@ -818,6 +817,7 @@ async function main(): Promise<void> {
|
||||
const [loadingState, setLoadingState] = useState<
|
||||
| "selecting"
|
||||
| "selecting_global"
|
||||
| "selecting_conversation"
|
||||
| "assembling"
|
||||
| "importing"
|
||||
| "initializing"
|
||||
@@ -836,6 +836,11 @@ async function main(): Promise<void> {
|
||||
const [selectedGlobalAgentId, setSelectedGlobalAgentId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
// Track agent and conversation for conversation selector (--resume flag)
|
||||
const [resumeAgentId, setResumeAgentId] = useState<string | null>(null);
|
||||
const [selectedConversationId, setSelectedConversationId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
// Track when user explicitly requested new agent from selector (not via --new flag)
|
||||
const [userRequestedNewAgent, setUserRequestedNewAgent] = useState(false);
|
||||
|
||||
@@ -953,6 +958,28 @@ async function main(): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
// Handle --resume flag: show conversation selector directly
|
||||
if (shouldResume) {
|
||||
// Find the last-used agent for this project
|
||||
const lastSession =
|
||||
settingsManager.getLocalLastSession(process.cwd()) ??
|
||||
settingsManager.getGlobalLastSession();
|
||||
const lastAgentId = lastSession?.agentId ?? localSettings.lastAgent;
|
||||
|
||||
if (lastAgentId) {
|
||||
// Verify agent exists
|
||||
try {
|
||||
await client.agents.retrieve(lastAgentId);
|
||||
setResumeAgentId(lastAgentId);
|
||||
setLoadingState("selecting_conversation");
|
||||
return;
|
||||
} catch {
|
||||
// Agent doesn't exist, fall through to normal flow
|
||||
}
|
||||
}
|
||||
// No valid agent found, fall through to normal startup
|
||||
}
|
||||
|
||||
// Show selector if:
|
||||
// 1. No lastAgent in this project (fresh directory)
|
||||
// 2. No explicit flags that bypass selection (--new, --agent, --from-af, --continue)
|
||||
@@ -967,7 +994,7 @@ async function main(): Promise<void> {
|
||||
setLoadingState("assembling");
|
||||
}
|
||||
checkAndStart();
|
||||
}, [forceNew, agentIdArg, fromAfFile, continueSession]);
|
||||
}, [forceNew, agentIdArg, fromAfFile, continueSession, shouldResume]);
|
||||
|
||||
// Main initialization effect - runs after profile selection
|
||||
useEffect(() => {
|
||||
@@ -989,6 +1016,11 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 1.5: Use agent from conversation selector (--resume flag)
|
||||
if (!resumingAgentId && resumeAgentId) {
|
||||
resumingAgentId = resumeAgentId;
|
||||
}
|
||||
|
||||
// Priority 2: Use agent selected from global selector (user just picked one)
|
||||
// This takes precedence over stale LRU since user explicitly chose it
|
||||
const shouldCreateNew = forceNew || userRequestedNewAgent;
|
||||
@@ -1283,6 +1315,7 @@ async function main(): Promise<void> {
|
||||
|
||||
// Debug: log resume flag status
|
||||
if (process.env.DEBUG) {
|
||||
console.log(`[DEBUG] shouldContinue=${shouldContinue}`);
|
||||
console.log(`[DEBUG] shouldResume=${shouldResume}`);
|
||||
console.log(
|
||||
`[DEBUG] specifiedConversationId=${specifiedConversationId}`,
|
||||
@@ -1318,7 +1351,7 @@ async function main(): Promise<void> {
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else if (shouldResume) {
|
||||
} else if (shouldContinue) {
|
||||
// Try to load the last session for this agent
|
||||
const lastSession =
|
||||
settingsManager.getLocalLastSession(process.cwd()) ??
|
||||
@@ -1372,6 +1405,29 @@ async function main(): Promise<void> {
|
||||
});
|
||||
conversationIdToUse = conversation.id;
|
||||
}
|
||||
} else if (selectedConversationId) {
|
||||
// User selected a specific conversation from the --resume selector
|
||||
try {
|
||||
setLoadingState("checking");
|
||||
const freshAgent = await client.agents.retrieve(agent.id);
|
||||
const data = await getResumeData(
|
||||
client,
|
||||
freshAgent,
|
||||
selectedConversationId,
|
||||
);
|
||||
conversationIdToUse = selectedConversationId;
|
||||
setResumedExistingConversation(true);
|
||||
setResumeData(data);
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof APIError &&
|
||||
(error.status === 404 || error.status === 422)
|
||||
) {
|
||||
console.error(`Conversation ${selectedConversationId} not found`);
|
||||
process.exit(1);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
// Default: create a new conversation on startup
|
||||
// This ensures each CLI session has isolated message history
|
||||
@@ -1418,7 +1474,9 @@ async function main(): Promise<void> {
|
||||
fromAfFile,
|
||||
loadingState,
|
||||
selectedGlobalAgentId,
|
||||
shouldResume,
|
||||
shouldContinue,
|
||||
resumeAgentId,
|
||||
selectedConversationId,
|
||||
]);
|
||||
|
||||
// Wait for keybinding auto-install to complete before showing UI
|
||||
@@ -1431,6 +1489,25 @@ async function main(): Promise<void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Show conversation selector for --resume flag
|
||||
if (loadingState === "selecting_conversation" && resumeAgentId) {
|
||||
return React.createElement(ConversationSelector, {
|
||||
agentId: resumeAgentId,
|
||||
currentConversationId: "", // No current conversation yet
|
||||
onSelect: (conversationId: string) => {
|
||||
setSelectedConversationId(conversationId);
|
||||
setLoadingState("assembling");
|
||||
},
|
||||
onNewConversation: () => {
|
||||
// Start with a new conversation for this agent
|
||||
setLoadingState("assembling");
|
||||
},
|
||||
onCancel: () => {
|
||||
process.exit(0);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Show global agent selector in fresh repos with global pinned agents
|
||||
if (loadingState === "selecting_global") {
|
||||
return React.createElement(ProfileSelectionInline, {
|
||||
@@ -1451,11 +1528,18 @@ async function main(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
// At this point, loadingState is not "selecting", "selecting_global", or "selecting_conversation"
|
||||
// (those are handled above), so it's safe to pass to App
|
||||
const appLoadingState = loadingState as Exclude<
|
||||
typeof loadingState,
|
||||
"selecting" | "selecting_global" | "selecting_conversation"
|
||||
>;
|
||||
|
||||
if (!agentId || !conversationId) {
|
||||
return React.createElement(App, {
|
||||
agentId: "loading",
|
||||
conversationId: "loading",
|
||||
loadingState,
|
||||
loadingState: appLoadingState,
|
||||
continueSession: isResumingSession,
|
||||
startupApproval: resumeData?.pendingApproval ?? null,
|
||||
startupApprovals: resumeData?.pendingApprovals ?? EMPTY_APPROVAL_ARRAY,
|
||||
@@ -1470,7 +1554,7 @@ async function main(): Promise<void> {
|
||||
agentId,
|
||||
agentState,
|
||||
conversationId,
|
||||
loadingState,
|
||||
loadingState: appLoadingState,
|
||||
continueSession: isResumingSession,
|
||||
startupApproval: resumeData?.pendingApproval ?? null,
|
||||
startupApprovals: resumeData?.pendingApprovals ?? EMPTY_APPROVAL_ARRAY,
|
||||
@@ -1483,7 +1567,7 @@ async function main(): Promise<void> {
|
||||
|
||||
render(
|
||||
React.createElement(LoadingApp, {
|
||||
continueSession: shouldResume,
|
||||
continueSession: shouldContinue,
|
||||
forceNew: forceNew,
|
||||
initBlocks: initBlocks,
|
||||
baseTools: baseTools,
|
||||
|
||||
@@ -81,6 +81,7 @@ export interface SystemInitMessage extends MessageEnvelope {
|
||||
type: "system";
|
||||
subtype: "init";
|
||||
agent_id: string;
|
||||
conversation_id: string;
|
||||
model: string;
|
||||
tools: string[];
|
||||
cwd: string;
|
||||
@@ -217,6 +218,7 @@ export interface ResultMessage extends MessageEnvelope {
|
||||
type: "result";
|
||||
subtype: ResultSubtype;
|
||||
agent_id: string;
|
||||
conversation_id: string;
|
||||
duration_ms: number;
|
||||
duration_api_ms: number;
|
||||
num_turns: number;
|
||||
|
||||
Reference in New Issue
Block a user