fix: task tool rendering issues (#534)
This commit is contained in:
@@ -43,13 +43,25 @@ export async function getResumeData(
|
||||
try {
|
||||
// Fetch messages from conversation or agent depending on what's provided
|
||||
let messages: Message[];
|
||||
// The source of truth for in-context message IDs:
|
||||
// - For conversations: conversation.in_context_message_ids
|
||||
// - For legacy agent-only: agent.message_ids
|
||||
let inContextMessageIds: string[] | null | undefined;
|
||||
|
||||
if (conversationId) {
|
||||
// Use conversations API for conversation-specific history
|
||||
messages = await client.conversations.messages.list(conversationId);
|
||||
// Fetch both messages and conversation state in parallel
|
||||
const [messagesResult, conversation] = await Promise.all([
|
||||
client.conversations.messages.list(conversationId),
|
||||
client.conversations.retrieve(conversationId),
|
||||
]);
|
||||
messages = messagesResult;
|
||||
inContextMessageIds = conversation.in_context_message_ids;
|
||||
} else {
|
||||
// Fall back to agent messages (legacy behavior)
|
||||
const messagesPage = await client.agents.messages.list(agent.id);
|
||||
messages = messagesPage.items;
|
||||
inContextMessageIds = agent.message_ids;
|
||||
}
|
||||
|
||||
if (!messages || messages.length === 0) {
|
||||
@@ -61,8 +73,7 @@ export async function getResumeData(
|
||||
}
|
||||
|
||||
// Compare cursor last message with in-context last message ID
|
||||
// The backend uses in-context messages for CONFLICT validation, so if they're
|
||||
// desynced, we need to check the in-context message for pending approvals
|
||||
// The source of truth is the conversation's (or agent's) in_context_message_ids
|
||||
const cursorLastMessage = messages[messages.length - 1];
|
||||
if (!cursorLastMessage) {
|
||||
return {
|
||||
@@ -73,8 +84,8 @@ export async function getResumeData(
|
||||
}
|
||||
|
||||
const inContextLastMessageId =
|
||||
agent.message_ids && agent.message_ids.length > 0
|
||||
? agent.message_ids[agent.message_ids.length - 1]
|
||||
inContextMessageIds && inContextMessageIds.length > 0
|
||||
? inContextMessageIds[inContextMessageIds.length - 1]
|
||||
: null;
|
||||
|
||||
// If there are no in-context messages, there can be no pending approval
|
||||
|
||||
@@ -7113,12 +7113,15 @@ Plan file path: ${planFilePath}`;
|
||||
// Skip Task tools that don't have a pending approval
|
||||
// They render as empty Boxes (ToolCallMessage returns null for non-finished Task tools)
|
||||
// which causes N blank lines when N Task tools are called in parallel
|
||||
// Note: pendingIds doesn't include the ACTIVE approval (currentApproval),
|
||||
// so we must also check if this is the active approval
|
||||
if (
|
||||
ln.kind === "tool_call" &&
|
||||
ln.name &&
|
||||
isTaskTool(ln.name) &&
|
||||
ln.toolCallId &&
|
||||
!pendingIds.has(ln.toolCallId)
|
||||
!pendingIds.has(ln.toolCallId) &&
|
||||
ln.toolCallId !== currentApproval?.toolCallId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -7462,21 +7465,68 @@ Plan file path: ${planFilePath}`;
|
||||
{/* Fallback approval UI when backfill is disabled (no liveItems) */}
|
||||
{liveItems.length === 0 && currentApproval && (
|
||||
<Box flexDirection="column">
|
||||
<InlineGenericApproval
|
||||
toolName={currentApproval.toolName}
|
||||
toolArgs={currentApproval.toolArgs}
|
||||
onApprove={() => handleApproveCurrent()}
|
||||
onApproveAlways={(scope) => handleApproveAlways(scope)}
|
||||
onDeny={(reason) => handleDenyCurrent(reason)}
|
||||
onCancel={handleCancelApprovals}
|
||||
isFocused={true}
|
||||
approveAlwaysText={
|
||||
currentApprovalContext?.approveAlwaysText
|
||||
}
|
||||
allowPersistence={
|
||||
currentApprovalContext?.allowPersistence ?? true
|
||||
}
|
||||
/>
|
||||
{isTaskTool(currentApproval.toolName) ? (
|
||||
<InlineTaskApproval
|
||||
taskInfo={(() => {
|
||||
try {
|
||||
const args = JSON.parse(
|
||||
currentApproval.toolArgs || "{}",
|
||||
);
|
||||
return {
|
||||
subagentType:
|
||||
typeof args.subagent_type === "string"
|
||||
? args.subagent_type
|
||||
: "unknown",
|
||||
description:
|
||||
typeof args.description === "string"
|
||||
? args.description
|
||||
: "(no description)",
|
||||
prompt:
|
||||
typeof args.prompt === "string"
|
||||
? args.prompt
|
||||
: "(no prompt)",
|
||||
model:
|
||||
typeof args.model === "string"
|
||||
? args.model
|
||||
: undefined,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
subagentType: "unknown",
|
||||
description: "(parse error)",
|
||||
prompt: "(parse error)",
|
||||
};
|
||||
}
|
||||
})()}
|
||||
onApprove={() => handleApproveCurrent()}
|
||||
onApproveAlways={(scope) => handleApproveAlways(scope)}
|
||||
onDeny={(reason) => handleDenyCurrent(reason)}
|
||||
onCancel={handleCancelApprovals}
|
||||
isFocused={true}
|
||||
approveAlwaysText={
|
||||
currentApprovalContext?.approveAlwaysText
|
||||
}
|
||||
allowPersistence={
|
||||
currentApprovalContext?.allowPersistence ?? true
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<InlineGenericApproval
|
||||
toolName={currentApproval.toolName}
|
||||
toolArgs={currentApproval.toolArgs}
|
||||
onApprove={() => handleApproveCurrent()}
|
||||
onApproveAlways={(scope) => handleApproveAlways(scope)}
|
||||
onDeny={(reason) => handleDenyCurrent(reason)}
|
||||
onCancel={handleCancelApprovals}
|
||||
isFocused={true}
|
||||
approveAlwaysText={
|
||||
currentApprovalContext?.approveAlwaysText
|
||||
}
|
||||
allowPersistence={
|
||||
currentApprovalContext?.allowPersistence ?? true
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1292,10 +1292,12 @@ async function main(): Promise<void> {
|
||||
setResumedExistingConversation(true);
|
||||
|
||||
// Load message history and pending approvals from the conversation
|
||||
// Re-fetch agent to get fresh message_ids for accurate pending approval detection
|
||||
setLoadingState("checking");
|
||||
const freshAgent = await client.agents.retrieve(agent.id);
|
||||
const data = await getResumeData(
|
||||
client,
|
||||
agent,
|
||||
freshAgent,
|
||||
specifiedConversationId,
|
||||
);
|
||||
setResumeData(data);
|
||||
@@ -1316,10 +1318,12 @@ async function main(): Promise<void> {
|
||||
setResumedExistingConversation(true);
|
||||
|
||||
// Load message history and pending approvals from the conversation
|
||||
// Re-fetch agent to get fresh message_ids for accurate pending approval detection
|
||||
setLoadingState("checking");
|
||||
const freshAgent = await client.agents.retrieve(agent.id);
|
||||
const data = await getResumeData(
|
||||
client,
|
||||
agent,
|
||||
freshAgent,
|
||||
lastSession.conversationId,
|
||||
);
|
||||
setResumeData(data);
|
||||
|
||||
Reference in New Issue
Block a user