@antelopejs/dms-ai 0.1.4 → 0.1.5

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.
Files changed (244) hide show
  1. package/README.md +48 -29
  2. package/dist/channels/bridge.d.ts +2 -8
  3. package/dist/channels/bridge.js +26 -42
  4. package/dist/channels/bridge.js.map +1 -1
  5. package/dist/channels/channel-service.d.ts +2 -2
  6. package/dist/channels/channel-service.js +12 -31
  7. package/dist/channels/channel-service.js.map +1 -1
  8. package/dist/channels/registry.d.ts +2 -2
  9. package/dist/channels/registry.js +7 -7
  10. package/dist/channels/registry.js.map +1 -1
  11. package/dist/config.d.ts +1 -0
  12. package/dist/config.js +8 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/constants/channels.d.ts +5 -10
  15. package/dist/constants/channels.js +6 -10
  16. package/dist/constants/channels.js.map +1 -1
  17. package/dist/constants/sidecar.d.ts +2 -1
  18. package/dist/constants/sidecar.js +3 -2
  19. package/dist/constants/sidecar.js.map +1 -1
  20. package/dist/index.js +8 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lifecycle/build-id.d.ts +1 -0
  23. package/dist/lifecycle/build-id.js +32 -0
  24. package/dist/lifecycle/build-id.js.map +1 -0
  25. package/dist/lifecycle/sidecar-socket.d.ts +1 -1
  26. package/dist/lifecycle/sidecar-socket.js +2 -2
  27. package/dist/lifecycle/sidecar-socket.js.map +1 -1
  28. package/dist/lifecycle/spawn-sidecar.d.ts +0 -1
  29. package/dist/lifecycle/spawn-sidecar.js +5 -27
  30. package/dist/lifecycle/spawn-sidecar.js.map +1 -1
  31. package/dist/routes/channels.d.ts +3 -3
  32. package/dist/routes/channels.js +11 -12
  33. package/dist/routes/channels.js.map +1 -1
  34. package/dist/routes/index.d.ts +0 -1
  35. package/dist/routes/index.js +0 -1
  36. package/dist/routes/index.js.map +1 -1
  37. package/frontend-vue/.prettierignore +3 -0
  38. package/frontend-vue/.prettierrc +10 -0
  39. package/frontend-vue/app/chat/components/ChatView.vue +582 -0
  40. package/frontend-vue/app/chat/components/ComposerInput.vue +742 -0
  41. package/frontend-vue/app/chat/components/ConnectionBanner.vue +105 -0
  42. package/frontend-vue/app/chat/components/ConversationDrawer.vue +266 -0
  43. package/frontend-vue/app/chat/components/MarkdownContent.vue +196 -0
  44. package/frontend-vue/app/chat/components/MessageList.vue +474 -0
  45. package/frontend-vue/app/chat/components/PermissionRequests.vue +323 -0
  46. package/frontend-vue/app/chat/components/QuestionPrompt.vue +275 -0
  47. package/frontend-vue/app/chat/components/QueuedMessages.vue +109 -0
  48. package/frontend-vue/app/chat/components/TodoList.vue +291 -0
  49. package/frontend-vue/app/chat/components/ToolCallEntry.vue +218 -0
  50. package/frontend-vue/app/chat/components/ToolCluster.vue +282 -0
  51. package/frontend-vue/app/chat/composables/useChatChannel.ts +116 -0
  52. package/frontend-vue/app/chat/composables/useConversation.ts +838 -0
  53. package/frontend-vue/app/chat/composables/useConversationId.ts +34 -0
  54. package/frontend-vue/app/chat/composables/useConversationList.ts +56 -0
  55. package/frontend-vue/app/chat/composables/usePermissionQueue.ts +59 -0
  56. package/frontend-vue/app/chat/composables/useQuestionQueue.ts +50 -0
  57. package/frontend-vue/app/chat/composables/useRunClock.ts +28 -0
  58. package/frontend-vue/app/chat/composables/useSettings.ts +67 -0
  59. package/frontend-vue/app/chat/constants/attachments.ts +16 -0
  60. package/frontend-vue/app/chat/constants/connection-banner.ts +7 -0
  61. package/frontend-vue/app/chat/constants/conversation-drawer.ts +13 -0
  62. package/frontend-vue/app/chat/constants/conversation.ts +75 -0
  63. package/frontend-vue/app/chat/constants/permissions.ts +50 -0
  64. package/frontend-vue/app/chat/constants/protocol.ts +48 -0
  65. package/frontend-vue/app/chat/constants/run-status.ts +50 -0
  66. package/frontend-vue/app/chat/constants/settings.ts +60 -0
  67. package/frontend-vue/app/chat/types/conversation.ts +87 -0
  68. package/frontend-vue/app/chat/types/permission.ts +7 -0
  69. package/frontend-vue/app/chat/types/question.ts +17 -0
  70. package/frontend-vue/app/chat/types/settings.ts +27 -0
  71. package/frontend-vue/app/chat/utils/attachments.ts +62 -0
  72. package/frontend-vue/app/chat/utils/format.ts +14 -0
  73. package/frontend-vue/app/chat/utils/ids.ts +34 -0
  74. package/frontend-vue/app/chat/utils/markdown.ts +188 -0
  75. package/frontend-vue/app/chat/utils/run-status.ts +90 -0
  76. package/frontend-vue/app/chat/utils/todos.ts +64 -0
  77. package/frontend-vue/app/chat/utils/tool-summary.ts +53 -0
  78. package/frontend-vue/app/components/ActivityView.vue +34 -34
  79. package/frontend-vue/app/components/ChatPanel.vue +276 -0
  80. package/frontend-vue/app/components/SettingsView.vue +120 -112
  81. package/frontend-vue/app/components/SkillsView.vue +55 -53
  82. package/frontend-vue/app/plugins/ai.client.ts +145 -129
  83. package/frontend-vue/app/runtime/assistant-session.ts +21 -0
  84. package/frontend-vue/app/runtime/backoff.ts +11 -14
  85. package/frontend-vue/app/runtime/channel-client.ts +130 -141
  86. package/frontend-vue/app/runtime/chat-transport.ts +25 -55
  87. package/frontend-vue/app/runtime/constants.ts +69 -59
  88. package/frontend-vue/app/runtime/current-page.ts +49 -15
  89. package/frontend-vue/app/runtime/event-stream.ts +35 -35
  90. package/frontend-vue/app/runtime/frame-router.ts +30 -0
  91. package/frontend-vue/app/runtime/header-action.ts +18 -18
  92. package/frontend-vue/app/runtime/host-commands.ts +57 -56
  93. package/frontend-vue/app/runtime/listeners.ts +31 -0
  94. package/frontend-vue/app/runtime/navigation-complete.ts +7 -7
  95. package/frontend-vue/app/runtime/overlay-prefs.ts +36 -36
  96. package/frontend-vue/app/runtime/panel-state.ts +88 -0
  97. package/frontend-vue/app/runtime/session-gate.ts +8 -8
  98. package/frontend-vue/app/runtime/shortcuts.ts +12 -12
  99. package/frontend-vue/app/runtime/sidecar-status.ts +61 -61
  100. package/frontend-vue/app/runtime/typed-message.ts +11 -0
  101. package/frontend-vue/dms.frontend.ts +12 -12
  102. package/frontend-vue/eslint.config.mjs +13 -13
  103. package/frontend-vue/package.json +11 -4
  104. package/frontend-vue/pnpm-lock.yaml +298 -96
  105. package/frontend-vue/tests/backoff.test.ts +26 -30
  106. package/frontend-vue/tests/channel-client.test.ts +187 -188
  107. package/frontend-vue/tests/chat/markdown.test.ts +271 -0
  108. package/frontend-vue/tests/chat/useChatChannel.test.ts +124 -0
  109. package/frontend-vue/tests/chat/useConversation.test.ts +283 -0
  110. package/frontend-vue/tests/chat-panel.test.ts +354 -0
  111. package/frontend-vue/tests/chat-transport.test.ts +84 -57
  112. package/frontend-vue/tests/host-commands.test.ts +126 -87
  113. package/frontend-vue/tests/navigation.test.ts +65 -34
  114. package/frontend-vue/tests/overlay-prefs.test.ts +49 -49
  115. package/frontend-vue/tests/panel-state.test.ts +58 -0
  116. package/frontend-vue/tests/session-gate.test.ts +33 -33
  117. package/frontend-vue/tests/sidecar-guest.test.ts +46 -46
  118. package/frontend-vue/tests/sidecar-status.test.ts +29 -29
  119. package/frontend-vue/tsconfig.chat.json +16 -0
  120. package/frontend-vue/vitest.config.ts +6 -4
  121. package/package.json +5 -5
  122. package/sidecar/dist/agent/effective-mode.d.ts +2 -2
  123. package/sidecar/dist/agent/effective-mode.js +1 -1
  124. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  125. package/sidecar/dist/agent/host-context.js +7 -9
  126. package/sidecar/dist/agent/host-context.js.map +1 -1
  127. package/sidecar/dist/agent/permission-bus.d.ts +1 -1
  128. package/sidecar/dist/agent/permission-bus.js +2 -2
  129. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  130. package/sidecar/dist/agent/provider.d.ts +2 -2
  131. package/sidecar/dist/agent/question-bus.d.ts +1 -1
  132. package/sidecar/dist/agent/question-bus.js +2 -2
  133. package/sidecar/dist/agent/question-bus.js.map +1 -1
  134. package/sidecar/dist/agent/runner.d.ts +2 -2
  135. package/sidecar/dist/agent/runner.js +24 -8
  136. package/sidecar/dist/agent/runner.js.map +1 -1
  137. package/sidecar/dist/agent/session.d.ts +2 -2
  138. package/sidecar/dist/agent/session.js +49 -9
  139. package/sidecar/dist/agent/session.js.map +1 -1
  140. package/sidecar/dist/agent/switching-runner.js +10 -5
  141. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  142. package/sidecar/dist/agent/turn-end-reasons.d.ts +2 -0
  143. package/sidecar/dist/agent/turn-end-reasons.js +6 -1
  144. package/sidecar/dist/agent/turn-end-reasons.js.map +1 -1
  145. package/sidecar/dist/constants/agent.d.ts +4 -1
  146. package/sidecar/dist/constants/agent.js +4 -1
  147. package/sidecar/dist/constants/agent.js.map +1 -1
  148. package/sidecar/dist/constants/cli.d.ts +0 -1
  149. package/sidecar/dist/constants/cli.js +0 -1
  150. package/sidecar/dist/constants/cli.js.map +1 -1
  151. package/sidecar/dist/constants/codex.d.ts +5 -2
  152. package/sidecar/dist/constants/codex.js +5 -2
  153. package/sidecar/dist/constants/codex.js.map +1 -1
  154. package/sidecar/dist/constants/http.d.ts +0 -9
  155. package/sidecar/dist/constants/http.js +0 -19
  156. package/sidecar/dist/constants/http.js.map +1 -1
  157. package/sidecar/dist/constants/mcp.d.ts +1 -1
  158. package/sidecar/dist/constants/mcp.js +1 -1
  159. package/sidecar/dist/constants/mcp.js.map +1 -1
  160. package/sidecar/dist/constants/paths.d.ts +0 -1
  161. package/sidecar/dist/constants/paths.js +0 -1
  162. package/sidecar/dist/constants/paths.js.map +1 -1
  163. package/sidecar/dist/constants/ws.d.ts +3 -4
  164. package/sidecar/dist/constants/ws.js +3 -4
  165. package/sidecar/dist/constants/ws.js.map +1 -1
  166. package/sidecar/dist/index.d.ts +0 -1
  167. package/sidecar/dist/index.js +6 -10
  168. package/sidecar/dist/index.js.map +1 -1
  169. package/sidecar/dist/mcp/tools/find-pages-using.js +5 -10
  170. package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
  171. package/sidecar/dist/mcp/types.d.ts +1 -1
  172. package/sidecar/dist/pages/page-filepath.d.ts +4 -0
  173. package/sidecar/dist/pages/page-filepath.js +28 -0
  174. package/sidecar/dist/pages/page-filepath.js.map +1 -0
  175. package/sidecar/dist/prompts/agent.d.ts +1 -1
  176. package/sidecar/dist/prompts/agent.js +1 -1
  177. package/sidecar/dist/prompts/agent.js.map +1 -1
  178. package/sidecar/dist/protocol/events.js +2 -2
  179. package/sidecar/dist/protocol/events.js.map +1 -1
  180. package/sidecar/dist/protocol/messages.d.ts +4 -15
  181. package/sidecar/dist/protocol/messages.js +4 -11
  182. package/sidecar/dist/protocol/messages.js.map +1 -1
  183. package/sidecar/dist/providers/claude/config.d.ts +2 -2
  184. package/sidecar/dist/providers/claude/config.js +2 -2
  185. package/sidecar/dist/providers/claude/config.js.map +1 -1
  186. package/sidecar/dist/providers/claude/provider.js +1 -1
  187. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  188. package/sidecar/dist/providers/codex/config.d.ts +3 -2
  189. package/sidecar/dist/providers/codex/config.js +3 -3
  190. package/sidecar/dist/providers/codex/config.js.map +1 -1
  191. package/sidecar/dist/providers/codex/process.js +107 -74
  192. package/sidecar/dist/providers/codex/process.js.map +1 -1
  193. package/sidecar/dist/providers/codex/provider.js +68 -31
  194. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  195. package/sidecar/dist/providers/codex/skills.d.ts +1 -0
  196. package/sidecar/dist/providers/codex/skills.js +4 -1
  197. package/sidecar/dist/providers/codex/skills.js.map +1 -1
  198. package/sidecar/dist/providers/codex/stop.d.ts +4 -0
  199. package/sidecar/dist/providers/codex/stop.js +71 -0
  200. package/sidecar/dist/providers/codex/stop.js.map +1 -0
  201. package/sidecar/dist/server/{iframe-socket-registry.d.ts → chat-socket-registry.d.ts} +3 -2
  202. package/sidecar/dist/server/{iframe-socket-registry.js → chat-socket-registry.js} +4 -3
  203. package/sidecar/dist/server/chat-socket-registry.js.map +1 -0
  204. package/sidecar/dist/server/client-auth.js +2 -14
  205. package/sidecar/dist/server/client-auth.js.map +1 -1
  206. package/sidecar/dist/server/host-command-router.d.ts +5 -0
  207. package/sidecar/dist/server/host-command-router.js +4 -0
  208. package/sidecar/dist/server/host-command-router.js.map +1 -0
  209. package/sidecar/dist/server/host-socket-registry.d.ts +1 -1
  210. package/sidecar/dist/server/host-socket-registry.js +16 -13
  211. package/sidecar/dist/server/host-socket-registry.js.map +1 -1
  212. package/sidecar/dist/server/http.d.ts +0 -1
  213. package/sidecar/dist/server/http.js +1 -44
  214. package/sidecar/dist/server/http.js.map +1 -1
  215. package/sidecar/dist/server/routing.d.ts +5 -6
  216. package/sidecar/dist/server/routing.js +18 -28
  217. package/sidecar/dist/server/routing.js.map +1 -1
  218. package/sidecar/dist/server/ws.d.ts +2 -2
  219. package/sidecar/dist/server/ws.js +41 -39
  220. package/sidecar/dist/server/ws.js.map +1 -1
  221. package/sidecar/dist/state/host-state.d.ts +10 -2
  222. package/sidecar/dist/state/host-state.js +13 -3
  223. package/sidecar/dist/state/host-state.js.map +1 -1
  224. package/sidecar/dist/state/settings-store.js +2 -2
  225. package/sidecar/dist/state/settings-store.js.map +1 -1
  226. package/sidecar/dist/state/settings-types.d.ts +3 -3
  227. package/sidecar/dist/state/settings-types.js +1 -1
  228. package/sidecar/dist/state/settings-types.js.map +1 -1
  229. package/sidecar/package.json +3 -5
  230. package/dist/chatbox/passthrough.d.ts +0 -3
  231. package/dist/chatbox/passthrough.js +0 -53
  232. package/dist/chatbox/passthrough.js.map +0 -1
  233. package/dist/constants/chatbox.d.ts +0 -10
  234. package/dist/constants/chatbox.js +0 -35
  235. package/dist/constants/chatbox.js.map +0 -1
  236. package/dist/routes/chatbox.d.ts +0 -7
  237. package/dist/routes/chatbox.js +0 -47
  238. package/dist/routes/chatbox.js.map +0 -1
  239. package/frontend-vue/app/runtime/overlay.ts +0 -400
  240. package/frontend-vue/app/runtime/theme-bridge.ts +0 -189
  241. package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +0 -1
  242. package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +0 -52
  243. package/sidecar/chatbox/dist/index.html +0 -13
  244. package/sidecar/dist/server/iframe-socket-registry.js.map +0 -1
