feat: add session review handoff flow

Introduce a desktop/web-only /handoff-review flow that generates a handoff from the active implementation session, creates or reuses a separate review session in the same directory, and links the pair through hidden OpenChamber session metadata.

Add review flow orchestration, metadata helpers, magic prompts, localized command/action labels, session metadata create/update support, and assistant message transfer actions for sending reviewer feedback back to the implementer or implementation responses back to the reviewer.

Review sessions are ordinary sessions, not child sessions. The flow avoids exposing session IDs or routing metadata to agents, hides review controls on mobile and VS Code, hides unrelated assistant actions inside review sessions, cleans up stale metadata where possible, and uses the optimistic send path so cross-session sends scroll like normal composer messages.
This commit is contained in:
Bohdan Triapitsyn
2026-06-07 01:22:40 +03:00
parent e0113c637d
commit 1f9769a932
19 changed files with 1381 additions and 13 deletions
+24 -1
View File
@@ -15,6 +15,7 @@ import { useInlineCommentDraftStore, type InlineCommentDraft } from '@/stores/us
import { useSnippetsStore } from '@/stores/useSnippetsStore';
import { appendInlineComments } from '@/lib/messages/inlineComments';
import { renderMagicPrompt } from '@/lib/magicPrompts';
import { startReviewFlow } from '@/lib/reviewFlow';
import { AttachedFilesList, AttachedVSCodeFileChips, ActiveEditorFileSuggestion } from './FileAttachment';
import ToolOutputDialog from './message/ToolOutputDialog';
import type { ToolPopupContent } from './message/types';
@@ -1106,10 +1107,11 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
const names = new Set<string>([
'init', 'review', 'undo', 'redo', 'timeline', 'compact', 'summary', 'workspace-review', 'plan-feature', 'catch-up', 'debug', 'weigh', 'explore',
]);
if (!isMobile && !isVSCodeRuntime()) names.add('handoff-review');
for (const command of availableCommands) names.add(command.name.toLowerCase());
for (const skill of availableSkills) names.add(skill.name.toLowerCase());
return names;
}, [availableCommands, availableSkills]);
}, [availableCommands, availableSkills, isMobile]);
// /command and /skill spans (primary color). Only tokens that match a known
// command/skill name are highlighted — partial/unknown tokens stay plain.
@@ -1937,6 +1939,27 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
}
return;
}
else if (commandName === 'handoff-review' && currentSessionId && !isMobile && !isVSCodeRuntime()) {
try {
const directory = useSessionUIStore.getState().getDirectoryForSession(currentSessionId) || currentDirectory || '';
if (!directory) {
throw new Error('Session directory is unavailable');
}
await startReviewFlow({
originalSessionID: currentSessionId,
directory,
providerID: providerIdToSend,
modelID: modelIdToSend,
agent: agentNameToSend,
variant: variantToSend,
agentMentionName,
});
scrollToBottom?.();
} catch (error) {
console.error('[review-flow] failed to start review flow', error);
}
return;
}
else if (commandName === 'plan-feature' && (currentSessionId || newSessionDraftOpen)) {
try {
await sessionActions.waitForConnectionOrThrow();