@tutti-os/agent-gui 0.0.203 → 0.0.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -90,6 +90,36 @@ accepted for host capabilities that are not agent activity data:
90
90
  AgentGUI has no host-API activity fallback. A host must inject the runtime and
91
91
  the grouped `AgentGUINodeProps` responsibility objects.
92
92
 
93
+ ## Standalone Conversation Participant Presentation
94
+
95
+ The `@tutti-os/agent-gui/agent-conversation` entrypoint exposes one optional,
96
+ host-owned participant presentation contract on `WorkspaceAgentSessionDetail`,
97
+ `AgentConversationFlow`, and `AgentTranscriptView`:
98
+
99
+ ```tsx
100
+ <WorkspaceAgentSessionDetail
101
+ participantPresentation={{
102
+ enabled: true,
103
+ status: "ready",
104
+ user: { name: "Alice", avatarUrl: userAvatarUrl },
105
+ agent: { name: "Codex", avatarUrl: agentAvatarUrl }
106
+ }}
107
+ {...props}
108
+ />
109
+ ```
110
+
111
+ Omitting the property or passing `{ enabled: false }` preserves the existing
112
+ transcript DOM and spacing. Pass `{ enabled: true, status: "loading" }` while
113
+ the host is resolving identities; existing messages stay visible and the
114
+ package renders fixed-size circular loading slots. In the `ready` state, each
115
+ participant requires a non-empty `name`; `avatarUrl` is optional and the shared
116
+ UI System `Avatar` falls back to the name's initial.
117
+
118
+ The host owns identity lookup and lifecycle. Agent GUI owns placement, sizing,
119
+ loading treatment, image fallback, and left/right message alignment. The
120
+ contract is presentation-only and must not be copied into canonical Session,
121
+ Turn, Message, or workspace-engine state.
122
+
93
123
  ## Session Handoff Drafts
94
124
 
95
125
  External AgentGUI hosts can use `createAgentSessionHandoffPrompt` to prefill a
@@ -9,6 +9,22 @@ import '../types-CHAMK2g7.js';
9
9
  import '../planImplementationPresentation-BxVOLt6A.js';
10
10
  import '@tutti-os/workspace-user-project/contracts';
11
11
 
