@antelopejs/dms-ai 0.1.3 → 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 (245) 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/pages/settings/page.js.map +1 -1
  32. package/dist/routes/channels.d.ts +3 -3
  33. package/dist/routes/channels.js +11 -12
  34. package/dist/routes/channels.js.map +1 -1
  35. package/dist/routes/index.d.ts +0 -1
  36. package/dist/routes/index.js +0 -1
  37. package/dist/routes/index.js.map +1 -1
  38. package/frontend-vue/.prettierignore +3 -0
  39. package/frontend-vue/.prettierrc +10 -0
  40. package/frontend-vue/app/chat/components/ChatView.vue +582 -0
  41. package/frontend-vue/app/chat/components/ComposerInput.vue +742 -0
  42. package/frontend-vue/app/chat/components/ConnectionBanner.vue +105 -0
  43. package/frontend-vue/app/chat/components/ConversationDrawer.vue +266 -0
  44. package/frontend-vue/app/chat/components/MarkdownContent.vue +196 -0
  45. package/frontend-vue/app/chat/components/MessageList.vue +474 -0
  46. package/frontend-vue/app/chat/components/PermissionRequests.vue +323 -0
  47. package/frontend-vue/app/chat/components/QuestionPrompt.vue +275 -0
  48. package/frontend-vue/app/chat/components/QueuedMessages.vue +109 -0
  49. package/frontend-vue/app/chat/components/TodoList.vue +291 -0
  50. package/frontend-vue/app/chat/components/ToolCallEntry.vue +218 -0
  51. package/frontend-vue/app/chat/components/ToolCluster.vue +282 -0
  52. package/frontend-vue/app/chat/composables/useChatChannel.ts +116 -0
  53. package/frontend-vue/app/chat/composables/useConversation.ts +838 -0
  54. package/frontend-vue/app/chat/composables/useConversationId.ts +34 -0
  55. package/frontend-vue/app/chat/composables/useConversationList.ts +56 -0
  56. package/frontend-vue/app/chat/composables/usePermissionQueue.ts +59 -0
  57. package/frontend-vue/app/chat/composables/useQuestionQueue.ts +50 -0
  58. package/frontend-vue/app/chat/composables/useRunClock.ts +28 -0
  59. package/frontend-vue/app/chat/composables/useSettings.ts +67 -0
  60. package/frontend-vue/app/chat/constants/attachments.ts +16 -0
  61. package/frontend-vue/app/chat/constants/connection-banner.ts +7 -0
  62. package/frontend-vue/app/chat/constants/conversation-drawer.ts +13 -0
  63. package/frontend-vue/app/chat/constants/conversation.ts +75 -0
  64. package/frontend-vue/app/chat/constants/permissions.ts +50 -0
  65. package/frontend-vue/app/chat/constants/protocol.ts +48 -0
  66. package/frontend-vue/app/chat/constants/run-status.ts +50 -0
  67. package/frontend-vue/app/chat/constants/settings.ts +60 -0
  68. package/frontend-vue/app/chat/types/conversation.ts +87 -0
  69. package/frontend-vue/app/chat/types/permission.ts +7 -0
  70. package/frontend-vue/app/chat/types/question.ts +17 -0
  71. package/frontend-vue/app/chat/types/settings.ts +27 -0
  72. package/frontend-vue/app/chat/utils/attachments.ts +62 -0
  73. package/frontend-vue/app/chat/utils/format.ts +14 -0
  74. package/frontend-vue/app/chat/utils/ids.ts +34 -0
  75. package/frontend-vue/app/chat/utils/markdown.ts +188 -0
  76. package/frontend-vue/app/chat/utils/run-status.ts +90 -0
  77. package/frontend-vue/app/chat/utils/todos.ts +64 -0
  78. package/frontend-vue/app/chat/utils/tool-summary.ts +53 -0
  79. package/frontend-vue/app/components/ActivityView.vue +34 -34
  80. package/frontend-vue/app/components/ChatPanel.vue +276 -0
  81. package/frontend-vue/app/components/SettingsView.vue +127 -130
  82. package/frontend-vue/app/components/SkillsView.vue +55 -53
  83. package/frontend-vue/app/plugins/ai.client.ts +145 -129
  84. package/frontend-vue/app/runtime/assistant-session.ts +21 -0
  85. package/frontend-vue/app/runtime/backoff.ts +11 -14
  86. package/frontend-vue/app/runtime/channel-client.ts +130 -141
  87. package/frontend-vue/app/runtime/chat-transport.ts +25 -55
  88. package/frontend-vue/app/runtime/constants.ts +70 -60
  89. package/frontend-vue/app/runtime/current-page.ts +49 -15
  90. package/frontend-vue/app/runtime/event-stream.ts +35 -35
  91. package/frontend-vue/app/runtime/frame-router.ts +30 -0
  92. package/frontend-vue/app/runtime/header-action.ts +18 -18
  93. package/frontend-vue/app/runtime/host-commands.ts +57 -56
  94. package/frontend-vue/app/runtime/listeners.ts +31 -0
  95. package/frontend-vue/app/runtime/navigation-complete.ts +7 -7
  96. package/frontend-vue/app/runtime/overlay-prefs.ts +36 -36
  97. package/frontend-vue/app/runtime/panel-state.ts +88 -0
  98. package/frontend-vue/app/runtime/session-gate.ts +8 -8
  99. package/frontend-vue/app/runtime/shortcuts.ts +12 -12
  100. package/frontend-vue/app/runtime/sidecar-status.ts +61 -61
  101. package/frontend-vue/app/runtime/typed-message.ts +11 -0
  102. package/frontend-vue/dms.frontend.ts +12 -12
  103. package/frontend-vue/eslint.config.mjs +13 -13
  104. package/frontend-vue/package.json +11 -4
  105. package/frontend-vue/pnpm-lock.yaml +298 -96
  106. package/frontend-vue/tests/backoff.test.ts +26 -30
  107. package/frontend-vue/tests/channel-client.test.ts +187 -188
  108. package/frontend-vue/tests/chat/markdown.test.ts +271 -0
  109. package/frontend-vue/tests/chat/useChatChannel.test.ts +124 -0
  110. package/frontend-vue/tests/chat/useConversation.test.ts +283 -0
  111. package/frontend-vue/tests/chat-panel.test.ts +354 -0
  112. package/frontend-vue/tests/chat-transport.test.ts +84 -57
  113. package/frontend-vue/tests/host-commands.test.ts +126 -87
  114. package/frontend-vue/tests/navigation.test.ts +65 -34
  115. package/frontend-vue/tests/overlay-prefs.test.ts +49 -49
  116. package/frontend-vue/tests/panel-state.test.ts +58 -0
  117. package/frontend-vue/tests/session-gate.test.ts +33 -33
  118. package/frontend-vue/tests/sidecar-guest.test.ts +46 -46
  119. package/frontend-vue/tests/sidecar-status.test.ts +29 -29
  120. package/frontend-vue/tsconfig.chat.json +16 -0
  121. package/frontend-vue/vitest.config.ts +6 -4
  122. package/package.json +5 -5
  123. package/sidecar/dist/agent/effective-mode.d.ts +2 -2
  124. package/sidecar/dist/agent/effective-mode.js +1 -1
  125. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  126. package/sidecar/dist/agent/host-context.js +7 -9
  127. package/sidecar/dist/agent/host-context.js.map +1 -1
  128. package/sidecar/dist/agent/permission-bus.d.ts +1 -1
  129. package/sidecar/dist/agent/permission-bus.js +2 -2
  130. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  131. package/sidecar/dist/agent/provider.d.ts +2 -2
  132. package/sidecar/dist/agent/question-bus.d.ts +1 -1
  133. package/sidecar/dist/agent/question-bus.js +2 -2
  134. package/sidecar/dist/agent/question-bus.js.map +1 -1
  135. package/sidecar/dist/agent/runner.d.ts +2 -2
  136. package/sidecar/dist/agent/runner.js +24 -8
  137. package/sidecar/dist/agent/runner.js.map +1 -1
  138. package/sidecar/dist/agent/session.d.ts +2 -2
  139. package/sidecar/dist/agent/session.js +49 -9
  140. package/sidecar/dist/agent/session.js.map +1 -1
  141. package/sidecar/dist/agent/switching-runner.js +10 -5
  142. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  143. package/sidecar/dist/agent/turn-end-reasons.d.ts +2 -0
  144. package/sidecar/dist/agent/turn-end-reasons.js +6 -1
  145. package/sidecar/dist/agent/turn-end-reasons.js.map +1 -1
  146. package/sidecar/dist/constants/agent.d.ts +4 -1
  147. package/sidecar/dist/constants/agent.js +4 -1
  148. package/sidecar/dist/constants/agent.js.map +1 -1
  149. package/sidecar/dist/constants/cli.d.ts +0 -1
  150. package/sidecar/dist/constants/cli.js +0 -1
  151. package/sidecar/dist/constants/cli.js.map +1 -1
  152. package/sidecar/dist/constants/codex.d.ts +5 -2
  153. package/sidecar/dist/constants/codex.js +5 -2
  154. package/sidecar/dist/constants/codex.js.map +1 -1
  155. package/sidecar/dist/constants/http.d.ts +0 -9
  156. package/sidecar/dist/constants/http.js +0 -19
  157. package/sidecar/dist/constants/http.js.map +1 -1
  158. package/sidecar/dist/constants/mcp.d.ts +1 -1
  159. package/sidecar/dist/constants/mcp.js +1 -1
  160. package/sidecar/dist/constants/mcp.js.map +1 -1
  161. package/sidecar/dist/constants/paths.d.ts +0 -1
  162. package/sidecar/dist/constants/paths.js +0 -1
  163. package/sidecar/dist/constants/paths.js.map +1 -1
  164. package/sidecar/dist/constants/ws.d.ts +3 -4
  165. package/sidecar/dist/constants/ws.js +3 -4
  166. package/sidecar/dist/constants/ws.js.map +1 -1
  167. package/sidecar/dist/index.d.ts +0 -1
  168. package/sidecar/dist/index.js +6 -10
  169. package/sidecar/dist/index.js.map +1 -1
  170. package/sidecar/dist/mcp/tools/find-pages-using.js +5 -10
  171. package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
  172. package/sidecar/dist/mcp/types.d.ts +1 -1
  173. package/sidecar/dist/pages/page-filepath.d.ts +4 -0
  174. package/sidecar/dist/pages/page-filepath.js +28 -0
  175. package/sidecar/dist/pages/page-filepath.js.map +1 -0
  176. package/sidecar/dist/prompts/agent.d.ts +1 -1
  177. package/sidecar/dist/prompts/agent.js +1 -1
  178. package/sidecar/dist/prompts/agent.js.map +1 -1
  179. package/sidecar/dist/protocol/events.js +2 -2
  180. package/sidecar/dist/protocol/events.js.map +1 -1
  181. package/sidecar/dist/protocol/messages.d.ts +4 -15
  182. package/sidecar/dist/protocol/messages.js +4 -11
  183. package/sidecar/dist/protocol/messages.js.map +1 -1
  184. package/sidecar/dist/providers/claude/config.d.ts +2 -2
  185. package/sidecar/dist/providers/claude/config.js +2 -2
  186. package/sidecar/dist/providers/claude/config.js.map +1 -1
  187. package/sidecar/dist/providers/claude/provider.js +1 -1
  188. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  189. package/sidecar/dist/providers/codex/config.d.ts +3 -2
  190. package/sidecar/dist/providers/codex/config.js +3 -3
  191. package/sidecar/dist/providers/codex/config.js.map +1 -1
  192. package/sidecar/dist/providers/codex/process.js +107 -74
  193. package/sidecar/dist/providers/codex/process.js.map +1 -1
  194. package/sidecar/dist/providers/codex/provider.js +68 -31
  195. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  196. package/sidecar/dist/providers/codex/skills.d.ts +1 -0
  197. package/sidecar/dist/providers/codex/skills.js +4 -1
  198. package/sidecar/dist/providers/codex/skills.js.map +1 -1
  199. package/sidecar/dist/providers/codex/stop.d.ts +4 -0
  200. package/sidecar/dist/providers/codex/stop.js +71 -0
  201. package/sidecar/dist/providers/codex/stop.js.map +1 -0
  202. package/sidecar/dist/server/{iframe-socket-registry.d.ts → chat-socket-registry.d.ts} +3 -2
  203. package/sidecar/dist/server/{iframe-socket-registry.js → chat-socket-registry.js} +4 -3
  204. package/sidecar/dist/server/chat-socket-registry.js.map +1 -0
  205. package/sidecar/dist/server/client-auth.js +2 -14
  206. package/sidecar/dist/server/client-auth.js.map +1 -1
  207. package/sidecar/dist/server/host-command-router.d.ts +5 -0
  208. package/sidecar/dist/server/host-command-router.js +4 -0
  209. package/sidecar/dist/server/host-command-router.js.map +1 -0
  210. package/sidecar/dist/server/host-socket-registry.d.ts +1 -1
  211. package/sidecar/dist/server/host-socket-registry.js +16 -13
  212. package/sidecar/dist/server/host-socket-registry.js.map +1 -1
  213. package/sidecar/dist/server/http.d.ts +0 -1
  214. package/sidecar/dist/server/http.js +1 -44
  215. package/sidecar/dist/server/http.js.map +1 -1
  216. package/sidecar/dist/server/routing.d.ts +5 -6
  217. package/sidecar/dist/server/routing.js +18 -28
  218. package/sidecar/dist/server/routing.js.map +1 -1
  219. package/sidecar/dist/server/ws.d.ts +2 -2
  220. package/sidecar/dist/server/ws.js +41 -39
  221. package/sidecar/dist/server/ws.js.map +1 -1
  222. package/sidecar/dist/state/host-state.d.ts +10 -2
  223. package/sidecar/dist/state/host-state.js +13 -3
  224. package/sidecar/dist/state/host-state.js.map +1 -1
  225. package/sidecar/dist/state/settings-store.js +2 -2
  226. package/sidecar/dist/state/settings-store.js.map +1 -1
  227. package/sidecar/dist/state/settings-types.d.ts +3 -3
  228. package/sidecar/dist/state/settings-types.js +1 -1
  229. package/sidecar/dist/state/settings-types.js.map +1 -1
  230. package/sidecar/package.json +3 -5
  231. package/dist/chatbox/passthrough.d.ts +0 -3
  232. package/dist/chatbox/passthrough.js +0 -53
  233. package/dist/chatbox/passthrough.js.map +0 -1
  234. package/dist/constants/chatbox.d.ts +0 -10
  235. package/dist/constants/chatbox.js +0 -35
  236. package/dist/constants/chatbox.js.map +0 -1
  237. package/dist/routes/chatbox.d.ts +0 -7
  238. package/dist/routes/chatbox.js +0 -47
  239. package/dist/routes/chatbox.js.map +0 -1
  240. package/frontend-vue/app/runtime/overlay.ts +0 -400
  241. package/frontend-vue/app/runtime/theme-bridge.ts +0 -189
  242. package/sidecar/chatbox/dist/assets/index-C8u7mkHn.css +0 -1
  243. package/sidecar/chatbox/dist/assets/index-tdZvY7ee.js +0 -52
  244. package/sidecar/chatbox/dist/index.html +0 -13
  245. package/sidecar/dist/server/iframe-socket-registry.js.map +0 -1
