fix: add guard for default conversation id (#1245)
This commit is contained in:
@@ -6,7 +6,7 @@ import { APIError } from "@letta-ai/letta-client/core/error";
|
|||||||
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
|
import type { AgentState } from "@letta-ai/letta-client/resources/agents/agents";
|
||||||
import type { Message } from "@letta-ai/letta-client/resources/agents/messages";
|
import type { Message } from "@letta-ai/letta-client/resources/agents/messages";
|
||||||
import type { ApprovalRequest } from "../cli/helpers/stream";
|
import type { ApprovalRequest } from "../cli/helpers/stream";
|
||||||
import { debugWarn } from "../utils/debug";
|
import { debugLog, debugWarn } from "../utils/debug";
|
||||||
|
|
||||||
// Backfill should feel like "the last turn(s)", not "the last N raw messages".
|
// Backfill should feel like "the last turn(s)", not "the last N raw messages".
|
||||||
// Tool-heavy turns can generate many tool_call/tool_return messages that would
|
// Tool-heavy turns can generate many tool_call/tool_return messages that would
|
||||||
@@ -344,16 +344,21 @@ export async function getResumeData(
|
|||||||
|
|
||||||
// Use conversations API for explicit conversations,
|
// Use conversations API for explicit conversations,
|
||||||
// use agents API for "default" or no conversationId (agent's primary message history)
|
// use agents API for "default" or no conversationId (agent's primary message history)
|
||||||
const useConversationsApi = conversationId && conversationId !== "default";
|
const useConversationsApi =
|
||||||
|
conversationId &&
|
||||||
|
conversationId !== "default" &&
|
||||||
|
!conversationId.startsWith("agent-");
|
||||||
|
|
||||||
if (process.env.DEBUG) {
|
if (conversationId?.startsWith("agent-")) {
|
||||||
console.log(
|
debugWarn(
|
||||||
`[DEBUG] getResumeData: conversationId=${conversationId}, useConversationsApi=${useConversationsApi}, agentId=${agent.id}`,
|
"check-approval",
|
||||||
|
`getResumeData called with agent ID as conversationId: ${conversationId}\n${new Error().stack}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useConversationsApi) {
|
if (useConversationsApi) {
|
||||||
// Get conversation to access in_context_message_ids (source of truth)
|
// Get conversation to access in_context_message_ids (source of truth)
|
||||||
|
debugLog("conversations", `retrieve(${conversationId}) [getResumeData]`);
|
||||||
const conversation = await client.conversations.retrieve(conversationId);
|
const conversation = await client.conversations.retrieve(conversationId);
|
||||||
inContextMessageIds = conversation.in_context_message_ids;
|
inContextMessageIds = conversation.in_context_message_ids;
|
||||||
|
|
||||||
|
|||||||
@@ -3280,6 +3280,10 @@ export default function App({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const client = await getClient();
|
const client = await getClient();
|
||||||
|
debugLog(
|
||||||
|
"conversations",
|
||||||
|
`retrieve(${conversationId}) [syncConversationModel]`,
|
||||||
|
);
|
||||||
const conversation =
|
const conversation =
|
||||||
await client.conversations.retrieve(conversationId);
|
await client.conversations.retrieve(conversationId);
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
@@ -6154,7 +6158,9 @@ export default function App({
|
|||||||
// Build success message
|
// Build success message
|
||||||
const agentLabel = agent.name || targetAgentId;
|
const agentLabel = agent.name || targetAgentId;
|
||||||
const isSpecificConv =
|
const isSpecificConv =
|
||||||
opts?.conversationId && opts.conversationId !== "default";
|
opts?.conversationId &&
|
||||||
|
opts.conversationId !== "default" &&
|
||||||
|
!opts?.conversationId.startsWith("agent-");
|
||||||
const successOutput = isSpecificConv
|
const successOutput = isSpecificConv
|
||||||
? [
|
? [
|
||||||
`Switched to **${agentLabel}**`,
|
`Switched to **${agentLabel}**`,
|
||||||
@@ -8695,7 +8701,7 @@ export default function App({
|
|||||||
|
|
||||||
// Build export parameters (include conversation_id if in specific conversation)
|
// Build export parameters (include conversation_id if in specific conversation)
|
||||||
const exportParams: { conversation_id?: string } = {};
|
const exportParams: { conversation_id?: string } = {};
|
||||||
if (conversationId !== "default") {
|
if (conversationId !== "default" && conversationId !== agentId) {
|
||||||
exportParams.conversation_id = conversationId;
|
exportParams.conversation_id = conversationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12914,7 +12920,8 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
|
|||||||
: `letta --agent ${agentId}`}
|
: `letta --agent ${agentId}`}
|
||||||
</Text>
|
</Text>
|
||||||
{/* Only show conversation hint if not on default (default is resumed automatically) */}
|
{/* Only show conversation hint if not on default (default is resumed automatically) */}
|
||||||
{conversationId !== "default" && (
|
{conversationId !== "default" &&
|
||||||
|
conversationId !== agentId && (
|
||||||
<>
|
<>
|
||||||
<Box height={1} />
|
<Box height={1} />
|
||||||
<Text dimColor>Resume this conversation with:</Text>
|
<Text dimColor>Resume this conversation with:</Text>
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ export const AgentInfoBar = memo(function AgentInfoBar({
|
|||||||
{/* Phantom alien row + Conversation ID */}
|
{/* Phantom alien row + Conversation ID */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text>{alienLines[3]}</Text>
|
<Text>{alienLines[3]}</Text>
|
||||||
{conversationId && conversationId !== "default" ? (
|
{conversationId &&
|
||||||
|
conversationId !== "default" &&
|
||||||
|
!conversationId.startsWith("agent-") ? (
|
||||||
<Box width={rightWidth} flexShrink={1}>
|
<Box width={rightWidth} flexShrink={1}>
|
||||||
<Text dimColor wrap="truncate-end">
|
<Text dimColor wrap="truncate-end">
|
||||||
{truncateText(conversationId, rightWidth)}
|
{truncateText(conversationId, rightWidth)}
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ import type {
|
|||||||
StreamEvent,
|
StreamEvent,
|
||||||
SystemInitMessage,
|
SystemInitMessage,
|
||||||
} from "./types/protocol";
|
} from "./types/protocol";
|
||||||
|
import { debugLog } from "./utils/debug";
|
||||||
import {
|
import {
|
||||||
markMilestone,
|
markMilestone,
|
||||||
measureSinceMilestone,
|
measureSinceMilestone,
|
||||||
@@ -526,7 +527,10 @@ export async function handleHeadlessCommand(
|
|||||||
// Validate shared mutual-exclusion rules for startup flags.
|
// Validate shared mutual-exclusion rules for startup flags.
|
||||||
try {
|
try {
|
||||||
validateFlagConflicts({
|
validateFlagConflicts({
|
||||||
guard: specifiedConversationId && specifiedConversationId !== "default",
|
guard:
|
||||||
|
specifiedConversationId &&
|
||||||
|
specifiedConversationId !== "default" &&
|
||||||
|
!specifiedConversationId.startsWith("agent-"),
|
||||||
checks: [
|
checks: [
|
||||||
{
|
{
|
||||||
when: specifiedAgentId,
|
when: specifiedAgentId,
|
||||||
@@ -730,8 +734,16 @@ export async function handleHeadlessCommand(
|
|||||||
// Priority 0: --conversation derives agent from conversation ID.
|
// Priority 0: --conversation derives agent from conversation ID.
|
||||||
// "default" is a virtual agent-scoped conversation (not a retrievable conv-*).
|
// "default" is a virtual agent-scoped conversation (not a retrievable conv-*).
|
||||||
// It requires --agent and should not hit conversations.retrieve().
|
// It requires --agent and should not hit conversations.retrieve().
|
||||||
if (specifiedConversationId && specifiedConversationId !== "default") {
|
if (
|
||||||
|
specifiedConversationId &&
|
||||||
|
specifiedConversationId !== "default" &&
|
||||||
|
!specifiedConversationId.startsWith("agent-")
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
|
debugLog(
|
||||||
|
"conversations",
|
||||||
|
`retrieve(${specifiedConversationId}) [headless conv→agent lookup]`,
|
||||||
|
);
|
||||||
const conversation = await client.conversations.retrieve(
|
const conversation = await client.conversations.retrieve(
|
||||||
specifiedConversationId,
|
specifiedConversationId,
|
||||||
);
|
);
|
||||||
@@ -1007,6 +1019,10 @@ export async function handleHeadlessCommand(
|
|||||||
} else {
|
} else {
|
||||||
// User specified an explicit conversation to resume - validate it exists
|
// User specified an explicit conversation to resume - validate it exists
|
||||||
try {
|
try {
|
||||||
|
debugLog(
|
||||||
|
"conversations",
|
||||||
|
`retrieve(${specifiedConversationId}) [headless --conv validate]`,
|
||||||
|
);
|
||||||
await client.conversations.retrieve(specifiedConversationId);
|
await client.conversations.retrieve(specifiedConversationId);
|
||||||
conversationId = specifiedConversationId;
|
conversationId = specifiedConversationId;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -1030,6 +1046,10 @@ export async function handleHeadlessCommand(
|
|||||||
} else {
|
} else {
|
||||||
// Verify the conversation still exists
|
// Verify the conversation still exists
|
||||||
try {
|
try {
|
||||||
|
debugLog(
|
||||||
|
"conversations",
|
||||||
|
`retrieve(${lastSession.conversationId}) [headless lastSession resume]`,
|
||||||
|
);
|
||||||
await client.conversations.retrieve(lastSession.conversationId);
|
await client.conversations.retrieve(lastSession.conversationId);
|
||||||
conversationId = lastSession.conversationId;
|
conversationId = lastSession.conversationId;
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
10
src/index.ts
10
src/index.ts
@@ -47,6 +47,7 @@ import { settingsManager } from "./settings-manager";
|
|||||||
import { startStartupAutoUpdateCheck } from "./startup-auto-update";
|
import { startStartupAutoUpdateCheck } from "./startup-auto-update";
|
||||||
import { telemetry } from "./telemetry";
|
import { telemetry } from "./telemetry";
|
||||||
import { loadTools } from "./tools/manager";
|
import { loadTools } from "./tools/manager";
|
||||||
|
import { debugLog } from "./utils/debug";
|
||||||
import { markMilestone } from "./utils/timing";
|
import { markMilestone } from "./utils/timing";
|
||||||
|
|
||||||
// Stable empty array constants to prevent new references on every render
|
// Stable empty array constants to prevent new references on every render
|
||||||
@@ -629,7 +630,10 @@ async function main(): Promise<void> {
|
|||||||
// Validate shared mutual-exclusion rules for startup flags.
|
// Validate shared mutual-exclusion rules for startup flags.
|
||||||
try {
|
try {
|
||||||
validateFlagConflicts({
|
validateFlagConflicts({
|
||||||
guard: specifiedConversationId && specifiedConversationId !== "default",
|
guard:
|
||||||
|
specifiedConversationId &&
|
||||||
|
specifiedConversationId !== "default" &&
|
||||||
|
!specifiedConversationId.startsWith("agent-"),
|
||||||
checks: [
|
checks: [
|
||||||
{
|
{
|
||||||
when: specifiedAgentId,
|
when: specifiedAgentId,
|
||||||
@@ -1193,6 +1197,10 @@ async function main(): Promise<void> {
|
|||||||
|
|
||||||
// For explicit conversations, derive agent from conversation
|
// For explicit conversations, derive agent from conversation
|
||||||
try {
|
try {
|
||||||
|
debugLog(
|
||||||
|
"conversations",
|
||||||
|
`retrieve(${specifiedConversationId}) [TUI conv→agent lookup]`,
|
||||||
|
);
|
||||||
const conversation = await client.conversations.retrieve(
|
const conversation = await client.conversations.retrieve(
|
||||||
specifiedConversationId,
|
specifiedConversationId,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user