12
+ interface AgentConversationParticipantIdentity {
13
+ name: string;
14
+ avatarUrl?: string | null;
15
+ }
16
+ type AgentConversationParticipantPresentation = {
17
+ enabled: false;
18
+ } | {
19
+ enabled: true;
20
+ status: "loading";
21
+ } | {
22
+ enabled: true;
23
+ status: "ready";
24
+ user: AgentConversationParticipantIdentity;
25
+ agent: AgentConversationParticipantIdentity;
26
+ };
27
+
12
28
  interface AgentTranscriptTurnAttachment {
13
29
  id: string;
14
30
  anchorTurnId: string | null;
@@ -27,6 +43,7 @@ interface AgentTranscriptViewProps {
27
43
  workspaceAppIcons?: readonly AgentMessageMarkdownWorkspaceAppIcon[];
28
44
  previewMode?: boolean;
29
45
  showRawTimelineJson?: boolean;
46
+ participantPresentation?: AgentConversationParticipantPresentation;
30
47
  labels: {
31
48
  toolCallsLabel: (count: number) => string;
32
49
  thinkingLabel: string;
@@ -53,6 +70,7 @@ interface AgentConversationFlowProps {
53
70
  workspaceAppIcons?: readonly AgentMessageMarkdownWorkspaceAppIcon[];
54
71
  previewMode?: boolean;
55
72
  showRawTimelineJson?: boolean;
73
+ participantPresentation?: AgentConversationParticipantPresentation;
56
74
  labels: {
57
75
  toolCallsLabel: (count: number) => string;
58
76
  thinkingLabel: string;
@@ -80,8 +98,9 @@ interface WorkspaceAgentSessionDetailProps {
80
98
  loadingLabel?: string;
81
99
  rawTimelineJsonLabel?: string;
82
100
  showRawTimelineJson?: boolean;
101
+ participantPresentation?: AgentConversationParticipantPresentation;
83
102
  }
84
- declare function WorkspaceAgentSessionDetail({ detail, avoidGroupingEdits, isLoading, timelineItemCount, onLinkAction, toolCallsLabel, thinkingLabel, loadingLabel, rawTimelineJsonLabel, showRawTimelineJson }: WorkspaceAgentSessionDetailProps): JSX.Element;
103
+ declare function WorkspaceAgentSessionDetail({ detail, avoidGroupingEdits, isLoading, timelineItemCount, onLinkAction, toolCallsLabel, thinkingLabel, loadingLabel, rawTimelineJsonLabel, showRawTimelineJson, participantPresentation }: WorkspaceAgentSessionDetailProps): JSX.Element;
85
104
 
86
105
  interface AgentConversationProjectionOptions {
87
106
  avoidGroupingEdits?: boolean;
@@ -129,4 +148,4 @@ declare function serializeAgentConversationForClipboard(input: {
129
148
  title: string;
130
149
  }): Promise<AgentGUIConversationSerializedTranscript>;
131
150
 
132
- export { AgentConversationFlow, AgentConversationVM, type AgentGUIConversationAttachment, type AgentGUIConversationCopyAction, type AgentGUIConversationCopyLabels, type AgentGUIConversationLocalImageReader, type AgentGUIConversationSerializedTranscript, AgentTranscriptSkeleton, AgentTranscriptView, WorkspaceAgentSessionDetail, WorkspaceAgentSessionDetailViewModel, projectAgentConversationVM, reconcileProjectedAgentConversationVM, serializeAgentConversationForClipboard, useProjectedAgentConversation };
151
+ export { AgentConversationFlow, type AgentConversationFlowProps, type AgentConversationParticipantIdentity, type AgentConversationParticipantPresentation, AgentConversationVM, type AgentGUIConversationAttachment, type AgentGUIConversationCopyAction, type AgentGUIConversationCopyLabels, type AgentGUIConversationLocalImageReader, type AgentGUIConversationSerializedTranscript, AgentTranscriptSkeleton, AgentTranscriptView, type AgentTranscriptViewProps, WorkspaceAgentSessionDetail, type WorkspaceAgentSessionDetailProps, WorkspaceAgentSessionDetailViewModel, projectAgentConversationVM, reconcileProjectedAgentConversationVM, serializeAgentConversationForClipboard, useProjectedAgentConversation };
@@ -7,7 +7,7 @@ import {
7
7
  reconcileProjectedAgentConversationVM,
8
8
  serializeAgentConversationForClipboard,
9
9
  useProjectedAgentConversation
10
- } from "../chunk-NEH45ROF.js";
10
+ } from "../chunk-GJZEMI6Z.js";
11
11
  import "../chunk-XD5NTTFJ.js";
12
12
  import "../chunk-CXC3ROXQ.js";
13
13
  import "../chunk-XJ34OIEQ.js";
@@ -15,7 +15,7 @@ import "../chunk-JDVP33S5.js";
15
15
  import "../chunk-JXFOFJYI.js";
16
16
  import "../chunk-3V5OCA2M.js";
17
17
  import "../chunk-FGLQZ6I4.js";
18
- import "../chunk-FFMVDJOE.js";
18
+ import "../chunk-ZTRTBLLP.js";
19
19
  import "../chunk-ZVYUTXQJ.js";
20
20
  import "../chunk-K5AOJ5DO.js";
21
21
  import "../chunk-F5UR6EJG.js";
@@ -44,7 +44,8 @@ function WorkspaceAgentSessionDetail({
44
44
  thinkingLabel = translate("agentHost.workspaceAgentSessionDetailThinking"),
45
45
  loadingLabel = translate("common.loading"),
46
46
  rawTimelineJsonLabel,
47
- showRawTimelineJson = false
47
+ showRawTimelineJson = false,
48
+ participantPresentation
48
49
  }) {
49
50
  const conversation = useProjectedAgentConversation({
50
51
  detail,
@@ -75,6 +76,7 @@ function WorkspaceAgentSessionDetail({
75
76
  empty: emptyState,
76
77
  onLinkAction,
77
78
  showRawTimelineJson,
79
+ participantPresentation,
78
80
  labels: flowLabels
79
81
  }
80
82
  ) });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../shared/WorkspaceAgentSessionDetail.tsx"],"sourcesContent":["import { useMemo, type JSX } from \"react\";\nimport type { WorkspaceLinkAction } from \"../contexts/workspace/presentation/renderer/actions/workspaceLinkActions\";\nimport { translate } from \"../i18n/index\";\nimport { AgentConversationFlow } from \"./agentConversation/components/AgentConversationFlow\";\nimport { useProjectedAgentConversation } from \"./agentConversation/projection/useProjectedAgentConversation\";\nimport type { WorkspaceAgentSessionDetailViewModel } from \"./workspaceAgentSessionDetailViewModel\";\n\ninterface WorkspaceAgentSessionDetailProps {\n detail: WorkspaceAgentSessionDetailViewModel;\n avoidGroupingEdits?: boolean;\n isLoading: boolean;\n timelineItemCount: number;\n onLinkAction?: (action: WorkspaceLinkAction) => void;\n toolCallsLabel: (count: number) => string;\n thinkingLabel?: string;\n loadingLabel?: string;\n rawTimelineJsonLabel?: string;\n showRawTimelineJson?: boolean;\n}\n\nexport function WorkspaceAgentSessionDetail({\n detail,\n avoidGroupingEdits = false,\n isLoading,\n timelineItemCount,\n onLinkAction,\n toolCallsLabel,\n thinkingLabel = translate(\"agentHost.workspaceAgentSessionDetailThinking\"),\n loadingLabel = translate(\"common.loading\"),\n rawTimelineJsonLabel,\n showRawTimelineJson = false\n}: WorkspaceAgentSessionDetailProps): JSX.Element {\n const conversation = useProjectedAgentConversation({\n detail,\n avoidGroupingEdits\n });\n const showLoadingSkeleton =\n detail.turns.length === 0 &&\n (isLoading ||\n detail.activity.status === \"waiting\" ||\n detail.activity.status === \"working\");\n const emptySummary =\n detail.activity.latestActivitySummary ||\n (timelineItemCount > 0\n ? translate(\"agentHost.workspaceAgentSessionDetailEmptyWithTimeline\")\n : translate(\"agentHost.workspaceAgentSessionDetailEmptyNoTimeline\"));\n const flowLabels = useMemo(\n () => ({\n thinkingLabel,\n toolCallsLabel,\n processing: translate(\"agentHost.agentGui.processing\"),\n turnSummary: translate(\"agentHost.agentGui.turnSummary\"),\n rawTimelineJson: rawTimelineJsonLabel\n }),\n [rawTimelineJsonLabel, thinkingLabel, toolCallsLabel]\n );\n const emptyState = useMemo(\n () => (\n <div className=\"workspace-agents-status-panel__detail-empty\">\n {emptySummary}\n </div>\n ),\n [emptySummary]\n );\n\n return (\n <div className=\"workspace-agents-status-panel__detail\">\n <AgentConversationFlow\n conversation={detail.turns.length > 0 ? conversation : null}\n isLoading={showLoadingSkeleton}\n loadingLabel={loadingLabel}\n empty={emptyState}\n onLinkAction={onLinkAction}\n showRawTimelineJson={showRawTimelineJson}\n labels={flowLabels}\n />\n </div>\n );\n}\n\nexport function WorkspaceAgentSessionDetailSkeleton({\n loading = true,\n loadingLabel = translate(\"common.loading\")\n}: {\n loading?: boolean;\n loadingLabel?: string;\n}): JSX.Element {\n const flowLabels = useMemo(\n () => ({\n thinkingLabel: translate(\"agentHost.workspaceAgentSessionDetailThinking\"),\n toolCallsLabel: (count: number) =>\n translate(\"agentHost.workspaceAgentSessionDetailToolCalls\", { count }),\n processing: translate(\"agentHost.agentGui.processing\"),\n turnSummary: translate(\"agentHost.agentGui.turnSummary\")\n }),\n []\n );\n\n if (!loading) {\n return <></>;\n }\n\n return (\n <div\n className=\"workspace-agents-status-panel__detail-skeleton\"\n data-testid=\"workspace-agents-status-panel-detail-skeleton\"\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel}\n >\n <AgentConversationFlow\n conversation={null}\n isLoading\n loadingLabel={loadingLabel}\n loadingTestId=\"workspace-agents-status-panel-detail-skeleton\"\n empty={null}\n labels={flowLabels}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAyB;AA0D5B,SAyCK,UAzCL;AAtCC,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,UAAU,+CAA+C;AAAA,EACzE,eAAe,UAAU,gBAAgB;AAAA,EACzC;AAAA,EACA,sBAAsB;AACxB,GAAkD;AAChD,QAAM,eAAe,8BAA8B;AAAA,IACjD;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,sBACJ,OAAO,MAAM,WAAW,MACvB,aACC,OAAO,SAAS,WAAW,aAC3B,OAAO,SAAS,WAAW;AAC/B,QAAM,eACJ,OAAO,SAAS,0BACf,oBAAoB,IACjB,UAAU,wDAAwD,IAClE,UAAU,sDAAsD;AACtE,QAAM,aAAa;AAAA,IACjB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,YAAY,UAAU,+BAA+B;AAAA,MACrD,aAAa,UAAU,gCAAgC;AAAA,MACvD,iBAAiB;AAAA,IACnB;AAAA,IACA,CAAC,sBAAsB,eAAe,cAAc;AAAA,EACtD;AACA,QAAM,aAAa;AAAA,IACjB,MACE,oBAAC,SAAI,WAAU,+CACZ,wBACH;AAAA,IAEF,CAAC,YAAY;AAAA,EACf;AAEA,SACE,oBAAC,SAAI,WAAU,yCACb;AAAA,IAAC;AAAA;AAAA,MACC,cAAc,OAAO,MAAM,SAAS,IAAI,eAAe;AAAA,MACvD,WAAW;AAAA,MACX;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,QAAQ;AAAA;AAAA,EACV,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../shared/WorkspaceAgentSessionDetail.tsx"],"sourcesContent":["import { useMemo, type JSX } from \"react\";\nimport type { WorkspaceLinkAction } from \"../contexts/workspace/presentation/renderer/actions/workspaceLinkActions\";\nimport { translate } from \"../i18n/index\";\nimport { AgentConversationFlow } from \"./agentConversation/components/AgentConversationFlow\";\nimport type { AgentConversationParticipantPresentation } from \"./agentConversation/contracts/agentConversationParticipantPresentation\";\nimport { useProjectedAgentConversation } from \"./agentConversation/projection/useProjectedAgentConversation\";\nimport type { WorkspaceAgentSessionDetailViewModel } from \"./workspaceAgentSessionDetailViewModel\";\n\nexport interface WorkspaceAgentSessionDetailProps {\n detail: WorkspaceAgentSessionDetailViewModel;\n avoidGroupingEdits?: boolean;\n isLoading: boolean;\n timelineItemCount: number;\n onLinkAction?: (action: WorkspaceLinkAction) => void;\n toolCallsLabel: (count: number) => string;\n thinkingLabel?: string;\n loadingLabel?: string;\n rawTimelineJsonLabel?: string;\n showRawTimelineJson?: boolean;\n participantPresentation?: AgentConversationParticipantPresentation;\n}\n\nexport function WorkspaceAgentSessionDetail({\n detail,\n avoidGroupingEdits = false,\n isLoading,\n timelineItemCount,\n onLinkAction,\n toolCallsLabel,\n thinkingLabel = translate(\"agentHost.workspaceAgentSessionDetailThinking\"),\n loadingLabel = translate(\"common.loading\"),\n rawTimelineJsonLabel,\n showRawTimelineJson = false,\n participantPresentation\n}: WorkspaceAgentSessionDetailProps): JSX.Element {\n const conversation = useProjectedAgentConversation({\n detail,\n avoidGroupingEdits\n });\n const showLoadingSkeleton =\n detail.turns.length === 0 &&\n (isLoading ||\n detail.activity.status === \"waiting\" ||\n detail.activity.status === \"working\");\n const emptySummary =\n detail.activity.latestActivitySummary ||\n (timelineItemCount > 0\n ? translate(\"agentHost.workspaceAgentSessionDetailEmptyWithTimeline\")\n : translate(\"agentHost.workspaceAgentSessionDetailEmptyNoTimeline\"));\n const flowLabels = useMemo(\n () => ({\n thinkingLabel,\n toolCallsLabel,\n processing: translate(\"agentHost.agentGui.processing\"),\n turnSummary: translate(\"agentHost.agentGui.turnSummary\"),\n rawTimelineJson: rawTimelineJsonLabel\n }),\n [rawTimelineJsonLabel, thinkingLabel, toolCallsLabel]\n );\n const emptyState = useMemo(\n () => (\n <div className=\"workspace-agents-status-panel__detail-empty\">\n {emptySummary}\n </div>\n ),\n [emptySummary]\n );\n\n return (\n <div className=\"workspace-agents-status-panel__detail\">\n <AgentConversationFlow\n conversation={detail.turns.length > 0 ? conversation : null}\n isLoading={showLoadingSkeleton}\n loadingLabel={loadingLabel}\n empty={emptyState}\n onLinkAction={onLinkAction}\n showRawTimelineJson={showRawTimelineJson}\n participantPresentation={participantPresentation}\n labels={flowLabels}\n />\n </div>\n );\n}\n\nexport function WorkspaceAgentSessionDetailSkeleton({\n loading = true,\n loadingLabel = translate(\"common.loading\")\n}: {\n loading?: boolean;\n loadingLabel?: string;\n}): JSX.Element {\n const flowLabels = useMemo(\n () => ({\n thinkingLabel: translate(\"agentHost.workspaceAgentSessionDetailThinking\"),\n toolCallsLabel: (count: number) =>\n translate(\"agentHost.workspaceAgentSessionDetailToolCalls\", { count }),\n processing: translate(\"agentHost.agentGui.processing\"),\n turnSummary: translate(\"agentHost.agentGui.turnSummary\")\n }),\n []\n );\n\n if (!loading) {\n return <></>;\n }\n\n return (\n <div\n className=\"workspace-agents-status-panel__detail-skeleton\"\n data-testid=\"workspace-agents-status-panel-detail-skeleton\"\n role=\"status\"\n aria-busy=\"true\"\n aria-label={loadingLabel}\n >\n <AgentConversationFlow\n conversation={null}\n isLoading\n loadingLabel={loadingLabel}\n loadingTestId=\"workspace-agents-status-panel-detail-skeleton\"\n empty={null}\n labels={flowLabels}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAyB;AA6D5B,SA0CK,UA1CL;AAvCC,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,UAAU,+CAA+C;AAAA,EACzE,eAAe,UAAU,gBAAgB;AAAA,EACzC;AAAA,EACA,sBAAsB;AAAA,EACtB;AACF,GAAkD;AAChD,QAAM,eAAe,8BAA8B;AAAA,IACjD;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,sBACJ,OAAO,MAAM,WAAW,MACvB,aACC,OAAO,SAAS,WAAW,aAC3B,OAAO,SAAS,WAAW;AAC/B,QAAM,eACJ,OAAO,SAAS,0BACf,oBAAoB,IACjB,UAAU,wDAAwD,IAClE,UAAU,sDAAsD;AACtE,QAAM,aAAa;AAAA,IACjB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,YAAY,UAAU,+BAA+B;AAAA,MACrD,aAAa,UAAU,gCAAgC;AAAA,MACvD,iBAAiB;AAAA,IACnB;AAAA,IACA,CAAC,sBAAsB,eAAe,cAAc;AAAA,EACtD;AACA,QAAM,aAAa;AAAA,IACjB,MACE,oBAAC,SAAI,WAAU,+CACZ,wBACH;AAAA,IAEF,CAAC,YAAY;AAAA,EACf;AAEA,SACE,oBAAC,SAAI,WAAU,yCACb;AAAA,IAAC;AAAA;AAAA,MACC,cAAc,OAAO,MAAM,SAAS,IAAI,eAAe;AAAA,MACvD,WAAW;AAAA,MACX;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA;AAAA,EACV,GACF;AAEJ;","names":[]}
package/dist/agent-gui.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AgentGUI
3
- } from "./chunk-LHT2UBPE.js";
3
+ } from "./chunk-STOGLKFA.js";
4
4
  import "./chunk-LB4AGT7B.js";
5
5
  import "./chunk-A4WCTHWS.js";
6
6
  import "./chunk-X5V6P2EB.js";
@@ -9,7 +9,7 @@ import "./chunk-4CHBM3G3.js";
9
9
  import "./chunk-CBMWHQ7P.js";
10
10
  import "./chunk-MGSRWYRN.js";
11
11
  import "./chunk-GWKXYGUN.js";
12
- import "./chunk-NEH45ROF.js";
12
+ import "./chunk-GJZEMI6Z.js";
13
13
  import "./chunk-XD5NTTFJ.js";
14
14
  import "./chunk-CXC3ROXQ.js";
15
15
  import "./chunk-UP3ZDYTN.js";
@@ -25,9 +25,9 @@ import "./chunk-JXFOFJYI.js";
25
25
  import "./chunk-3V5OCA2M.js";
26
26
  import "./chunk-RMQREVJY.js";
27
27
  import "./chunk-FGLQZ6I4.js";
28
- import "./chunk-RD3ZAYNV.js";
28
+ import "./chunk-FHCARLIO.js";
29
29
  import "./chunk-EVQ4JY3N.js";
30
- import "./chunk-FFMVDJOE.js";
30
+ import "./chunk-ZTRTBLLP.js";
31
31
  import "./chunk-ZVYUTXQJ.js";
32
32
  import "./chunk-UEBPMWKS.js";
33
33
  import "./chunk-YMXYBG7U.js";
@@ -8,7 +8,7 @@ import {
8
8
  isPromptRequestIdTitle,
9
9
  latestPlanTurnId,
10
10
  planImplementationPromptFromPlanTurn
11
- } from "../chunk-RD3ZAYNV.js";
11
+ } from "../chunk-FHCARLIO.js";
12
12
  import {
13
13
  useEngineSelector
14
14
  } from "../chunk-EVQ4JY3N.js";
@@ -21,7 +21,7 @@ import {
21
21
  isExitPlanSwitchModeInput,
22
22
  normalizeAgentApprovalPurpose,
23
23
  normalizeAskUserQuestions
24
- } from "../chunk-FFMVDJOE.js";
24
+ } from "../chunk-ZTRTBLLP.js";
25
25
  import {
26
26
  cn
27
27
  } from "../chunk-ZVYUTXQJ.js";
@@ -3731,6 +3731,38 @@ aside.workspace-agents-status-panel
3731
3731
  justify-items: end;
3732
3732
  }