@@ -0,0 +1,474 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import {
4
+ MESSAGE_ROLES,
5
+ RETRY_LABEL,
6
+ ROLE_LABELS,
7
+ } from "../constants/conversation";
8
+ import {
9
+ ACTIVITY_KINDS,
10
+ QUIET_LABEL,
11
+ RECONNECT_LABEL,
12
+ STALLED_LABEL,
13
+ STOP_LABEL,
14
+ } from "../constants/run-status";
15
+ import type {
16
+ AssistantMessage,
17
+ ConversationMessage,
18
+ ErrorMessage,
19
+ MessageAttachment,
20
+ RunProgress,
21
+ ToolCallMessage,
22
+ UserMessage,
23
+ } from "../types/conversation";
24
+ import { formatBytes, isInlineImage } from "../utils/attachments";
25
+ import {
26
+ agentQuietMs,
27
+ describeActivity,
28
+ formatClock,
29
+ isAgentQuiet,
30
+ runElapsedMs,
31
+ } from "../utils/run-status";
32
+ import { isTodoWrite } from "../utils/todos";
33
+ import MarkdownContent from "./MarkdownContent.vue";
34
+ import ToolCallEntry from "./ToolCallEntry.vue";
35
+ import ToolCluster from "./ToolCluster.vue";
36
+
37
+ interface Props {
38
+ messages: ConversationMessage[];
39
+ isRunning: boolean;
40
+ progress: RunProgress | null;
41
+ nowMs: number;
42
+ isStalled: boolean;
43
+ stalledForMs: number;
44
+ }
45
+
46
+ interface Emits {
47
+ retry: [];
48
+ reconnect: [];
49
+ stop: [];
50
+ }
51
+
52
+ type TextMessage = UserMessage | AssistantMessage | ErrorMessage;
53
+
54
+ interface MessageRenderItem {
55
+ kind: "message";
56
+ key: string;
57
+ message: TextMessage;
58
+ }
59
+
60
+ interface ToolsRenderItem {
61
+ kind: "tools";
62
+ key: string;
63
+ tools: ToolCallMessage[];
64
+ }
65
+
66
+ type RenderItem = MessageRenderItem | ToolsRenderItem;
67
+
68
+ const props = defineProps<Props>();
69
+ const emit = defineEmits<Emits>();
70
+
71
+ const ATTACH_FILE_ICON = "i-ph-file";
72
+
73
+ const ROLE_LABEL_BY_ROLE: Record<string, string> = {
74
+ [MESSAGE_ROLES.USER]: ROLE_LABELS.USER,
75
+ [MESSAGE_ROLES.ERROR]: ROLE_LABELS.ERROR,
76
+ [MESSAGE_ROLES.ASSISTANT]: ROLE_LABELS.ASSISTANT,
77
+ [MESSAGE_ROLES.TOOL]: ROLE_LABELS.TOOL,
78
+ };
79
+
80
+ const renderItems = computed<RenderItem[]>(() => {
81
+ const out: RenderItem[] = [];
82
+ let run: ToolCallMessage[] = [];
83
+
84
+ const flush = (): void => {
85
+ if (run.length === 0) return;
86
+ out.push({ kind: "tools", key: `tools-${run[0].id}`, tools: run });
87
+ run = [];
88
+ };
89
+
90
+ for (const message of props.messages) {
91
+ if (isTodoWrite(message)) {
92
+ flush();
93
+ continue;
94
+ }
95
+ if (message.role === MESSAGE_ROLES.TOOL) {
96
+ run = [...run, message];
97
+ continue;
98
+ }
99
+ flush();
100
+ out.push({ kind: "message", key: message.id, message });
101
+ }
102
+ flush();
103
+ return out;
104
+ });
105
+
106
+ const lastToolKey = computed<string | null>(() => {
107
+ for (let i = renderItems.value.length - 1; i >= 0; i--) {
108
+ const item = renderItems.value[i];
109
+ if (item.kind === "tools") return item.key;
110
+ }
111
+ return null;
112
+ });
113
+
114
+ const showActivity = computed<boolean>(() => {
115
+ if (!props.isRunning) return false;
116
+ if (props.progress?.activity !== ACTIVITY_KINDS.RESPONDING) return true;
117
+ const last = props.messages.at(-1);
118
+ return last === undefined || last.role !== MESSAGE_ROLES.ASSISTANT;
119
+ });
120
+
121
+ const activityLabel = computed<string>(() => describeActivity(props.progress));
122
+
123
+ const elapsedLabel = computed<string>(() =>
124
+ formatClock(runElapsedMs(props.progress, props.nowMs)),
125
+ );
126
+
127
+ const quietLabel = computed<string>(() => {
128
+ if (!isAgentQuiet(props.progress, props.nowMs)) return "";
129
+ return `${QUIET_LABEL} ${formatClock(agentQuietMs(props.progress, props.nowMs))}`;
130
+ });
131
+
132
+ const streamingMessageId = computed<string | null>(() => {
133
+ if (!props.isRunning) return null;
134
+ const last = props.messages.at(-1);
135
+ return last?.role === MESSAGE_ROLES.ASSISTANT ? last.id : null;
136
+ });
137
+
138
+ const lastErrorId = computed<string | null>(() => {
139
+ const last = props.messages.at(-1);
140
+ return last?.role === MESSAGE_ROLES.ERROR ? last.id : null;
141
+ });
142
+
143
+ function roleLabel(role: string): string {
144
+ return ROLE_LABEL_BY_ROLE[role] ?? role;
145
+ }
146
+
147
+ function userAttachments(message: TextMessage): MessageAttachment[] {
148
+ if (message.role !== MESSAGE_ROLES.USER) return [];
149
+ return message.attachments ?? [];
150
+ }
151
+
152
+ function formatTime(ms: number): string {
153
+ return new Date(ms).toLocaleTimeString();
154
+ }
155
+ </script>
156
+
157
+ <template>
158
+ <ol class="message-list">
159
+ <template v-for="item in renderItems" :key="item.key">
160
+ <li v-if="item.kind === 'tools'" class="message-item" data-role="tool">
161
+ <ToolCluster
162
+ v-if="item.tools.length > 1"
163
+ :tools="item.tools"
164
+ :live="isRunning && item.key === lastToolKey"
165
+ />
166
+ <ToolCallEntry v-else :message="item.tools[0]" />
167
+ </li>
168
+
169
+ <li v-else class="message-item" :data-role="item.message.role">
170
+ <header class="message-meta">
171
+ <span class="message-role">{{ roleLabel(item.message.role) }}</span>
172
+ <span class="message-time">
173
+ {{ formatTime(item.message.timestampMs) }}
174
+ </span>
175
+ </header>
176
+
177
+ <template v-if="item.message.role === MESSAGE_ROLES.USER">
178
+ <p
179
+ v-if="item.message.content"
180
+ class="message-bubble message-bubble-user"
181
+ >
182
+ {{ item.message.content }}
183
+ </p>
184
+
185
+ <ul
186
+ v-if="userAttachments(item.message).length > 0"
187
+ class="msg-attachments"
188
+ >
189
+ <li
190
+ v-for="(att, i) in userAttachments(item.message)"
191
+ :key="i"
192
+ class="msg-att"
193
+ >
194
+ <img
195
+ v-if="isInlineImage(att.mimeType) && att.dataUrl"
196
+ :src="att.dataUrl"
197
+ :alt="att.name"
198
+ class="msg-att-thumb"
199
+ />
200
+ <template v-else>
201
+ <UIcon :name="ATTACH_FILE_ICON" class="size-[15px]" />
202
+ <span class="msg-att-name">{{ att.name }}</span>
203
+ <span class="msg-att-size">{{ formatBytes(att.size) }}</span>
204
+ </template>
205
+ </li>
206
+ </ul>
207
+ </template>
208
+
209
+ <div
210
+ v-else-if="item.message.role === MESSAGE_ROLES.ERROR"
211
+ class="message-bubble message-bubble-error"
212
+ role="alert"
213
+ >
214
+ <p class="error-text">{{ item.message.content }}</p>
215
+ <button
216
+ v-if="
217
+ !isRunning &&
218
+ item.message.isRetryable &&
219
+ item.message.id === lastErrorId
220
+ "
221
+ type="button"
222
+ class="run-action"
223
+ @click="emit('retry')"
224
+ >
225
+ {{ RETRY_LABEL }}
226
+ </button>
227
+ </div>
228
+
229
+ <MarkdownContent
230
+ v-else
231
+ class="message-bubble message-bubble-assistant"
232
+ :content="item.message.content"
233
+ :is-streaming="item.message.id === streamingMessageId"
234
+ />
235
+ </li>
236
+ </template>
237
+
238
+ <li v-if="isStalled" class="message-item" data-role="assistant">
239
+ <div class="run-stalled" role="alert">
240
+ <span>{{ STALLED_LABEL }} {{ formatClock(stalledForMs) }}.</span>
241
+ <button type="button" class="run-action" @click="emit('reconnect')">
242
+ {{ RECONNECT_LABEL }}
243
+ </button>
244
+ <button type="button" class="run-action" @click="emit('stop')">
245
+ {{ STOP_LABEL }}
246
+ </button>
247
+ </div>
248
+ </li>
249
+ <li v-else-if="showActivity" class="message-item" data-role="assistant">
250
+ <div class="thinking" aria-live="polite">
251
+ <span class="thinking-label">{{ activityLabel }}</span>
252
+ <span class="thinking-dots">
253
+ <i></i>
254
+ <i></i>
255
+ <i></i>
256
+ </span>
257
+ <span class="thinking-clock">{{ elapsedLabel }}</span>
258
+ <span v-if="quietLabel" class="thinking-quiet">{{ quietLabel }}</span>
259
+ </div>
260
+ </li>
261
+ </ol>
262
+ </template>
263
+
264
+ <style scoped>
265
+ .message-list {
266
+ list-style: none;
267
+ margin: 0;
268
+ padding: 12px;
269
+ display: flex;
270
+ flex-direction: column;
271
+ gap: 12px;
272
+ }
273
+
274
+ .message-item {
275
+ display: flex;
276
+ flex-direction: column;
277
+ gap: 4px;
278
+ max-width: 85%;
279
+ }
280
+
281
+ .message-item[data-role="user"] {
282
+ align-self: flex-end;
283
+ align-items: flex-end;
284
+ }
285
+
286
+ .message-item[data-role="assistant"] {
287
+ align-self: flex-start;
288
+ align-items: flex-start;
289
+ }
290
+
291
+ .message-item[data-role="tool"] {
292
+ align-self: stretch;
293
+ max-width: 100%;
294
+ }
295
+
296
+ .message-meta {
297
+ display: flex;
298
+ gap: 8px;
299
+ font-family: var(--font-code);
300
+ font-size: 10px;
301
+ color: var(--fg-tertiary);
302
+ }
303
+
304
+ .message-item[data-role="assistant"] .message-role,
305
+ .message-item[data-role="user"] .message-role {
306
+ display: none;
307
+ }
308
+
309
+ .message-role {
310
+ font-weight: 600;
311
+ }
312
+
313
+ .message-bubble {
314
+ margin: 0;
315
+ white-space: pre-wrap;
316
+ word-break: break-word;
317
+ font-size: 13.5px;
318
+ line-height: 1.55;
319
+ }
320
+
321
+ .message-bubble-user {
322
+ padding: 11px 14px;
323
+ border-radius: 14px 14px 4px 14px;
324
+ background: var(--accent);
325
+ color: var(--accent-fg);
326
+ font-weight: 500;
327
+ }
328
+
329
+ .message-bubble-assistant {
330
+ white-space: normal;
331
+ color: var(--fg-secondary);
332
+ }
333
+
334
+ .msg-attachments {
335
+ list-style: none;
336
+ margin: 6px 0 0;
337
+ padding: 0;
338
+ display: flex;
339
+ flex-wrap: wrap;
340
+ justify-content: flex-end;
341
+ gap: 6px;
342
+ }
343
+
344
+ .msg-att {
345
+ display: inline-flex;
346
+ align-items: center;
347
+ gap: 6px;
348
+ max-width: 220px;
349
+ padding: 5px 9px;
350
+ background: var(--surface-inset);
351
+ border: 1px solid var(--hair);
352
+ border-radius: var(--corner-md);
353
+ color: var(--fg-secondary);
354
+ font-size: 12px;
355
+ }
356
+
357
+ .msg-att-thumb {
358
+ max-width: 180px;
359
+ max-height: 180px;
360
+ padding: 0;
361
+ border-radius: var(--corner-md);
362
+ object-fit: contain;
363
+ }
364
+
365
+ .msg-att:has(.msg-att-thumb) {
366
+ padding: 0;
367
+ overflow: hidden;
368
+ }
369
+
370
+ .msg-att-name {
371
+ white-space: nowrap;
372
+ overflow: hidden;
373
+ text-overflow: ellipsis;
374
+ font-weight: 500;
375
+ color: var(--fg);
376
+ }
377
+
378
+ .msg-att-size {
379
+ flex: 0 0 auto;
380
+ color: var(--fg-tertiary);
381
+ font-size: 10px;
382
+ }
383
+
384
+ .thinking {
385
+ display: inline-flex;
386
+ align-items: center;
387
+ gap: 8px;
388
+ color: var(--fg-tertiary);
389
+ font-size: 13px;
390
+ }
391
+
392
+ .thinking-dots {
393
+ display: inline-flex;
394
+ gap: 3px;
395
+ }
396
+
397
+ .thinking-clock,
398
+ .thinking-quiet {
399
+ font-family: var(--font-code);
400
+ font-size: 11px;
401
+ }
402
+
403
+ .thinking-quiet {
404
+ color: var(--fg-secondary);
405
+ }
406
+
407
+ .message-item[data-role="error"] {
408
+ align-self: stretch;
409
+ max-width: 100%;
410
+ }
411
+
412
+ .message-bubble-error,
413
+ .run-stalled {
414
+ display: flex;
415
+ flex-wrap: wrap;
416
+ align-items: center;
417
+ gap: 8px;
418
+ padding: 9px 12px;
419
+ border: 1px solid var(--danger-400);
420
+ border-radius: var(--corner-md);
421
+ background: var(--danger-bg);
422
+ color: var(--fg);
423
+ white-space: normal;
424
+ }
425
+
426
+ .error-text {
427
+ flex: 1 1 200px;
428
+ margin: 0;
429
+ }
430
+
431
+ .run-action {
432
+ padding: 4px 10px;
433
+ border: 1px solid var(--hair);
434
+ border-radius: var(--corner-md);
435
+ background: var(--surface-card);
436
+ color: var(--fg);
437
+ font-size: 12px;
438
+ font-weight: 600;
439
+ cursor: pointer;
440
+ }
441
+
442
+ .run-action:hover {
443
+ border-color: var(--accent);
444
+ }
445
+
446
+ .thinking-dots i {
447
+ width: 5px;
448
+ height: 5px;
449
+ border-radius: 50%;
450
+ background: #9ca3af;
451
+ animation: thinking-bounce 1.2s infinite ease-in-out both;
452
+ }
453
+
454
+ .thinking-dots i:nth-child(2) {
455
+ animation-delay: 0.16s;
456
+ }
457
+
458
+ .thinking-dots i:nth-child(3) {
459
+ animation-delay: 0.32s;
460
+ }
461
+
462
+ @keyframes thinking-bounce {
463
+ 0%,
464
+ 80%,
465
+ 100% {
466
+ transform: scale(0.6);
467
+ opacity: 0.4;
468
+ }
469
+ 40% {
470
+ transform: scale(1);
471
+ opacity: 1;
472
+ }
473
+ }
474
+ </style>