@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,34 @@
1
+ import { STORAGE_CONVERSATION_ID_KEY } from "../constants/conversation";
2
+ import { newUuid } from "../utils/ids";
3
+
4
+ function readPersisted(): string | null {
5
+ try {
6
+ return window.localStorage.getItem(STORAGE_CONVERSATION_ID_KEY);
7
+ } catch {
8
+ return null;
9
+ }
10
+ }
11
+
12
+ function persist(id: string): void {
13
+ try {
14
+ window.localStorage.setItem(STORAGE_CONVERSATION_ID_KEY, id);
15
+ } catch {
16
+ return;
17
+ }
18
+ }
19
+
20
+ export function resolveConversationId(): string {
21
+ const existing = readPersisted();
22
+ if (existing !== null && existing.length > 0) return existing;
23
+ const next = newUuid();
24
+ persist(next);
25
+ return next;
26
+ }
27
+
28
+ export function persistConversationId(id: string): void {
29
+ persist(id);
30
+ }
31
+
32
+ export function newConversationId(): string {
33
+ return newUuid();
34
+ }
@@ -0,0 +1,56 @@
1
+ import { type Ref, ref } from "vue";
2
+ import {
3
+ CLIENT_MESSAGE_TYPES,
4
+ SERVER_EVENT_TYPES,
5
+ } from "../constants/protocol";
6
+ import type { ConversationSummary } from "../types/conversation";
7
+
8
+ export interface UseConversationListOptions {
9
+ send: (msg: object) => void;
10
+ onMessage: (handler: (msg: unknown) => void) => () => void;
11
+ }
12
+
13
+ export interface UseConversationListResult {
14
+ conversations: Ref<ConversationSummary[]>;
15
+ refresh: () => void;
16
+ remove: (id: string) => void;
17
+ }
18
+
19
+ interface ConversationListEvent {
20
+ type: typeof SERVER_EVENT_TYPES.CONVERSATION_LIST;
21
+ conversations: ConversationSummary[];
22
+ }
23
+
24
+ function isObject(value: unknown): value is Record<string, unknown> {
25
+ return value !== null && typeof value === "object";
26
+ }
27
+
28
+ function getEventType(msg: unknown): string | null {
29
+ if (!isObject(msg)) return null;
30
+ const t = msg.type;
31
+ return typeof t === "string" ? t : null;
32
+ }
33
+
34
+ export function useConversationList(
35
+ options: UseConversationListOptions,
36
+ ): UseConversationListResult {
37
+ const conversations = ref<ConversationSummary[]>([]);
38
+
39
+ options.onMessage((msg: unknown): void => {
40
+ if (getEventType(msg) !== SERVER_EVENT_TYPES.CONVERSATION_LIST) return;
41
+ conversations.value = (msg as ConversationListEvent).conversations;
42
+ });
43
+
44
+ const refresh = (): void => {
45
+ options.send({ type: CLIENT_MESSAGE_TYPES.LIST_CONVERSATIONS });
46
+ };
47
+
48
+ const remove = (id: string): void => {
49
+ options.send({
50
+ type: CLIENT_MESSAGE_TYPES.DELETE_CONVERSATION,
51
+ conversationId: id,
52
+ });
53
+ };
54
+
55
+ return { conversations, refresh, remove };
56
+ }
@@ -0,0 +1,59 @@
1
+ import { type Ref, ref } from "vue";
2
+ import type { PermissionDecision } from "../constants/permissions";
3
+ import { CLIENT_MESSAGE_TYPES } from "../constants/protocol";
4
+ import type { PermissionRequestData } from "../types/permission";
5
+
6
+ export interface UsePermissionQueueOptions {
7
+ send: (msg: object) => void;
8
+ }
9
+
10
+ export interface UsePermissionQueueResult {
11
+ queue: Ref<PermissionRequestData[]>;
12
+ enqueue: (req: PermissionRequestData) => void;
13
+ respond: (requestId: string, decision: PermissionDecision) => void;
14
+ respondAll: (decision: PermissionDecision) => void;
15
+ clear: () => void;
16
+ }
17
+
18
+ function buildResponseMessage(
19
+ req: PermissionRequestData,
20
+ decision: PermissionDecision,
21
+ ): object {
22
+ return {
23
+ type: CLIENT_MESSAGE_TYPES.PERMISSION_RESPONSE,
24
+ conversationId: req.conversationId,
25
+ requestId: req.requestId,
26
+ decision,
27
+ };
28
+ }
29
+
30
+ export function usePermissionQueue(
31
+ options: UsePermissionQueueOptions,
32
+ ): UsePermissionQueueResult {
33
+ const queue = ref<PermissionRequestData[]>([]);
34
+
35
+ const enqueue = (req: PermissionRequestData): void => {
36
+ if (queue.value.some((q) => q.requestId === req.requestId)) return;
37
+ queue.value = [...queue.value, req];
38
+ };
39
+
40
+ const respond = (requestId: string, decision: PermissionDecision): void => {
41
+ const target = queue.value.find((q) => q.requestId === requestId);
42
+ if (target === undefined) return;
43
+ options.send(buildResponseMessage(target, decision));
44
+ queue.value = queue.value.filter((q) => q.requestId !== requestId);
45
+ };
46
+
47
+ const respondAll = (decision: PermissionDecision): void => {
48
+ for (const req of queue.value) {
49
+ options.send(buildResponseMessage(req, decision));
50
+ }
51
+ queue.value = [];
52
+ };
53
+
54
+ const clear = (): void => {
55
+ queue.value = [];
56
+ };
57
+
58
+ return { queue, enqueue, respond, respondAll, clear };
59
+ }
@@ -0,0 +1,50 @@
1
+ import { type Ref, ref } from "vue";
2
+ import { CLIENT_MESSAGE_TYPES } from "../constants/protocol";
3
+ import type { QuestionRequestData } from "../types/question";
4
+
5
+ export interface UseQuestionQueueOptions {
6
+ send: (msg: object) => void;
7
+ }
8
+
9
+ export interface UseQuestionQueueResult {
10
+ queue: Ref<QuestionRequestData[]>;
11
+ enqueue: (req: QuestionRequestData) => void;
12
+ respond: (requestId: string, answers: string[]) => void;
13
+ clear: () => void;
14
+ }
15
+
16
+ function buildResponseMessage(
17
+ req: QuestionRequestData,
18
+ answers: string[],
19
+ ): object {
20
+ return {
21
+ type: CLIENT_MESSAGE_TYPES.QUESTION_RESPONSE,
22
+ conversationId: req.conversationId,
23
+ requestId: req.requestId,
24
+ answers,
25
+ };
26
+ }
27
+
28
+ export function useQuestionQueue(
29
+ options: UseQuestionQueueOptions,
30
+ ): UseQuestionQueueResult {
31
+ const queue = ref<QuestionRequestData[]>([]);
32
+
33
+ const enqueue = (req: QuestionRequestData): void => {
34
+ if (queue.value.some((q) => q.requestId === req.requestId)) return;
35
+ queue.value = [...queue.value, req];
36
+ };
37
+
38
+ const respond = (requestId: string, answers: string[]): void => {
39
+ const target = queue.value.find((q) => q.requestId === requestId);
40
+ if (target === undefined) return;
41
+ options.send(buildResponseMessage(target, answers));
42
+ queue.value = queue.value.filter((q) => q.requestId !== requestId);
43
+ };
44
+
45
+ const clear = (): void => {
46
+ queue.value = [];
47
+ };
48
+
49
+ return { queue, enqueue, respond, clear };
50
+ }
@@ -0,0 +1,28 @@
1
+ import { onBeforeUnmount, type Ref, ref, watch } from "vue";
2
+ import { RUN_CLOCK_TICK_MS } from "../constants/run-status";
3
+
4
+ /**
5
+ * The current time, ticking every second while `isActive` is true, so elapsed
6
+ * and quiet durations keep moving between two reports from the sidecar.
7
+ */
8
+ export function useRunClock(isActive: Ref<boolean>): Ref<number> {
9
+ const nowMs = ref(Date.now());
10
+ let timer: ReturnType<typeof setInterval> | null = null;
11
+ const stop = (): void => {
12
+ if (timer === null) return;
13
+ clearInterval(timer);
14
+ timer = null;
15
+ };
16
+ const start = (): void => {
17
+ stop();
18
+ nowMs.value = Date.now();
19
+ timer = setInterval(() => {
20
+ nowMs.value = Date.now();
21
+ }, RUN_CLOCK_TICK_MS);
22
+ };
23
+ watch(isActive, (active) => (active ? start() : stop()), {
24
+ immediate: true,
25
+ });
26
+ onBeforeUnmount(stop);
27
+ return nowMs;
28
+ }
@@ -0,0 +1,67 @@
1
+ import { type Ref, ref } from "vue";
2
+ import { DEFAULT_SETTINGS } from "../constants/settings";
3
+ import {
4
+ CLIENT_MESSAGE_TYPES,
5
+ SERVER_EVENT_TYPES,
6
+ } from "../constants/protocol";
7
+ import type {
8
+ AppSettings,
9
+ ProviderAvailability,
10
+ ProviderName,
11
+ } from "../types/settings";
12
+
13
+ export interface UseSettingsOptions {
14
+ send: (msg: object) => void;
15
+ onMessage: (handler: (msg: unknown) => void) => () => void;
16
+ }
17
+
18
+ export interface UseSettingsResult {
19
+ settings: Ref<AppSettings>;
20
+ update: (next: Partial<AppSettings>) => void;
21
+ }
22
+
23
+ interface SettingsUpdateEvent {
24
+ type: typeof SERVER_EVENT_TYPES.SETTINGS_UPDATE;
25
+ settings: AppSettings;
26
+ builderAvailable?: boolean;
27
+ providers?: Record<ProviderName, ProviderAvailability>;
28
+ }
29
+
30
+ function isObject(value: unknown): value is Record<string, unknown> {
31
+ return value !== null && typeof value === "object";
32
+ }
33
+
34
+ function getEventType(msg: unknown): string | null {
35
+ if (!isObject(msg)) return null;
36
+ const t = msg.type;
37
+ return typeof t === "string" ? t : null;
38
+ }
39
+
40
+ export function useSettings(options: UseSettingsOptions): UseSettingsResult {
41
+ const settings = ref<AppSettings>({ ...DEFAULT_SETTINGS });
42
+
43
+ options.onMessage((msg: unknown): void => {
44
+ if (getEventType(msg) !== SERVER_EVENT_TYPES.SETTINGS_UPDATE) return;
45
+ const event = msg as SettingsUpdateEvent;
46
+ settings.value = {
47
+ ...event.settings,
48
+ builderAvailable: event.builderAvailable ?? false,
49
+ providers: event.providers ?? DEFAULT_SETTINGS.providers,
50
+ };
51
+ });
52
+
53
+ const update = (partial: Partial<AppSettings>): void => {
54
+ const next: AppSettings = { ...settings.value, ...partial };
55
+ settings.value = next;
56
+ options.send({
57
+ type: CLIENT_MESSAGE_TYPES.SET_SETTINGS,
58
+ provider: next.provider,
59
+ mode: next.mode,
60
+ thinking: next.thinking,
61
+ generationMode: next.generationMode,
62
+ allowLocalSkills: next.allowLocalSkills,
63
+ });
64
+ };
65
+
66
+ return { settings, update };
67
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Mirror of the sidecar's attachment limits (sidecar/src/constants/attachments.ts),
3
+ * kept in sync by hand like the protocol's message types.
4
+ */
5
+ export const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
6
+ export const MAX_ATTACHMENTS = 10;
7
+
8
+ /** Media types shown with an image thumbnail, and inlined natively by the agent. */
9
+ export const INLINE_IMAGE_MIME_TYPES = [
10
+ "image/jpeg",
11
+ "image/png",
12
+ "image/gif",
13
+ "image/webp",
14
+ ] as const;
15
+
16
+ export const ATTACH_LABEL = "Attach files";
@@ -0,0 +1,7 @@
1
+ export const BANNER_LABELS = {
2
+ connecting: "Connecting…",
3
+ reconnecting: "Reconnecting…",
4
+ disconnected: "Disconnected",
5
+ } as const;
6
+
7
+ export const RECONNECT_BUTTON_LABEL = "Reconnect now";
@@ -0,0 +1,13 @@
1
+ export const DRAWER_TITLE = "Conversations";
2
+ export const NEW_CONVERSATION_LABEL = "+ New";
3
+ export const EMPTY_LIST_LABEL = "No conversations yet";
4
+ export const DELETE_BUTTON_LABEL = "Delete";
5
+ export const DELETE_BUTTON_GLYPH = "🗑";
6
+ export const TOGGLE_DRAWER_LABEL = "Conversations";
7
+ export const CLOSE_DRAWER_LABEL = "Close";
8
+
9
+ export const RELATIVE_TIME_NOW = "just now";
10
+ export const SECONDS_PER_MINUTE = 60;
11
+ export const MINUTES_PER_HOUR = 60;
12
+ export const HOURS_PER_DAY = 24;
13
+ export const DAYS_BEFORE_DATE = 7;
@@ -0,0 +1,75 @@
1
+ export const MESSAGE_ROLES = {
2
+ USER: "user",
3
+ ASSISTANT: "assistant",
4
+ TOOL: "tool",
5
+ ERROR: "error",
6
+ } as const;
7
+
8
+ export const STORED_TOOL_USE_ROLE = "tool_use";
9
+ export const STORED_TOOL_RESULT_ROLE = "tool_result";
10
+ export const STORED_ERROR_ROLE = "error";
11
+
12
+ export const TOOL_STATUS = {
13
+ PENDING: "pending",
14
+ SUCCESS: "success",
15
+ ERROR: "error",
16
+ } as const;
17
+
18
+ export const TODO_WRITE_TOOL_NAME = "TodoWrite";
19
+
20
+ export const TODO_STATUS = {
21
+ PENDING: "pending",
22
+ IN_PROGRESS: "in_progress",
23
+ COMPLETED: "completed",
24
+ } as const;
25
+
26
+ export const TODO_PANEL_TITLE = "Plan";
27
+
28
+ export const STORAGE_CONVERSATION_ID_KEY = "dms-ai-conversation-id";
29
+
30
+ export const SEND_HOTKEY = "Enter";
31
+ export const NEWLINE_HOTKEY_MODIFIER = "shiftKey";
32
+
33
+ export const ROLE_LABELS = {
34
+ USER: "You",
35
+ ASSISTANT: "Assistant",
36
+ TOOL: "Tool",
37
+ ERROR: "The run did not finish",
38
+ } as const;
39
+
40
+ export const STATUS_LABELS = {
41
+ PENDING: "running",
42
+ SUCCESS: "success",
43
+ ERROR: "error",
44
+ } as const;
45
+
46
+ /** Shown when a message could not even leave the chat. */
47
+ export const NOT_SENT_MESSAGE =
48
+ "Your message was not sent: the assistant is not connected. Reconnect, then retry.";
49
+
50
+ /** Shown when a retry needs files that only the original message still had. */
51
+ export const RETRY_NEEDS_FILES_MESSAGE =
52
+ "Attach the files again to retry: they are no longer available after a reload.";
53
+
54
+ /** Shown when Stop could not reach the sidecar, so the run is let go locally. */
55
+ export const STOP_NOT_DELIVERED_MESSAGE =
56
+ "The assistant could not be reached to stop the run. Reconnect to see where it stands.";
57
+
58
+ /** Result given to a tool that was still running when its run ended. */
59
+ export const TOOL_CUT_SHORT_RESULT = "The run ended before this tool finished.";
60
+
61
+ export const RETRY_LABEL = "Retry";
62
+
63
+ export const TOOL_CLUSTER_LABEL = "Used";
64
+
65
+ /**
66
+ * A live cluster peeks only its most recent rows; the rest hide behind a
67
+ * reveal.
68
+ */
69
+ export const TOOL_CLUSTER_VISIBLE_LIMIT = 3;
70
+
71
+ /**
72
+ * How long a cluster stays open after it stops being the active workspace, so
73
+ * a fast hand-off to the next cluster doesn't flash.
74
+ */
75
+ export const TOOL_CLUSTER_COLLAPSE_LINGER_MS = 600;
@@ -0,0 +1,50 @@
1
+ export const PERMISSION_DECISION_VALUES = [
2
+ "allow_once",
3
+ "allow_session",
4
+ "deny",
5
+ ] as const;
6
+
7
+ export type PermissionDecision = (typeof PERMISSION_DECISION_VALUES)[number];
8
+
9
+ export const PERMISSION_DECISIONS = {
10
+ ALLOW_ONCE: "allow_once",
11
+ ALLOW_SESSION: "allow_session",
12
+ DENY: "deny",
13
+ } as const;
14
+
15
+ export const PERMISSION_BUTTONS: ReadonlyArray<{
16
+ label: string;
17
+ decision: PermissionDecision;
18
+ variant: "primary" | "secondary" | "danger";
19
+ }> = [
20
+ {
21
+ label: "Approve once",
22
+ decision: PERMISSION_DECISIONS.ALLOW_ONCE,
23
+ variant: "primary",
24
+ },
25
+ {
26
+ label: "Approve session",
27
+ decision: PERMISSION_DECISIONS.ALLOW_SESSION,
28
+ variant: "secondary",
29
+ },
30
+ {
31
+ label: "Deny",
32
+ decision: PERMISSION_DECISIONS.DENY,
33
+ variant: "danger",
34
+ },
35
+ ] as const;
36
+
37
+ export const PERMISSION_LABELS = {
38
+ TOOL_NAME: "Tool",
39
+ SUMMARY: "Action",
40
+ ARGS_TOGGLE_SHOW: "Show details",
41
+ ARGS_TOGGLE_HIDE: "Hide details",
42
+ ARGS_HEADING: "Arguments",
43
+ } as const;
44
+
45
+ export const PERMISSION_INLINE_LABELS = {
46
+ HEADING_ONE: "Permission required",
47
+ HEADING_MANY: "Permissions required",
48
+ APPLY_ALL: "Allow all",
49
+ DENY_ALL: "Deny all",
50
+ } as const;
@@ -0,0 +1,48 @@
1
+ export const CLIENT_MESSAGE_TYPES = {
2
+ HELLO: "hello",
3
+ USER_MESSAGE: "user_message",
4
+ PERMISSION_RESPONSE: "permission_response",
5
+ QUESTION_RESPONSE: "question_response",
6
+ LIST_CONVERSATIONS: "list_conversations",
7
+ DELETE_CONVERSATION: "delete_conversation",
8
+ SET_SETTINGS: "set_settings",
9
+ INTERRUPT_TURN: "interrupt_turn",
10
+ QUEUE_ENQUEUE: "queue_enqueue",
11
+ QUEUE_CANCEL: "queue_cancel",
12
+ } as const;
13
+
14
+ /**
15
+ * Route of the dms-ai Settings page, reached from the chat's cogwheel. Module
16
+ * pages live under `/modules/<moduleId>/<slug>`.
17
+ */
18
+ export const SETTINGS_PAGE_PATH = "/modules/ai/settings";
19
+
20
+ export const SERVER_EVENT_TYPES = {
21
+ ASSISTANT_MESSAGE_CHUNK: "assistant_message_chunk",
22
+ TOOL_CALL_START: "tool_call_start",
23
+ TOOL_CALL_END: "tool_call_end",
24
+ RUN_DONE: "run_done",
25
+ RUN_ERROR: "run_error",
26
+ RUN_RESUMED: "run_resumed",
27
+ CONVERSATION_SNAPSHOT: "conversation_snapshot",
28
+ CONVERSATION_LIST: "conversation_list",
29
+ SETTINGS_UPDATE: "settings_update",
30
+ PERMISSION_REQUEST: "permission_request",
31
+ ASK_QUESTION: "ask_question",
32
+ QUEUE_STATE: "queue_state",
33
+ USER_MESSAGE_ECHO: "user_message_echo",
34
+ RUN_PROGRESS: "run_progress",
35
+ } as const;
36
+
37
+ /** The role the chat says hello with. */
38
+ export const CHAT_ROLE = "chat";
39
+
40
+ export const CONNECTION_STATUSES = {
41
+ CONNECTING: "connecting",
42
+ CONNECTED: "connected",
43
+ DISCONNECTED: "disconnected",
44
+ RECONNECTING: "reconnecting",
45
+ } as const;
46
+
47
+ export type ConnectionStatus =
48
+ (typeof CONNECTION_STATUSES)[keyof typeof CONNECTION_STATUSES];
@@ -0,0 +1,50 @@
1
+ import type { ActivityKind } from "../types/conversation";
2
+
3
+ /** What a running turn can be busy with, as the sidecar reports it. */
4
+ export const ACTIVITY_KINDS = {
5
+ THINKING: "thinking",
6
+ RESPONDING: "responding",
7
+ WRITING: "writing",
8
+ TOOL: "tool",
9
+ COMPACTING: "compacting",
10
+ RETRYING: "retrying",
11
+ } as const;
12
+
13
+ /** What a running turn shows while the agent works, by reported activity. */
14
+ export const ACTIVITY_LABELS: Record<ActivityKind, string> = {
15
+ [ACTIVITY_KINDS.THINKING]: "Thinking",
16
+ [ACTIVITY_KINDS.RESPONDING]: "Writing the answer",
17
+ [ACTIVITY_KINDS.WRITING]: "Preparing",
18
+ [ACTIVITY_KINDS.TOOL]: "Running",
19
+ [ACTIVITY_KINDS.COMPACTING]: "Compacting the conversation",
20
+ [ACTIVITY_KINDS.RETRYING]: "The model is busy, retrying",
21
+ };
22
+
23
+ /** Activities whose detail names a tool, shown with the tool's own label. */
24
+ export const TOOL_DETAIL_ACTIVITIES: readonly ActivityKind[] = [
25
+ ACTIVITY_KINDS.WRITING,
26
+ ACTIVITY_KINDS.TOOL,
27
+ ];
28
+
29
+ /** Activities during which a silent agent is expected, so never called quiet. */
30
+ export const SILENT_ACTIVITIES: readonly ActivityKind[] = [ACTIVITY_KINDS.TOOL];
31
+
32
+ export const DEFAULT_ACTIVITY_LABEL = "Thinking";
33
+ export const ACTIVITY_ELLIPSIS = "…";
34
+
35
+ /**
36
+ * How long a running turn may go without any word from the sidecar, heartbeats
37
+ * included, before the chat says it lost track of it. The sidecar beats every
38
+ * 5 seconds, so this is several missed beats, never a slow step.
39
+ */
40
+ export const STALL_AFTER_MS = 20_000;
41
+
42
+ /** From how long without agent activity the chat says so next to the label. */
43
+ export const QUIET_NOTICE_AFTER_MS = 60_000;
44
+
45
+ export const RUN_CLOCK_TICK_MS = 1_000;
46
+
47
+ export const STALLED_LABEL = "No news from the assistant for";
48
+ export const QUIET_LABEL = "no activity for";
49
+ export const RECONNECT_LABEL = "Reconnect";
50
+ export const STOP_LABEL = "Stop";
@@ -0,0 +1,60 @@
1
+ import type { AppSettings, ChatMode, ProviderName } from "../types/settings";
2
+
3
+ export const DEFAULT_SETTINGS: AppSettings = {
4
+ provider: "claude",
5
+ mode: "normal",
6
+ thinking: "medium",
7
+ generationMode: "safe",
8
+ allowLocalSkills: false,
9
+ builderAvailable: false,
10
+ providers: {
11
+ claude: { available: true },
12
+ codex: { available: false },
13
+ },
14
+ };
15
+
16
+ export const OPEN_SETTINGS_LABEL = "Settings";
17
+
18
+ export const MODE_SECTION_LABEL = "Mode";
19
+ export const PROVIDER_SECTION_LABEL = "Agent";
20
+
21
+ export const PROVIDER_OPTIONS: { value: ProviderName; label: string }[] = [
22
+ { value: "claude", label: "Claude" },
23
+ { value: "codex", label: "Codex" },
24
+ ];
25
+
26
+ /**
27
+ * Shown in place of the hint when the sidecar reports the provider as
28
+ * unavailable, so the reason travels with the greyed-out option.
29
+ */
30
+ export const PROVIDER_UNAVAILABLE_PREFIX = "Unavailable: ";
31
+
32
+ /**
33
+ * Sessions belong to the backend that opened them, so a switch tears them down.
34
+ * The transcript survives; what the agent remembers of it does not — and that
35
+ * is not something to discover after the fact.
36
+ */
37
+ export const PROVIDER_SWITCH_WARNING =
38
+ "Switching agent starts a new session: open conversations lose what the agent remembers of them. Transcripts are kept.";
39
+ export const PROVIDER_SWITCH_CONFIRM = `${PROVIDER_SWITCH_WARNING}\n\nSwitch now?`;
40
+ export const PROVIDER_BUSY_HINT =
41
+ "Stop the current turn before switching agent.";
42
+
43
+ export const MODE_OPTIONS: { value: ChatMode; label: string }[] = [
44
+ { value: "normal", label: "Normal" },
45
+ { value: "acceptEdits", label: "Accept edits" },
46
+ { value: "plan", label: "Plan" },
47
+ { value: "auto", label: "Auto" },
48
+ ];
49
+
50
+ export const MODE_HINTS: Record<ChatMode, string> = {
51
+ normal: "Ask before each tool action",
52
+ acceptEdits: "Auto-accept file edits, ask for the rest",
53
+ plan: "Plan only — propose without making changes",
54
+ auto: "Auto-approve every tool action",
55
+ };
56
+
57
+ // Safe mode refuses raw edits whatever the mode, and Auto stops short of
58
+ // approving everything there, so the mode hint says so while it is active.
59
+ export const SAFE_MODE_MODE_NOTE =
60
+ "Safe mode: raw edits and shell commands stay blocked, and Auto still asks before other actions.";