3733
3733
 
3734
+ .agent-gui-conversation__participant-message-layout {
3735
+ display: grid;
3736
+ width: 100%;
3737
+ min-width: 0;
3738
+ column-gap: 10px;
3739
+ align-items: start;
3740
+ }
3741
+
3742
+ .agent-gui-conversation__participant-message-layout[data-agent-message-speaker="assistant"] {
3743
+ grid-template-columns: 32px minmax(0, 1fr);
3744
+ }
3745
+
3746
+ .agent-gui-conversation__participant-message-layout[data-agent-message-speaker="user"] {
3747
+ grid-template-columns: minmax(0, 1fr) 32px;
3748
+ }
3749
+
3750
+ .agent-gui-conversation__participant-message-content {
3751
+ display: grid;
3752
+ width: 100%;
3753
+ min-width: 0;
3754
+ }
3755
+
3756
+ .agent-gui-conversation__participant-message-layout[data-agent-message-speaker="user"]
3757
+ > .agent-gui-conversation__participant-message-content {
3758
+ row-gap: 14px;
3759
+ justify-items: end;
3760
+ }
3761
+
3762
+ .agent-gui-conversation__participant-avatar {
3763
+ margin-top: 2px;
3764
+ }
3765
+
3734
3766
  .agent-gui-conversation__message-group {
3735
3767
  position: relative;
3736
3768
  display: grid;
@@ -2,7 +2,7 @@ import {
2
2
  AgentGUIConversation_styles_default,
3
3
  MessageSquareMoreIcon,
4
4
  extractAgentMcpToolTarget
5
- } from "./chunk-FFMVDJOE.js";
5
+ } from "./chunk-ZTRTBLLP.js";
6
6
  import {
7
7
  getOptionalAgentHostApi,
8
8
  useOptionalAgentHostApi
@@ -1518,4 +1518,4 @@ export {
1518
1518
  isPromptRequestIdTitle,
1519
1519
  AgentInteractivePromptSurface
1520
1520
  };
1521
- //# sourceMappingURL=chunk-RD3ZAYNV.js.map
1521
+ //# sourceMappingURL=chunk-FHCARLIO.js.map
@@ -47,7 +47,7 @@ import {
47
47
  resolveWorkspaceFileLinkAction,
48
48
  resolveWorkspaceFilePathCandidate,
49
49
  resolveWorkspaceLinkAction
50
- } from "./chunk-FFMVDJOE.js";
50
+ } from "./chunk-ZTRTBLLP.js";
51
51
  import {
52
52
  cn
53
53
  } from "./chunk-ZVYUTXQJ.js";
