feat: enhance team provisioning and activity timeline for improved messaging
- Updated TeamProvisioningService to mirror provisioning narration into the live cache, allowing visibility in Messages/Activity. - Modified TeamDetailView to include current lead session ID for better context in team interactions. - Enhanced ActivityTimeline to suppress reconnect boundaries based on current lead session ID, improving session clarity. - Adjusted MessageComposer to clarify provisioning status in UI messages and placeholders, enhancing user awareness during team launches.
This commit is contained in:
parent
afb6e2794f
commit
dd29bcefcf
4 changed files with 24 additions and 14 deletions
|
|
@ -3154,9 +3154,14 @@ export class TeamProvisioningService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Pre-ready: provisioning narration is shown in the ProvisioningProgressBlock banner
|
// Pre-ready: keep showing provisioning narration in the banner, but also mirror it
|
||||||
// (via provisioningOutputParts). Do NOT push to live cache to avoid duplicate display
|
// into the live cache so Messages/Activity can show the earliest assistant output.
|
||||||
// and leaking internal provisioning monologue into the Activity timeline.
|
if (!run.silentUserDmForward && !hasCapturedSendMessage) {
|
||||||
|
const cleanText = stripAgentBlocks(text).trim();
|
||||||
|
if (cleanText.length > 0) {
|
||||||
|
this.pushLiveLeadTextMessage(run, cleanText);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1606,6 +1606,7 @@ export const TeamDetailView = ({ teamName }: TeamDetailViewProps): React.JSX.Ele
|
||||||
expandOverrides={expandedSet}
|
expandOverrides={expandedSet}
|
||||||
onToggleExpandOverride={toggleExpandOverride}
|
onToggleExpandOverride={toggleExpandOverride}
|
||||||
teamSessionIds={teamSessionIds}
|
teamSessionIds={teamSessionIds}
|
||||||
|
currentLeadSessionId={data?.config.leadSessionId}
|
||||||
onMemberClick={setSelectedMember}
|
onMemberClick={setSelectedMember}
|
||||||
onCreateTaskFromMessage={(subject, description) => {
|
onCreateTaskFromMessage={(subject, description) => {
|
||||||
openCreateTaskDialog(subject, description);
|
openCreateTaskDialog(subject, description);
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,12 @@ interface ActivityTimelineProps {
|
||||||
onToggleExpandOverride?: (key: string) => void;
|
onToggleExpandOverride?: (key: string) => void;
|
||||||
/**
|
/**
|
||||||
* All session IDs belonging to this team (current + history).
|
* All session IDs belonging to this team (current + history).
|
||||||
* When two adjacent messages have different leadSessionId but both are in this set,
|
* Used together with currentLeadSessionId to suppress only the reconnect boundary
|
||||||
* the "New session" separator is suppressed (it's a reconnect, not a new session).
|
* from the current live session back into the team's previous session history.
|
||||||
*/
|
*/
|
||||||
teamSessionIds?: Set<string>;
|
teamSessionIds?: Set<string>;
|
||||||
|
/** Current lead session ID for the active team, if known. */
|
||||||
|
currentLeadSessionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIEWPORT_THRESHOLD = 0.15;
|
const VIEWPORT_THRESHOLD = 0.15;
|
||||||
|
|
@ -155,6 +157,7 @@ export const ActivityTimeline = ({
|
||||||
expandOverrides,
|
expandOverrides,
|
||||||
onToggleExpandOverride,
|
onToggleExpandOverride,
|
||||||
teamSessionIds,
|
teamSessionIds,
|
||||||
|
currentLeadSessionId,
|
||||||
}: ActivityTimelineProps): React.JSX.Element => {
|
}: ActivityTimelineProps): React.JSX.Element => {
|
||||||
const [visibleCount, setVisibleCount] = useState(MESSAGES_PAGE_SIZE);
|
const [visibleCount, setVisibleCount] = useState(MESSAGES_PAGE_SIZE);
|
||||||
|
|
||||||
|
|
@ -361,13 +364,15 @@ export const ActivityTimeline = ({
|
||||||
const prevSessionId = getItemSessionId(timelineItems[realIndex - 1]);
|
const prevSessionId = getItemSessionId(timelineItems[realIndex - 1]);
|
||||||
const currSessionId = getItemSessionId(item);
|
const currSessionId = getItemSessionId(item);
|
||||||
if (prevSessionId && currSessionId && prevSessionId !== currSessionId) {
|
if (prevSessionId && currSessionId && prevSessionId !== currSessionId) {
|
||||||
// Suppress separator when both sessions belong to the same team
|
// Suppress only the boundary between the current live session and the team's
|
||||||
// (reconnects produce new session IDs but are not "new sessions" from user's perspective)
|
// older session history. Older historical session boundaries should still render.
|
||||||
const isSameTeam =
|
const isReconnectBoundary =
|
||||||
|
!!currentLeadSessionId &&
|
||||||
teamSessionIds &&
|
teamSessionIds &&
|
||||||
teamSessionIds.has(prevSessionId) &&
|
teamSessionIds.has(prevSessionId) &&
|
||||||
teamSessionIds.has(currSessionId);
|
teamSessionIds.has(currSessionId) &&
|
||||||
if (!isSameTeam) {
|
(prevSessionId === currentLeadSessionId || currSessionId === currentLeadSessionId);
|
||||||
|
if (!isReconnectBoundary) {
|
||||||
sessionSeparator = (
|
sessionSeparator = (
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-3"
|
className="flex items-center gap-3"
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,6 @@ export const MessageComposer = ({
|
||||||
trimmed.length > 0 &&
|
trimmed.length > 0 &&
|
||||||
trimmed.length <= MAX_TEXT_LENGTH &&
|
trimmed.length <= MAX_TEXT_LENGTH &&
|
||||||
!sending &&
|
!sending &&
|
||||||
!isProvisioning &&
|
|
||||||
!attachmentsBlocked;
|
!attachmentsBlocked;
|
||||||
|
|
||||||
// Track whether we initiated a send — clear draft only on confirmed success
|
// Track whether we initiated a send — clear draft only on confirmed success
|
||||||
|
|
@ -345,7 +344,7 @@ export const MessageComposer = ({
|
||||||
<div className="ml-auto flex shrink-0 items-center gap-2">
|
<div className="ml-auto flex shrink-0 items-center gap-2">
|
||||||
{isProvisioning ? (
|
{isProvisioning ? (
|
||||||
<span className="text-[10px]" style={{ color: 'var(--warning-text)' }}>
|
<span className="text-[10px]" style={{ color: 'var(--warning-text)' }}>
|
||||||
Launching...
|
Launching... inbox delivery only
|
||||||
</span>
|
</span>
|
||||||
) : !isTeamAlive ? (
|
) : !isTeamAlive ? (
|
||||||
<span className="text-[10px]" style={{ color: 'var(--warning-text)' }}>
|
<span className="text-[10px]" style={{ color: 'var(--warning-text)' }}>
|
||||||
|
|
@ -462,7 +461,7 @@ export const MessageComposer = ({
|
||||||
id={`compose-${teamName}`}
|
id={`compose-${teamName}`}
|
||||||
placeholder={
|
placeholder={
|
||||||
isProvisioning
|
isProvisioning
|
||||||
? 'Team is launching... Please wait.'
|
? 'Team is launching... message will be queued for inbox delivery.'
|
||||||
: 'Write a message... (Enter to send, Shift+Enter for new line)'
|
: 'Write a message... (Enter to send, Shift+Enter for new line)'
|
||||||
}
|
}
|
||||||
value={draft.text}
|
value={draft.text}
|
||||||
|
|
@ -476,7 +475,7 @@ export const MessageComposer = ({
|
||||||
minRows={2}
|
minRows={2}
|
||||||
maxRows={6}
|
maxRows={6}
|
||||||
maxLength={MAX_TEXT_LENGTH}
|
maxLength={MAX_TEXT_LENGTH}
|
||||||
disabled={sending || isProvisioning}
|
disabled={sending}
|
||||||
cornerAction={
|
cornerAction={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* NOTE: ContextRing disabled — usage formula is inaccurate */}
|
{/* NOTE: ContextRing disabled — usage formula is inaccurate */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue