Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class EntryAbility extends UIAbility {

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
const scenarioId = want.parameters?.['openbitfunDesignPreview'];
if (scenarioId === 'history-scroll' || scenarioId === 'durable-timeline' || scenarioId === 'composer-submit' || scenarioId === 'catalog-refresh' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-dark' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'subagent-process-dark' || scenarioId === 'file-visibility' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector' || scenarioId === 'device-selector-dark' || scenarioId === 'welcome-home' || scenarioId === 'connected-conversation' ||
if (scenarioId === 'history-scroll' || scenarioId === 'durable-timeline' || scenarioId === 'composer-submit' || scenarioId === 'catalog-refresh' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-dark' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'subagent-process-dark' || scenarioId === 'subagent-swarm' || scenarioId === 'subagent-swarm-dark' || scenarioId === 'file-visibility' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector' || scenarioId === 'device-selector-dark' || scenarioId === 'welcome-home' || scenarioId === 'connected-conversation' ||
scenarioId === 'streaming-dark' ||
scenarioId === 'reconnecting-wide' || scenarioId === 'narrow-multiline' ||
scenarioId === 'fold-context' || scenarioId === 'long-reading' ||
Expand All @@ -25,11 +25,16 @@ export default class EntryAbility extends UIAbility {
this.previewStorage = new LocalStorage();
this.previewStorage.setOrCreate('scenarioId', scenarioId);
AppStorage.setOrCreate('scenarioId', scenarioId);
// A preview can ask for a variant of its scenario without a scenario id of
// its own, because the gallery routes by the id alone. Anything not asked
// for is the scenario's own default.
const option = want.parameters?.['openbitfunDesignOption'] as string | undefined;
AppStorage.setOrCreate('previewOption', option ?? '');
}
try {
const colorMode = scenarioId === 'cold-start-home-dark' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector-dark' || scenarioId === 'subagent-process-dark' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector-dark' || scenarioId === 'streaming-dark' || scenarioId === 'interaction-mailbox-dark' || scenarioId === 'copy-selection-dark'
const colorMode = scenarioId === 'cold-start-home-dark' || scenarioId === 'catalog-refresh-dark' || scenarioId === 'model-role-selector-dark' || scenarioId === 'subagent-process-dark' || scenarioId === 'subagent-swarm-dark' || scenarioId === 'file-visibility-dark' || scenarioId === 'immersive-bottom-dark' || scenarioId === 'immersive-bottom-drawer-dark' || scenarioId === 'immersive-bottom-settings-dark' || scenarioId === 'composer-overflow-dark' || scenarioId === 'composer-overflow-expanded-dark' || scenarioId === 'device-selector-dark' || scenarioId === 'streaming-dark' || scenarioId === 'interaction-mailbox-dark' || scenarioId === 'copy-selection-dark'
? ConfigurationConstant.ColorMode.COLOR_MODE_DARK
: scenarioId === 'cold-start-home' || scenarioId === 'catalog-refresh' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'file-visibility' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'device-selector' || scenarioId === 'connected-conversation' || scenarioId === 'reconnecting-wide' || scenarioId === 'copy-selection' || scenarioId === 'copy-selection-nav' || scenarioId === 'copy-selection-wide'
: scenarioId === 'cold-start-home' || scenarioId === 'catalog-refresh' || scenarioId === 'model-role-selector' || scenarioId === 'model-role-selector-wide' || scenarioId === 'subagent-process' || scenarioId === 'subagent-swarm' || scenarioId === 'file-visibility' || scenarioId === 'immersive-bottom' || scenarioId === 'immersive-bottom-wide' || scenarioId === 'immersive-bottom-drawer' || scenarioId === 'immersive-bottom-settings' || scenarioId === 'immersive-bottom-cover' || scenarioId === 'immersive-bottom-miniapps' || scenarioId === 'immersive-bottom-wide-shell' || scenarioId === 'immersive-bottom-tools' || scenarioId === 'immersive-bottom-tools-editor' || scenarioId === 'immersive-bottom-tools-file-action' || scenarioId === 'immersive-bottom-view-settings' || scenarioId === 'immersive-bottom-connect' || scenarioId === 'composer-overflow' || scenarioId === 'composer-overflow-empty' || scenarioId === 'composer-overflow-wide' || scenarioId === 'composer-overflow-expanded' || scenarioId === 'device-selector' || scenarioId === 'connected-conversation' || scenarioId === 'reconnecting-wide' || scenarioId === 'copy-selection' || scenarioId === 'copy-selection-nav' || scenarioId === 'copy-selection-wide'
? ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT
: ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;
this.context.getApplicationContext().setColorMode(colorMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ export interface RemoteToolStatusResponse {
error_preview?: string;
exit_code?: number;
plan?: RemotePlanToolResponse;
/**
* Child Session a launching tool started, when this record is a delegation.
*
* The durable record names it (`subagent_session_id` on the item data), and
* this projection keeps the name instead of dropping it: a transcript restored
* from the phone's cache is the only thing a cold or offline session view has,
* and the link is what maps a stored child back to the branch that owns it.
*/
subagent_session_id?: string;
}

export interface RemotePlanToolResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum ConversationIntentType {
OpenFilePreview = 'open_file_preview',
DownloadFile = 'download_file',
BuildPlan = 'build_plan',
BranchExpanded = 'branch_expanded',
Send = 'send',
VoiceInput = 'voice_input',
ChatInputChanged = 'chat_input_changed'
Expand Down Expand Up @@ -154,4 +155,12 @@ export class ConversationIntents {
new PlanBuildRequest(path, name)
);
}

/**
* The reader opened a delegation card. The branch id is the tool call that
* launched the worker, which is what its records are found by.
*/
static branchExpanded(branchId: string): ConversationIntent {
return new ConversationIntent(ConversationIntentType.BranchExpanded, '', branchId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ConversationIntentDispatcherHooks {
readonly openFilePreview: (route: AppRoute, request: FilePreviewRequest) => void;
readonly downloadFile: (path: string) => void;
readonly buildPlan: (path: string, name: string) => Promise<void>;
readonly branchExpanded: (branchId: string) => void;
readonly send: () => Promise<void>;
readonly voiceInput: () => Promise<void>;
readonly inputChanged: (route: AppRoute, value: string) => void;
Expand Down Expand Up @@ -70,6 +71,7 @@ export class ConversationIntentDispatcher {
void this.hooks.buildPlan(intent.planBuildRequest.path, intent.planBuildRequest.name);
}
return;
case ConversationIntentType.BranchExpanded: this.hooks.branchExpanded(intent.toolId); return;
case ConversationIntentType.Send: void this.hooks.send(); return;
case ConversationIntentType.VoiceInput: void this.hooks.voiceInput(); return;
case ConversationIntentType.ChatInputChanged: this.hooks.inputChanged(route, intent.value); return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ChatMessageStructurePolicy, StructuredRenderGroup } from '../policy/Cha
import { AssistantProcessLayoutPolicy } from '../policy/AssistantProcessLayoutPolicy';
import { ConversationMessageRenderPolicy } from '../policy/ConversationMessageRenderPolicy';
import { ThinkingPresentationPolicy } from '../policy/ThinkingPresentationPolicy';
import { SubagentBranchExpansionState } from '../state/SubagentBranchExpansionState';
import { ACCENT, INK, LINE, STATUS_DANGER } from './Theme';
import { MessageBodyMarkdown, MessageFileCards, MessageImageGallery, MessageMarkdown } from './ChatMessageContent';
import {
Expand Down Expand Up @@ -89,6 +90,14 @@ export struct ChatMessageBubble {
@Event onOpenFilePreview: (path: string, label: string) => void = (_path: string, _label: string) => {};
@Event onBuildPlan: (path: string, name: string) => void = (_path: string, _name: string) => {};
@Event onDownloadFile: (path: string) => void = (_path: string) => {};
/**
* Disclosure of the delegation cards in this message, owned by the
* conversation: the row and the structured subtree under it are rebuilt
* whenever the turn is republished, and disclosure has to outlive that.
*/
@Param subagentBranches: SubagentBranchExpansionState = new SubagentBranchExpansionState();
/** Raised when the reader opens a delegation card, so its worker is followed. */
@Event onBranchExpanded: (branchId: string) => void = (_branchId: string) => {};
private readonly fileReferenceCache: MessageFileReferenceProjectionCache =
new MessageFileReferenceProjectionCache();

Expand Down Expand Up @@ -415,10 +424,13 @@ export struct ChatMessageBubble {
interactionMailbox: this.interactionMailbox,
title: this.subagentTitle(this.currentSubagentBranch(entry, path)),
path: path,
branchId: entry.tool?.id || '',
status: this.currentSubagentBranch(entry, path).tool?.status ||
(activeScope ? 'running' : 'completed'),
items: this.currentSubagentBranch(entry, path).subItems || [],
expansion: this.subagentBranches,
renderRevision: this.renderRevision,
onBranchExpanded: this.onBranchExpanded,
onApproveTool: this.onApproveTool,
onRejectTool: this.onRejectTool,
onCancelTool: this.onCancelTool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ObservableChatTimelineItem
} from '../../model/ChatTimelineModels';
import { ChatSurface } from '../state/ChatSurface';
import { SubagentBranchExpansionState } from '../state/SubagentBranchExpansionState';
import { MessageCopyScope } from '../actions/ConversationIntent';
import {
CARD,
Expand Down Expand Up @@ -65,6 +66,10 @@ struct ChatAssistantTimelineRow {
@Event onOpenFilePreview: (path: string, label: string) => void = (_path: string, _label: string) => {};
@Event onBuildPlan: (path: string, name: string) => void = (_path: string, _name: string) => {};
@Event onDownloadFile: (path: string) => void = (_path: string) => {};
/** Disclosure of this row's delegation cards, owned above the row. */
@Param subagentBranches: SubagentBranchExpansionState = new SubagentBranchExpansionState();
/** Raised when the reader opens a delegation card, so its worker is followed. */
@Event onBranchExpanded: (branchId: string) => void = (_branchId: string) => {};

aboutToAppear(): void {
this.synchronizeMessage();
Expand Down Expand Up @@ -97,6 +102,7 @@ struct ChatAssistantTimelineRow {
fileDownloadStatus: this.fileDownloadStatus,
activeFilePreviewPath: this.activeFilePreviewPath,
activeFilePreviewLoading: this.activeFilePreviewLoading,
subagentBranches: this.subagentBranches,
onApproveTool: this.onApproveTool,
onRejectTool: this.onRejectTool,
onCancelTool: this.onCancelTool,
Expand All @@ -107,7 +113,8 @@ struct ChatAssistantTimelineRow {
onRetryMessage: this.onRetryMessage,
onOpenFilePreview: this.onOpenFilePreview,
onBuildPlan: this.onBuildPlan,
onDownloadFile: this.onDownloadFile
onDownloadFile: this.onDownloadFile,
onBranchExpanded: this.onBranchExpanded
})
}

Expand Down Expand Up @@ -154,6 +161,15 @@ export struct ChatTimeline {
@Param activeFilePreviewPath: string = '';
@Param activeFilePreviewLoading: boolean = false;
@Param maxContentWidth: number = 0;
/**
* Disclosure of the delegation cards in this transcript.
*
* A conversation projection passes the instance its state owns, so a card the
* reader opened stays open while the rows around it are rebuilt. A host that
* passes nothing — the previews — gets one per timeline, which is still wider
* than the row and the card it outlives.
*/
@Param subagentBranches: SubagentBranchExpansionState = new SubagentBranchExpansionState();
/**
* How much of the pane the floating header and composer cover. The list runs
* the full height behind them, so without these the first and last messages
Expand All @@ -176,6 +192,8 @@ export struct ChatTimeline {
@Event onOpenFilePreview: (path: string, label: string) => void = (_path: string, _label: string) => {};
@Event onBuildPlan: (path: string, name: string) => void = (_path: string, _name: string) => {};
@Event onDownloadFile: (path: string) => void = (_path: string) => {};
/** Raised when the reader opens a delegation card, so its worker is followed. */
@Event onBranchExpanded: (branchId: string) => void = (_branchId: string) => {};

// Says whether an open reuses this subtree or rebuilds it. A rebuild means
// every bubble is constructed from nothing, and the cost of that is the
Expand Down Expand Up @@ -272,6 +290,7 @@ export struct ChatTimeline {
fileDownloadStatus: this.fileDownloadStatus,
activeFilePreviewPath: this.activeFilePreviewPath,
activeFilePreviewLoading: this.activeFilePreviewLoading,
subagentBranches: this.subagentBranches,
onApproveTool: (toolId: string, updatedInput?: Object) => {
this.onApproveTool(toolId, updatedInput);
},
Expand Down Expand Up @@ -304,6 +323,9 @@ export struct ChatTimeline {
},
onDownloadFile: (path: string) => {
this.onDownloadFile(path);
},
onBranchExpanded: (branchId: string) => {
this.onBranchExpanded(branchId);
}
})
.reuse({ reuseId: () => 'chat_assistant' })
Expand Down Expand Up @@ -333,6 +355,7 @@ export struct ChatTimeline {
fileDownloadStatus: this.fileDownloadStatus,
activeFilePreviewPath: this.activeFilePreviewPath,
activeFilePreviewLoading: this.activeFilePreviewLoading,
subagentBranches: this.subagentBranches,
onApproveTool: this.onApproveTool,
onRejectTool: this.onRejectTool,
onCancelTool: this.onCancelTool,
Expand All @@ -343,7 +366,8 @@ export struct ChatTimeline {
onRetryMessage: this.onRetryMessage,
onOpenFilePreview: this.onOpenFilePreview,
onBuildPlan: this.onBuildPlan,
onDownloadFile: this.onDownloadFile
onDownloadFile: this.onDownloadFile,
onBranchExpanded: this.onBranchExpanded
})
}
.onAppear(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ export struct ConversationView {
activeFilePreviewPath: this.activeFilePreviewPath,
activeFilePreviewLoading: this.activeFilePreviewLoading,
maxContentWidth: this.composerPresentation === ComposerPresentation.Floating ? 760 : 0,
subagentBranches: this.state.subagentBranches,
topInset: this.hoverOperateMode ? 0 : this.topInset,
bottomInset: this.hoverOperateMode ? 0 : this.bottomInset,
onLoadOlder: () => {
Expand Down Expand Up @@ -457,6 +458,9 @@ export struct ConversationView {
},
onDownloadFile: (path: string) => {
this.onDownloadFile(path);
},
onBranchExpanded: (branchId: string) => {
this.onIntent(ConversationIntents.branchExpanded(branchId));
}
})
}
Expand Down
Loading
Loading