@@ -5745,6 +5745,7 @@ import {
5745
5745
  useEffect as useEffect6,
5746
5746
  useState as useState10
5747
5747
  } from "react";
5748
+ import { Avatar } from "@tutti-os/ui-system";
5748
5749
  import { CheckIcon, CopyIcon } from "@tutti-os/ui-system/icons";
5749
5750
 
5750
5751
  // shared/errors/appError.ts
@@ -7612,7 +7613,8 @@ function AgentMessageBlock({
7612
7613
  workspaceAppIcons,
7613
7614
  previewMode = false,
7614
7615
  showRawTimelineJson = false,
7615
- rawTimelineJsonLabel = ""
7616
+ rawTimelineJsonLabel = "",
7617
+ participantPresentation
7616
7618
  }) {
7617
7619
  "use memo";
7618
7620
  const agentHostApi = useOptionalAgentHostApi();
@@ -7665,6 +7667,110 @@ function AgentMessageBlock({
7665
7667
  },
7666
7668
  thinking.id
7667
7669
  )) : null;
7670
+ const messageContent = row.messages.map((message) => {
7671
+ const rawTimelineJson2 = showRawTimelineJson && rawTimelineJsonLabel && (message.sourceTimelineItems?.length ?? 0) > 0 ? /* @__PURE__ */ jsx18(
7672
+ RawTimelineJsonDisclosure,
7673
+ {
7674
+ items: message.sourceTimelineItems,
7675
+ label: rawTimelineJsonLabel
7676
+ }
7677
+ ) : null;
7678
+ const recoveredError = !isUser && !message.visibleError ? recoverVisibleErrorFromMessage(message, provider) : null;
7679
+ const content = isUser && message.contentKind === "image-grid" ? /* @__PURE__ */ jsx18(AgentUserImageGrid, { message }) : isUser ? /* @__PURE__ */ jsx18(
7680
+ AgentRichTextReadonly,
7681
+ {
7682
+ value: message.body,
7683
+ className: `workspace-agents-status-panel__detail-user-message ${AgentGUIConversation_styles_default.userMessageBubble}`,
7684
+ editorClassName: "text-[inherit]",
7685
+ onLinkClick: handleLinkClick,
7686
+ availableSkills,
7687
+ workspaceAppIcons
7688
+ }
7689
+ ) : message.visibleError ? /* @__PURE__ */ jsx18(
7690
+ AgentVisibleErrorMessage,
7691
+ {
7692
+ message,
7693
+ onAuthLogin,
7694
+ onExternalLink: handleLinkClick
7695
+ }
7696
+ ) : recoveredError ? /* @__PURE__ */ jsx18(
7697
+ AgentVisibleErrorMessage,
7698
+ {
7699
+ message: recoveredError,
7700
+ onAuthLogin,
7701
+ onExternalLink: handleLinkClick
7702
+ }
7703
+ ) : message.systemNotice ? /* @__PURE__ */ jsx18(AgentSystemNoticeMessage, { message }) : message.contentKind === "collaboration" && message.collaboration ? /* @__PURE__ */ jsx18(
7704
+ AgentCollaborationRow,
7705
+ {
7706
+ collaboration: message.collaboration,
7707
+ workspaceRoot,
7708
+ basePath,
7709
+ onLinkAction,
7710
+ workspaceAppIcons,
7711
+ previewMode
7712
+ }
7713
+ ) : message.contentKind === "plan" ? /* @__PURE__ */ jsx18(
7714
+ AgentPlanCardMessage,
7715
+ {
7716
+ message,
7717
+ workspaceRoot,
7718
+ basePath,
7719
+ onLinkAction,
7720
+ workspaceAppIcons,
7721
+ previewMode
7722
+ }
7723
+ ) : /* @__PURE__ */ jsx18(
7724
+ AgentMessageMarkdown,
7725
+ {
7726
+ content: message.body,
7727
+ className: AgentGUIConversation_styles_default.assistantMarkdown,
7728
+ onLinkAction,
7729
+ workspaceLinkContext: {
7730
+ workspaceRoot,
7731
+ basePath,
7732
+ source: "agent-markdown"
7733
+ },
7734
+ workspaceAppIcons,
7735
+ enableImageZoom: true,
7736
+ previewMode,
7737
+ streaming: message.statusKind === "working"
7738
+ }
7739
+ );
7740
+ if (rawTimelineJson2) {
7741
+ return /* @__PURE__ */ jsxs10(
7742
+ AgentCopyableMessageGroup,
7743
+ {
7744
+ copyText: message.copyText ?? null,
7745
+ occurredAtUnixMs: message.occurredAtUnixMs,
7746
+ speaker: row.speaker,
7747
+ onCopyMessageText: handleCopyMessageText,
7748
+ children: [
7749
+ content,
7750
+ rawTimelineJson2
7751
+ ]
7752
+ },
7753
+ message.id
7754
+ );
7755
+ }
7756
+ const copyText = message.copyText ?? null;
7757
+ if (copyText) {
7758
+ return /* @__PURE__ */ jsx18(
7759
+ AgentCopyableMessageGroup,
7760
+ {
7761
+ copyText,
7762
+ occurredAtUnixMs: message.occurredAtUnixMs,
7763
+ speaker: row.speaker,
7764
+ onCopyMessageText: handleCopyMessageText,
7765
+ children: content
7766
+ },
7767
+ message.id
7768
+ );
7769
+ }
7770
+ return /* @__PURE__ */ jsx18(Fragment2, { children: content }, message.id);
7771
+ });
7772
+ const enabledParticipantPresentation = participantPresentation?.enabled === true ? participantPresentation : null;
7773
+ const showParticipant = enabledParticipantPresentation !== null && row.messages.length > 0;
7668
7774
  return /* @__PURE__ */ jsxs10(
7669
7775
  "div",
7670
7776
  {
@@ -7673,112 +7779,64 @@ function AgentMessageBlock({
7673
7779
  "data-agent-message-flow-thinking-last": !isUser && row.thinking.length > 0 && row.messages.length === 0 ? "true" : void 0,
7674
7780
  children: [
7675
7781
  thinkingContent,
7676
- row.messages.map((message) => {
7677
- const rawTimelineJson2 = showRawTimelineJson && rawTimelineJsonLabel && (message.sourceTimelineItems?.length ?? 0) > 0 ? /* @__PURE__ */ jsx18(
7678
- RawTimelineJsonDisclosure,
7679
- {
7680
- items: message.sourceTimelineItems,
7681
- label: rawTimelineJsonLabel
7682
- }
7683
- ) : null;
7684
- const recoveredError = !isUser && !message.visibleError ? recoverVisibleErrorFromMessage(message, provider) : null;
7685
- const content = isUser && message.contentKind === "image-grid" ? /* @__PURE__ */ jsx18(AgentUserImageGrid, { message }) : isUser ? /* @__PURE__ */ jsx18(
7686
- AgentRichTextReadonly,
7687
- {
7688
- value: message.body,
7689
- className: `workspace-agents-status-panel__detail-user-message ${AgentGUIConversation_styles_default.userMessageBubble}`,
7690
- editorClassName: "text-[inherit]",
7691
- onLinkClick: handleLinkClick,
7692
- availableSkills,
7693
- workspaceAppIcons
7694
- }
7695
- ) : message.visibleError ? /* @__PURE__ */ jsx18(
7696
- AgentVisibleErrorMessage,
7697
- {
7698
- message,
7699
- onAuthLogin,
7700
- onExternalLink: handleLinkClick
7701
- }
7702
- ) : recoveredError ? /* @__PURE__ */ jsx18(
7703
- AgentVisibleErrorMessage,
7704
- {
7705
- message: recoveredError,
7706
- onAuthLogin,
7707
- onExternalLink: handleLinkClick
7708
- }
7709
- ) : message.systemNotice ? /* @__PURE__ */ jsx18(AgentSystemNoticeMessage, { message }) : message.contentKind === "collaboration" && message.collaboration ? /* @__PURE__ */ jsx18(
7710
- AgentCollaborationRow,
7711
- {
7712
- collaboration: message.collaboration,
7713
- workspaceRoot,
7714
- basePath,
7715
- onLinkAction,
7716
- workspaceAppIcons,
7717
- previewMode
7718
- }
7719
- ) : message.contentKind === "plan" ? /* @__PURE__ */ jsx18(
7720
- AgentPlanCardMessage,
7721
- {
7722
- message,
7723
- workspaceRoot,
7724
- basePath,
7725
- onLinkAction,
7726
- workspaceAppIcons,
7727
- previewMode
7728
- }
7729
- ) : /* @__PURE__ */ jsx18(
7730
- AgentMessageMarkdown,
7731
- {
7732
- content: message.body,
7733
- className: AgentGUIConversation_styles_default.assistantMarkdown,
7734
- onLinkAction,
7735
- workspaceLinkContext: {
7736
- workspaceRoot,
7737
- basePath,
7738
- source: "agent-markdown"
7739
- },
7740
- workspaceAppIcons,
7741
- enableImageZoom: true,
7742
- previewMode,
7743
- streaming: message.statusKind === "working"
7744
- }
7745
- );
7746
- if (rawTimelineJson2) {
7747
- return /* @__PURE__ */ jsxs10(
7748
- AgentCopyableMessageGroup,
7749
- {
7750
- copyText: message.copyText ?? null,
7751
- occurredAtUnixMs: message.occurredAtUnixMs,
7752
- speaker: row.speaker,
7753
- onCopyMessageText: handleCopyMessageText,
7754
- children: [
7755
- content,
7756
- rawTimelineJson2
7757
- ]
7758
- },
7759
- message.id
7760
- );
7761
- }
7762
- const copyText = message.copyText ?? null;
7763
- if (copyText) {
7764
- return /* @__PURE__ */ jsx18(
7765
- AgentCopyableMessageGroup,
7766
- {
7767
- copyText,
7768
- occurredAtUnixMs: message.occurredAtUnixMs,
7769
- speaker: row.speaker,
7770
- onCopyMessageText: handleCopyMessageText,
7771
- children: content
7772
- },
7773
- message.id
7774
- );
7782
+ showParticipant ? /* @__PURE__ */ jsxs10(
7783
+ "div",
7784
+ {
7785
+ className: AgentGUIConversation_styles_default.participantMessageLayout,
7786
+ "data-agent-message-speaker": row.speaker,
7787
+ children: [
7788
+ !isUser ? /* @__PURE__ */ jsx18(
7789
+ AgentConversationParticipantAvatar,
7790
+ {
7791
+ presentation: enabledParticipantPresentation,
7792
+ speaker: "assistant"
7793
+ }
7794
+ ) : null,
7795
+ /* @__PURE__ */ jsx18("div", { className: AgentGUIConversation_styles_default.participantMessageContent, children: messageContent }),
7796
+ isUser ? /* @__PURE__ */ jsx18(
7797
+ AgentConversationParticipantAvatar,
7798
+ {
7799
+ presentation: enabledParticipantPresentation,
7800
+ speaker: "user"
7801
+ }
7802
+ ) : null
7803
+ ]
7775
7804
  }
7776
- return /* @__PURE__ */ jsx18(Fragment2, { children: content }, message.id);
7777
- })
7805
+ ) : messageContent
7778
7806
  ]
7779
7807
  }
7780
7808
  );
7781
7809
  }
7810
+ function AgentConversationParticipantAvatar({
7811
+ presentation,
7812
+ speaker
7813
+ }) {
7814
+ if (presentation.status === "loading") {
7815
+ return /* @__PURE__ */ jsx18(
7816
+ Avatar,
7817
+ {
7818
+ "aria-hidden": "true",
7819
+ className: AgentGUIConversation_styles_default.participantAvatar,
7820
+ "data-agent-conversation-participant-avatar": speaker,
7821
+ label: "",
7822
+ loading: true,
7823
+ size: "md"
7824
+ }
7825
+ );
7826
+ }
7827
+ const participant = speaker === "user" ? presentation.user : presentation.agent;
7828
+ return /* @__PURE__ */ jsx18(
7829
+ Avatar,
7830
+ {
7831
+ "aria-label": participant.name,
7832
+ className: AgentGUIConversation_styles_default.participantAvatar,
7833
+ "data-agent-conversation-participant-avatar": speaker,
7834
+ label: participant.name,
7835
+ size: "md",
7836
+ src: participant.avatarUrl
7837
+ }
7838
+ );
7839
+ }
7782
7840
  function AgentCopyableMessageGroup({
7783
7841
  children,
7784
7842
  copyText,
@@ -13712,6 +13770,7 @@ var AgentTranscriptItemView = memo3(function AgentTranscriptItemView2({
13712
13770
  workspaceAppIcons,
13713
13771
  previewMode = false,
13714
13772
  showRawTimelineJson = false,
13773
+ participantPresentation,
13715
13774
  toolGroupExpanded,
13716
13775
  toolGroupExpansionKey,
13717
13776
  onToolGroupExpandedChange
@@ -13758,7 +13817,8 @@ var AgentTranscriptItemView = memo3(function AgentTranscriptItemView2({
13758
13817
  previewMode,
13759
13818
  thinkingLabel: labels.thinkingLabel,
13760
13819
  showRawTimelineJson,
13761
- rawTimelineJsonLabel: labels.rawTimelineJson
13820
+ rawTimelineJsonLabel: labels.rawTimelineJson,
13821
+ participantPresentation
13762
13822
  }
13763
13823
  );
13764
13824
  case "tool-group":
@@ -15261,6 +15321,24 @@ var AGENT_TRANSCRIPT_DISCLOSURE_TURN_GAP_PX = 24;
15261
15321
  var AGENT_TRANSCRIPT_LEGACY_TURN_GAP_PX = 12;
15262
15322
  var AGENT_TRANSCRIPT_FALLBACK_TURN_COUNT = 3;
15263
15323
  var preventVirtualScrollAdjustment = () => false;
15324
+ function participantPresentationEqual(previous, next) {
15325
+ if (previous === next) {
15326
+ return true;
15327
+ }
15328
+ if ((!previous || !previous.enabled) && (!next || !next.enabled)) {
15329
+ return true;
15330
+ }
15331
+ if (!previous?.enabled || !next?.enabled) {
15332
+ return false;
15333
+ }
15334
+ if (previous.status !== next.status) {
15335
+ return false;
15336
+ }
15337
+ if (previous.status === "loading" || next.status === "loading") {
15338
+ return true;
15339
+ }
15340
+ return previous.user.name === next.user.name && previous.user.avatarUrl === next.user.avatarUrl && previous.agent.name === next.agent.name && previous.agent.avatarUrl === next.agent.avatarUrl;
15341
+ }
15264
15342
  function transcriptLabelsEqual(previous, next) {
15265
15343
  return previous === next || previous.thinkingLabel === next.thinkingLabel && previous.processing === next.processing && previous.turnSummary === next.turnSummary && previous.rawTimelineJson === next.rawTimelineJson && previous.userMessageLocator === next.userMessageLocator && previous.toolCallsLabel === next.toolCallsLabel;
15266
15344
  }
@@ -15286,7 +15364,10 @@ function areAgentTranscriptViewPropsEqual(previous, next) {
15286
15364
  return transcriptConversationRenderInputEquals(
15287
15365
  previous.conversation,
15288
15366
  next.conversation
15289
- ) && previous.onLinkAction === next.onLinkAction && previous.onAuthLogin === next.onAuthLogin && previous.availableSkills === next.availableSkills && previous.workspaceAppIcons === next.workspaceAppIcons && previous.turnAttachments === next.turnAttachments && previous.turnAttachmentLocatorRef === next.turnAttachmentLocatorRef && previous.onTurnAttachmentVisibilityChange === next.onTurnAttachmentVisibilityChange && previous.previewMode === next.previewMode && previous.showRawTimelineJson === next.showRawTimelineJson && transcriptLabelsEqual(previous.labels, next.labels);
15367
+ ) && previous.onLinkAction === next.onLinkAction && previous.onAuthLogin === next.onAuthLogin && previous.availableSkills === next.availableSkills && previous.workspaceAppIcons === next.workspaceAppIcons && previous.turnAttachments === next.turnAttachments && previous.turnAttachmentLocatorRef === next.turnAttachmentLocatorRef && previous.onTurnAttachmentVisibilityChange === next.onTurnAttachmentVisibilityChange && previous.previewMode === next.previewMode && previous.showRawTimelineJson === next.showRawTimelineJson && participantPresentationEqual(
15368
+ previous.participantPresentation,
15369
+ next.participantPresentation
15370
+ ) && transcriptLabelsEqual(previous.labels, next.labels);
15290
15371
  }
15291
15372
  var AgentTranscriptView = memo4(function AgentTranscriptView2({
15292
15373
  conversation,
@@ -15299,6 +15380,7 @@ var AgentTranscriptView = memo4(function AgentTranscriptView2({
15299
15380
  workspaceAppIcons,
15300
15381
  previewMode = false,
15301
15382
  showRawTimelineJson = false,
15383
+ participantPresentation,
15302
15384
  labels
15303
15385
  }) {
15304
15386
  "use memo";
@@ -15464,6 +15546,7 @@ var AgentTranscriptView = memo4(function AgentTranscriptView2({
15464
15546
  workspaceAppIcons,
15465
15547
  previewMode,
15466
15548
  showRawTimelineJson,
15549
+ participantPresentation,
15467
15550
  toolGroupExpanded: row.kind === "tool-group" ? expandedToolRows[rowKey] === true : void 0,
15468
15551
  toolGroupExpansionKey: row.kind === "tool-group" ? rowKey : void 0,
15469
15552
  onToolGroupExpandedChange: handleToolGroupExpandedChange
@@ -15609,6 +15692,7 @@ var AgentConversationFlow = memo5(function AgentConversationFlow2({
15609
15692
  workspaceAppIcons,
15610
15693
  previewMode = false,
15611
15694
  showRawTimelineJson = false,
15695
+ participantPresentation,
15612
15696
  labels
15613
15697
  }) {
15614
15698
  "use memo";
@@ -15631,7 +15715,8 @@ var AgentConversationFlow = memo5(function AgentConversationFlow2({
15631
15715
  workspaceAppIcons,
15632
15716
  previewMode,
15633
15717
  labels,
15634
- showRawTimelineJson
15718
+ showRawTimelineJson,
15719
+ participantPresentation
15635
15720
  }
15636
15721
  );
15637
15722
  }
@@ -15704,4 +15789,4 @@ export {
15704
15789
  AgentConversationFlow,
15705
15790
  useProjectedAgentConversation
15706
15791
  };
15707
- //# sourceMappingURL=chunk-NEH45ROF.js.map
15792
+ //# sourceMappingURL=chunk-GJZEMI6Z.js.map