@@ -0,0 +1,291 @@
1
+ <script setup lang="ts">
2
+ import { computed, nextTick, onMounted, ref, watch } from "vue";
3
+ import { TODO_PANEL_TITLE, TODO_STATUS } from "../constants/conversation";
4
+ import type { TodoItem } from "../utils/todos";
5
+
6
+ interface Props {
7
+ todos: TodoItem[];
8
+ isRunning: boolean;
9
+ }
10
+
11
+ const props = defineProps<Props>();
12
+
13
+ const TODO_ITEM_GAP_PX = 2;
14
+
15
+ const expanded = ref(true);
16
+ const itemsEl = ref<HTMLElement | null>(null);
17
+
18
+ const activeIndex = computed<number>(() =>
19
+ props.todos.findIndex((t) => t.status === TODO_STATUS.IN_PROGRESS),
20
+ );
21
+
22
+ async function scrollActiveIntoView(): Promise<void> {
23
+ if (!expanded.value || activeIndex.value < 0) return;
24
+ await nextTick();
25
+ const el = itemsEl.value;
26
+ if (el === null) return;
27
+ const child = el.children[activeIndex.value];
28
+ if (child === undefined) return;
29
+ const list = el.getBoundingClientRect();
30
+ const item = child.getBoundingClientRect();
31
+ const margin = item.height + TODO_ITEM_GAP_PX;
32
+ if (item.top < list.top + margin) {
33
+ el.scrollTop += item.top - list.top - margin;
34
+ } else if (item.bottom > list.bottom - margin) {
35
+ el.scrollTop += item.bottom - list.bottom + margin;
36
+ }
37
+ }
38
+
39
+ watch(
40
+ () => [props.todos, expanded.value],
41
+ () => {
42
+ void scrollActiveIntoView();
43
+ },
44
+ );
45
+
46
+ onMounted(() => {
47
+ void scrollActiveIntoView();
48
+ });
49
+
50
+ const completedCount = computed<number>(
51
+ () => props.todos.filter((t) => t.status === TODO_STATUS.COMPLETED).length,
52
+ );
53
+
54
+ const allDone = computed<boolean>(
55
+ () => props.todos.length > 0 && completedCount.value === props.todos.length,
56
+ );
57
+
58
+ const progressPercent = computed<number>(() => {
59
+ if (props.todos.length === 0) return 0;
60
+ return Math.round((completedCount.value / props.todos.length) * 100);
61
+ });
62
+
63
+ const activeLabel = computed<string>(() => {
64
+ const active = props.todos.find((t) => t.status === TODO_STATUS.IN_PROGRESS);
65
+ if (active !== undefined) return active.activeForm;
66
+ if (allDone.value) return "";
67
+ const next = props.todos.find((t) => t.status === TODO_STATUS.PENDING);
68
+ return next?.content ?? "";
69
+ });
70
+
71
+ function toggle(): void {
72
+ expanded.value = !expanded.value;
73
+ }
74
+
75
+ function todoText(todo: TodoItem): string {
76
+ return todo.status === TODO_STATUS.IN_PROGRESS
77
+ ? todo.activeForm
78
+ : todo.content;
79
+ }
80
+ </script>
81
+
82
+ <template>
83
+ <section class="todo-dock" :data-complete="allDone">
84
+ <button
85
+ type="button"
86
+ class="todo-head"
87
+ :aria-expanded="expanded"
88
+ @click="toggle"
89
+ >
90
+ <span class="todo-toggle">{{ expanded ? "▾" : "▸" }}</span>
91
+ <span class="todo-title">{{ TODO_PANEL_TITLE }}</span>
92
+ <span v-if="!expanded && activeLabel.length > 0" class="todo-active">
93
+ {{ activeLabel }}
94
+ </span>
95
+ <span class="todo-count">{{ completedCount }}/{{ todos.length }}</span>
96
+ </button>
97
+ <div class="todo-track" aria-hidden="true">
98
+ <div class="todo-fill" :style="{ width: `${progressPercent}%` }"></div>
99
+ </div>
100
+ <ul v-if="expanded" ref="itemsEl" class="todo-items">
101
+ <li
102
+ v-for="(todo, i) in todos"
103
+ :key="i"
104
+ class="todo-item"
105
+ :data-status="todo.status"
106
+ >
107
+ <span class="todo-glyph" :data-status="todo.status" aria-hidden="true">
108
+ <svg
109
+ v-if="todo.status === 'completed'"
110
+ viewBox="0 0 24 24"
111
+ width="12"
112
+ height="12"
113
+ fill="none"
114
+ stroke="currentColor"
115
+ stroke-width="3"
116
+ stroke-linecap="round"
117
+ stroke-linejoin="round"
118
+ >
119
+ <path d="M20 6 9 17l-5-5" />
120
+ </svg>
121
+ <svg
122
+ v-else-if="todo.status === 'in_progress' && isRunning"
123
+ class="todo-spin"
124
+ viewBox="0 0 24 24"
125
+ width="12"
126
+ height="12"
127
+ fill="none"
128
+ stroke="currentColor"
129
+ stroke-width="2.5"
130
+ stroke-linecap="round"
131
+ >
132
+ <path d="M21 12a9 9 0 1 1-6.2-8.6" />
133
+ </svg>
134
+ <svg
135
+ v-else
136
+ viewBox="0 0 24 24"
137
+ width="12"
138
+ height="12"
139
+ fill="none"
140
+ stroke="currentColor"
141
+ stroke-width="2"
142
+ >
143
+ <circle cx="12" cy="12" r="8" />
144
+ </svg>
145
+ </span>
146
+ <span class="todo-text">{{ todoText(todo) }}</span>
147
+ </li>
148
+ </ul>
149
+ </section>
150
+ </template>
151
+
152
+ <style scoped>
153
+ .todo-dock {
154
+ flex: 0 0 auto;
155
+ background: var(--surface-side);
156
+ border-top: 1px solid var(--hair);
157
+ }
158
+
159
+ .todo-head {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 8px;
163
+ width: 100%;
164
+ padding: 8px 14px;
165
+ background: transparent;
166
+ border: none;
167
+ cursor: pointer;
168
+ font: inherit;
169
+ text-align: left;
170
+ color: var(--fg);
171
+ }
172
+
173
+ .todo-head:hover {
174
+ background: var(--surface-inset);
175
+ }
176
+
177
+ .todo-toggle {
178
+ color: var(--fg-tertiary);
179
+ font-size: 11px;
180
+ flex: 0 0 auto;
181
+ }
182
+
183
+ .todo-title {
184
+ font-weight: 600;
185
+ font-size: 12.5px;
186
+ flex: 0 0 auto;
187
+ }
188
+
189
+ .todo-active {
190
+ flex: 1;
191
+ min-width: 0;
192
+ font-size: 11.5px;
193
+ color: var(--fg-tertiary);
194
+ white-space: nowrap;
195
+ overflow: hidden;
196
+ text-overflow: ellipsis;
197
+ }
198
+
199
+ .todo-count {
200
+ margin-left: auto;
201
+ font-family: var(--font-code);
202
+ font-size: 10px;
203
+ font-weight: 700;
204
+ letter-spacing: 0.04em;
205
+ color: var(--fg-tertiary);
206
+ flex: 0 0 auto;
207
+ }
208
+
209
+ .todo-dock[data-complete="true"] .todo-count {
210
+ color: var(--success-400);
211
+ }
212
+
213
+ .todo-track {
214
+ height: 2px;
215
+ background: var(--surface-inset);
216
+ }
217
+
218
+ .todo-fill {
219
+ height: 100%;
220
+ background: var(--accent);
221
+ transition: width 0.25s ease;
222
+ }
223
+
224
+ .todo-dock[data-complete="true"] .todo-fill {
225
+ background: var(--success-400);
226
+ }
227
+
228
+ .todo-items {
229
+ list-style: none;
230
+ margin: 0;
231
+ padding: 6px 8px 8px;
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: 2px;
235
+ max-height: 150px;
236
+ overflow-y: auto;
237
+ }
238
+
239
+ .todo-item {
240
+ display: flex;
241
+ align-items: flex-start;
242
+ gap: 8px;
243
+ padding: 3px 4px;
244
+ font-size: 12.5px;
245
+ line-height: 1.45;
246
+ }
247
+
248
+ .todo-glyph {
249
+ width: 16px;
250
+ height: 16px;
251
+ flex: 0 0 auto;
252
+ display: grid;
253
+ place-items: center;
254
+ margin-top: 1px;
255
+ color: var(--fg-tertiary);
256
+ }
257
+
258
+ .todo-glyph[data-status="completed"] {
259
+ color: var(--success-400);
260
+ }
261
+
262
+ .todo-glyph[data-status="in_progress"] {
263
+ color: var(--accent);
264
+ }
265
+
266
+ .todo-spin {
267
+ animation: todo-spin 0.9s linear infinite;
268
+ }
269
+
270
+ @keyframes todo-spin {
271
+ to {
272
+ transform: rotate(360deg);
273
+ }
274
+ }
275
+
276
+ .todo-text {
277
+ min-width: 0;
278
+ word-break: break-word;
279
+ color: var(--fg-secondary);
280
+ }
281
+
282
+ .todo-item[data-status="completed"] .todo-text {
283
+ color: var(--fg-tertiary);
284
+ text-decoration: line-through;
285
+ }
286
+
287
+ .todo-item[data-status="in_progress"] .todo-text {
288
+ color: var(--fg);
289
+ font-weight: 500;
290
+ }
291
+ </style>
@@ -0,0 +1,218 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from "vue";
3
+ import { STATUS_LABELS, TOOL_STATUS } from "../constants/conversation";
4
+ import type { ToolCallMessage } from "../types/conversation";
5
+ import { stringifyValue } from "../utils/format";
6
+ import { toolLabel, toolSummary } from "../utils/tool-summary";
7
+
8
+ interface Props {
9
+ message: ToolCallMessage;
10
+ }
11
+
12
+ const props = defineProps<Props>();
13
+ const isExpanded = ref(false);
14
+
15
+ const STATUS_BADGE_LABELS: Record<string, string> = {
16
+ [TOOL_STATUS.PENDING]: STATUS_LABELS.PENDING,
17
+ [TOOL_STATUS.SUCCESS]: STATUS_LABELS.SUCCESS,
18
+ [TOOL_STATUS.ERROR]: STATUS_LABELS.ERROR,
19
+ };
20
+
21
+ const statusLabel = computed<string>(
22
+ () => STATUS_BADGE_LABELS[props.message.status] ?? props.message.status,
23
+ );
24
+
25
+ const title = computed<string>(() =>
26
+ props.message.toolName.length > 0
27
+ ? toolLabel(props.message.toolName)
28
+ : "Tool",
29
+ );
30
+ const summary = computed<string>(() =>
31
+ toolSummary(props.message.toolName, props.message.args),
32
+ );
33
+
34
+ const argsText = computed<string>(() => stringifyValue(props.message.args));
35
+ const resultText = computed<string>(() => stringifyValue(props.message.result));
36
+
37
+ function toggle(): void {
38
+ isExpanded.value = !isExpanded.value;
39
+ }
40
+ </script>
41
+
42
+ <template>
43
+ <article class="tool-entry" :data-status="message.status">
44
+ <button
45
+ type="button"
46
+ class="tool-header"
47
+ :aria-expanded="isExpanded"
48
+ @click="toggle"
49
+ >
50
+ <span class="tool-icon" aria-hidden="true">
51
+ <svg
52
+ viewBox="0 0 24 24"
53
+ width="14"
54
+ height="14"
55
+ fill="none"
56
+ stroke="currentColor"
57
+ stroke-width="2"
58
+ stroke-linecap="round"
59
+ stroke-linejoin="round"
60
+ >
61
+ <path
62
+ d="M14.7 6.3a4 4 0 0 0-5.6 5.6l-6 6a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l6-6a4 4 0 0 0 5.6-5.6l-2.5 2.5-2-2 2.5-2.5z"
63
+ />
64
+ </svg>
65
+ </span>
66
+ <span class="tool-text">
67
+ <span class="tool-name">{{ title }}</span>
68
+ <span v-if="summary.length > 0" class="tool-summary">
69
+ {{ summary }}
70
+ </span>
71
+ </span>
72
+ <span class="tool-status" :data-status="message.status">
73
+ {{ statusLabel }}
74
+ </span>
75
+ <span class="tool-toggle">{{ isExpanded ? "▾" : "▸" }}</span>
76
+ </button>
77
+ <div v-if="isExpanded" class="tool-body">
78
+ <section v-if="argsText.length > 0" class="tool-section">
79
+ <h4 class="tool-section-title">args</h4>
80
+ <pre class="tool-pre">{{ argsText }}</pre>
81
+ </section>
82
+ <section v-if="resultText.length > 0" class="tool-section">
83
+ <h4 class="tool-section-title">result</h4>
84
+ <pre class="tool-pre">{{ resultText }}</pre>
85
+ </section>
86
+ </div>
87
+ </article>
88
+ </template>
89
+
90
+ <style scoped>
91
+ .tool-entry {
92
+ border: 1px solid var(--hair);
93
+ border-radius: var(--corner-md);
94
+ background: var(--surface-card);
95
+ overflow: hidden;
96
+ }
97
+
98
+ .tool-header {
99
+ display: flex;
100
+ align-items: center;
101
+ gap: 9px;
102
+ width: 100%;
103
+ padding: 8px 10px;
104
+ background: transparent;
105
+ border: none;
106
+ cursor: pointer;
107
+ font: inherit;
108
+ text-align: left;
109
+ color: var(--fg);
110
+ }
111
+
112
+ .tool-header:hover {
113
+ background: var(--surface-card-2);
114
+ }
115
+
116
+ .tool-icon {
117
+ width: 24px;
118
+ height: 24px;
119
+ flex: 0 0 auto;
120
+ display: grid;
121
+ place-items: center;
122
+ border-radius: 6px;
123
+ background: var(--surface-inset);
124
+ border: 1px solid var(--hair);
125
+ color: var(--fg-secondary);
126
+ }
127
+
128
+ .tool-text {
129
+ flex: 1;
130
+ min-width: 0;
131
+ display: flex;
132
+ align-items: baseline;
133
+ gap: 8px;
134
+ }
135
+
136
+ .tool-name {
137
+ font-weight: 600;
138
+ font-size: 12.5px;
139
+ flex: 0 0 auto;
140
+ }
141
+
142
+ .tool-summary {
143
+ font-family: var(--font-code);
144
+ font-size: 11px;
145
+ color: var(--fg-tertiary);
146
+ white-space: nowrap;
147
+ overflow: hidden;
148
+ text-overflow: ellipsis;
149
+ min-width: 0;
150
+ }
151
+
152
+ .tool-status {
153
+ font-family: var(--font-code);
154
+ font-size: 9px;
155
+ font-weight: 700;
156
+ letter-spacing: 0.04em;
157
+ text-transform: uppercase;
158
+ padding: 2px 6px;
159
+ border-radius: var(--corner-sm);
160
+ background: var(--surface-inset);
161
+ color: var(--fg-tertiary);
162
+ flex: 0 0 auto;
163
+ }
164
+
165
+ .tool-status[data-status="pending"] {
166
+ background: var(--warning-bg);
167
+ color: var(--warning-400);
168
+ }
169
+
170
+ .tool-status[data-status="success"] {
171
+ background: var(--success-bg);
172
+ color: var(--success-400);
173
+ }
174
+
175
+ .tool-status[data-status="error"] {
176
+ background: var(--danger-bg);
177
+ color: var(--danger-400);
178
+ }
179
+
180
+ .tool-toggle {
181
+ color: var(--fg-tertiary);
182
+ font-size: 11px;
183
+ flex: 0 0 auto;
184
+ }
185
+
186
+ .tool-body {
187
+ padding: 10px 12px;
188
+ border-top: 1px solid var(--hair);
189
+ }
190
+
191
+ .tool-section + .tool-section {
192
+ margin-top: 8px;
193
+ }
194
+
195
+ .tool-section-title {
196
+ margin: 0 0 4px;
197
+ font-family: var(--font-code);
198
+ font-size: 10px;
199
+ text-transform: uppercase;
200
+ letter-spacing: 0.04em;
201
+ color: var(--fg-tertiary);
202
+ }
203
+
204
+ .tool-pre {
205
+ margin: 0;
206
+ padding: 8px;
207
+ background: var(--surface-inset);
208
+ border: 1px solid var(--hair);
209
+ border-radius: var(--corner-sm);
210
+ font-family: var(--font-code);
211
+ font-size: 11px;
212
+ color: var(--fg-secondary);
213
+ white-space: pre-wrap;
214
+ word-break: break-word;
215
+ max-height: 300px;
216
+ overflow: auto;
217
+ }
218
+ </style>