Merge pull request #3171 from pocharlies-org/feat/btw-boundary

feat(btw): make a btw session a side question, not a continuation of the parent
This commit is contained in:
Bohdan Triapitsyn
2026-08-28 01:24:03 +03:00
committed by GitHub
6 changed files with 211 additions and 19 deletions
+14 -2
View File
@@ -35,7 +35,8 @@ import {
import { ReviewFlowDialog, type ReviewFlowExecution } from '@/components/session/ReviewFlowDialog';
import { BtwPanel } from './btw/BtwPanel';
import { useBtwPanelState } from './btw/useBtwPanelState';
import { destroyBtwSession, startBtwSession, type BtwSessionRef } from '@/lib/btw';
import { wasPromotedBtwSession } from '@/lib/sessionBtwMetadata';
import { BTW_BOUNDARY_INSTRUCTION, BTW_PROMOTION_NOTICE, destroyBtwSession, startBtwSession, type BtwSessionRef } from '@/lib/btw';
import { AttachedFilesList, AttachedVSCodeFileChips, ActiveEditorFileSuggestion } from './FileAttachment';
import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery';
import type { ToolPopupContent } from './message/types';
@@ -340,6 +341,10 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({
[btwDirectory, btwSessionId, currentSessionId],
);
const isBtwActive = Boolean(btwSessionRef) && !btwPanel.collapsed;
// A session promoted out of `/btw` keeps the boundary instructions in its
// transcript — there is no way to delete a message part — so it has to say
// they no longer apply.
const isPromotedBtwSession = wasPromotedBtwSession(btwPanel.parentSession);
const activeRuntimeKey = getRuntimeKey();
const chatDraftIdentity = React.useMemo(
() => createChatDraftIdentity(
@@ -1129,7 +1134,14 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({
composerText: !queuedOnly && inputSnapshot.hasContent ? inputSnapshot.message : null,
composerAttachments: attachedFiles,
inlineComments: drafts,
syntheticTexts: syntheticParts?.map((part) => part.text) ?? [],
// btw mode: the boundary rides with every send, not just the
// first one, so the inherited transcript stays reference material
// for the whole side conversation.
syntheticTexts: [
...(isBtwActive ? [BTW_BOUNDARY_INSTRUCTION] : []),
...(isPromotedBtwSession ? [BTW_PROMOTION_NOTICE] : []),
...(syntheticParts?.map((part) => part.text) ?? []),
],
linkedIssue: linkedIssue
? { number: linkedIssue.number, title: linkedIssue.title, url: linkedIssue.url, contextText: linkedIssue.contextText }
: null,
@@ -5,6 +5,8 @@ import { getBtwBoundaryMessageID, getBtwSessionID } from '@/lib/sessionBtwMetada
import { useBtwStore } from '@/stores/useBtwStore';
export type BtwPanelState = {
/** The session the composer is in — the one `/btw` would fork. */
parentSession: Session | null;
/** The active fork for this parent, or null when no panel should exist. */
btwSessionId: string | null;
btwSession: Session | null;
@@ -40,6 +42,7 @@ export function useBtwPanelState(
const destroying = Boolean(uiState?.destroying);
const btwSessionId = btwSession && !destroying ? linkedBtwSessionId : null;
return {
parentSession: parentSession ?? null,
btwSessionId,
btwSession: btwSessionId ? btwSession : null,
// SAFETY: the SDK Session type omits the server's `directory` field; this